gform-react
Back to The Basics

Initial Values

Seed a form with onInit (sync or async) or the per-field value prop, and understand how initial values define the reset baseline.

Every field starts empty by default. There are two ways to give a form starting values - seed the whole form at once with onInit, or set a starting value on each GInput. Whichever you use, those initial values also become the form's reset baseline.

SourceUseWhen
The whole formonInit on GFormEdit forms, or any seed computed in one place - sync or async
One fieldThe value prop on GInputDefaults and constants you know at render time
One field, loaded asyncThe fetch prop on GInputA field whose value depends on an API or other fields

Seed the whole form with onInit

onInit(state) runs once, right after the form mounts. Return a partial form and gform merges it into the fields. It can be synchronous - return the partial directly when you already have the data:

<GForm<ProfileForm>
  onInit={() => ({
    name: { value: "Ada Lovelace" },
    city: { value: "London" },
  })}
>

…or asynchronous - return a Promise to load a record after mount. This is the standard "load a record to edit" pattern:

<GForm<ProfileForm>
  onInit={async () => {
    const profile = await fetchProfile();
    return {
      name: { value: profile.name },
      city: { value: profile.city },
    };
  }}
>

For a single field that loads on its own (or reloads when other fields change), use GInput's fetch prop instead. See GForm → onInit for the full contract.

Set a starting value per field with value

For a value you know at render time, set the value prop on GInput. It's the initial value, not a controlled value, so the user (and dispatchChanges) can change it freely afterward:

<GInput formKey="name" value="Ada Lovelace" />

The type of value matches the field's type, and so does the value you read back from state / toRawData():

<GInput formKey="name"   value="Ada" />                    {/* string  */}
<GInput formKey="age"    type="number"   value={30} />     {/* number  */}
<GInput formKey="active" type="checkbox" value={true} />   {/* boolean */}

When you don't pass a value, the field falls back to a sensible per-type default - "" for text, 0 for number, false for checkbox, null for file. (For convenience the native defaultValue and checked attributes are accepted too, but value is the canonical way to seed.)

This works through the element render prop exactly the same way - the seeded value arrives in props.value (or props.checked), so you just spread it:

<GInput
  formKey="status"
  value="on-track"
  element={(input, props) => (
    <select {...props}>
      <option value="on-track">On track</option>
      <option value="ahead">Ahead</option>
    </select>
  )}
/>

Per-field values are validated on first paint

A field that mounts with a value has its native constraints checked immediately, so an invalid starting value shows its error on the very first render - no interaction needed - and the form's native validity is synced so it can't be submitted while invalid:

const validators = {
  "*": new GValidator().withRequiredMessage("Required"),
  username: new GValidator(/* … */).withMinLengthMessage("At least 4 characters"),
};
 
// Renders already showing "At least 4 characters":
<GInput formKey="username" value="ab" minLength={4} />

A field that mounts empty stays pristine - error/touched are false until the user interacts with it (or you submit). So seeding an empty optional field costs nothing, and seeding a value you know is invalid surfaces the problem up front.

Custom validators wait for the mount effect

Only native constraints (required, minLength, pattern, type="email", …) are baked at registration for the first paint. Custom and async validators (withCustomValidation) run in the field's mount effect with the full field set, so a cross-field initial error appears a tick later.

Initial values define the reset baseline

A form reset restores each field to its initial value - the value prop, or whatever onInit seeded (gform re-snapshots the baseline after onInit runs, so an edit form resets to the loaded record, not to empty). In other words, "initial" and "reset target" are the same thing.

Try it live

A mix of seeded fields - text, number, a checked checkbox, a select, and a username that starts invalid so you can see its error on first paint. The form is valid as soon as username is long enough:

Loading playground…

The Username field shows "At least 4 characters" immediately because it mounted with an invalid value; fix it and the Save button enables. Notice toRawData() already reflects every seeded value before you touch anything - the number stays a number and the checkbox a boolean.

See GForm for onInit, Fetching Data for the fetch prop, and Resetting a Form for how the initial values become the reset baseline.