Dynamic Fields
Add and remove fields at runtime without losing the rest of the form's state.
gform-react registers each field by its formKey. That means you can add or remove GInputs at
runtime - render them from a state array - and the rest of the form keeps its values. When a field
unmounts, gform unregisters it; when it mounts, it registers fresh.
Pattern
Keep a list of stable ids in component state and map each to a GInput with a unique formKey.
import { useState } from "react";
import { GForm, GInput, GValidator, type GValidators } from "gform-react";
const validators: GValidators = {
"*": new GValidator().withRequiredMessage("Required"),
};
let nextId = 1;
export default function DynamicOptions() {
const [ids, setIds] = useState<number[]>([0]);
return (
<GForm
validators={validators}
onSubmit={(state, e) => {
e.preventDefault();
console.log(state.toRawData()); // { "option-0": …, "option-1": … }
}}
>
{(state) => (
<>
{ids.map((id) => (
<div key={id}>
<GInput
formKey={`option-${id}`}
required
element={(input, props) => (
<span>
<input {...props} placeholder={`Option ${id}`} />
{input.error && <small>{input.errorText}</small>}
</span>
)}
/>
<button type="button" onClick={() => setIds((xs) => xs.filter((x) => x !== id))}>
Remove
</button>
</div>
))}
<button type="button" onClick={() => setIds((xs) => [...xs, nextId++])}>
Add option
</button>
<button disabled={state.isInvalid}>Submit</button>
</>
)}
</GForm>
);
}Use a stable key
Give each row a stable id (not the array index) for both React's key and the formKey. That keeps
values attached to the right field when you remove an item from the middle of the list.
Try it live
Loading playground…
Add a few options, fill them in, then remove one from the middle - the others keep their values.