gform-react
Back to Components

GInput

The field component - default rendering, the element render prop, per-type values, fetch, and field lifecycle.

GInput declares one field of the form. Its formKey identifies the field everywhere: it names the underlying control (name={formKey}), keys the field's slice of form state (state.<formKey>), and selects its validator. GInput is memoized and subscribes only to its own slice, so typing in one field re-renders that field alone - see Core Concepts for the full model.

import { GInput } from "gform-react";
 
<GInput formKey="email" type="email" required placeholder="you@example.com" />;

Props

PropTypeDescription
formKeystringRequired. Field key; also the control's name.
element(input: GInputState, props: GElementProps) => ReactNodeRender prop. Omit it to render a native <input>.
typeHTMLInputTypeDrives the value type (below). Default "text".
valuedepends on typeInitial value - seeds form state and is validated on mount.
defaultValuestring | numberUncontrolled initial value.
requiredbooleanNative required constraint.
validatorKeystringLook up another key's validator instead of formKey.
validatorDepsstring[]Field keys whose value changes also re-run this field's validator.
minLength / maxLengthnumberLength constraints (text types).
patternstringRegex constraint (text types).
min / max / stepnumberRange constraints (number/date types).
multiplebooleantype="file" only - stores File[].
debouncenumberDebounce (ms) for async validation and fetch. Default 300.
fetch(input, fields) => void | Partial | Promise<>Populate the field asynchronously (below).
fetchDepsstring[]Field keys whose value changes re-trigger fetch.

Everything else you put on GInput - placeholder, autoComplete, className, id, event handlers, … - passes through to the rendered control (directly onto the default <input>, or via the props object handed to element). Keep a field's whole configuration on GInput.

Rendering

The default: a native input

When you don't pass element, GInput renders a native <input>, fully wired - value, handlers, name, and aria attributes are all applied for you. For plain text-ish fields that's all you need:

<GInput formKey="name" required placeholder="Name" />

The element render prop

To render anything else - custom markup, a <select>, a <textarea>, or a component from MUI, PrimeReact, etc. - pass an element function. It receives:

  • input - the field's live GInputState (value, error, errorText, formKey, …).
  • props - the props to spread onto your control: gform's wiring plus every pass-through prop you set on GInput.
<GInput
  formKey="email"
  type="email"
  required
  element={(input, props) => (
    <label>
      <span>Email</span>
      <input {...props} />
      {input.error && <small>{input.errorText}</small>}
    </label>
  )}
/>

props is typed as GElementProps - the intersection of <input>, <select>, and <textarea> attributes - so it spreads onto any of them without a cast. Everything else you set on GInput is forwarded into props too, so a field's whole configuration stays in one place - see Props below.

type selects the value type

GInputProps is a discriminated union on type: it constrains value, the type-specific constraint props, and the input/props your element receives.

typevalue typeType-specific props
text, password, email, tel, url, searchstringminLength, maxLength, pattern
checkboxbooleanchecked, defaultChecked
number, rangenumbermin, max, step
date, time, week, month, datetime-localstringmin, max, step
colorstring-
fileFile | File[] | nullmultiple

So with type="number", input.value is a number; with type="checkbox" the checked state lives in input.value as a boolean; and so on.

What gform wires for you

A few props on the rendered control are managed by gform and can't be overridden:

  • name is always the formKey - this is how FormData and Server Actions find the field.
  • value / checked are controlled by form state (type="file" is the exception - file inputs stay uncontrolled).
  • ref, aria-invalid, and aria-required are injected.
  • title falls back to the current errorText, so hovering an invalid control shows its error message - pass your own title to override.

Handlers are chained, not replaced: an onChange, onBlur, or onInvalid you pass to GInput runs after gform's own handler. gform also calls preventDefault() on invalid events to suppress the browser's default validation tooltip in favor of input.errorText.

Initial values

value seeds the field's state, typed by type. A field that mounts with a value is validated immediately - constraint errors render on the first paint and an invalid initial value blocks submission, with custom/async validators running right after mount.

See Initial Values for per-type defaults, the reset baseline, and GForm's onInit for seeding the whole form at once.

Loading values asynchronously: fetch and fetchDeps

fetch(input, fields) runs once after the field mounts, and again - debounced by debounce - whenever the value of any key listed in fetchDeps changes. Return a partial field state (or a Promise of one) and gform dispatches it.

See Fetching Data for the worked pattern (e.g. a ZIP → city lookup). To seed the whole form in one go, prefer GForm's onInit.

Reusing validators: validatorKey

A field resolves its validator as validators[validatorKey || formKey], falling back to the "*" entry. validatorKey is handy when formKeys are dynamic but share one rule set:

const validators: GValidators = {
  option: new GValidator().withRequiredMessage("Required"),
};
 
{ids.map((id) => (
  <GInput key={id} formKey={`option-${id}`} validatorKey="option" required />
))}

See Constraint Validation for validation patterns and GValidator for the full class reference.

Cross-field re-validation: validatorDeps

A field only re-runs its own validator when its own value changes. validatorDeps lists other field keys whose value change should also re-trigger this field's validator - useful for a confirm-password field, or any pair/group routed by a cross-field rule (.refine()/.superRefine() with schema validation, or a manual withCustomValidation):

<GInput formKey="password" required minLength={8} />
<GInput formKey="confirm" required validatorDeps={["password"]} />

Here, editing password re-validates confirm too, so a stale "Passwords must match" clears as soon as the user fixes the other field - without confirm itself being touched.

Lifecycle

  • A field registers on its first render. Two mounted GInputs with the same formKey are not allowed - gform warns in development and ignores the duplicate.
  • A field unregisters on unmount: its value leaves form state and overall validity is recalculated. Remounting registers it fresh. This is what makes dynamic fields work without bookkeeping.

File inputs

type="file" stores the real File (or File[] with multiple) in form state - not the C:\fakepath\… string - and toFormData() includes the files. File inputs stay uncontrolled at the DOM level; gform syncs the selection for you. See the File Inputs guide.

React Native

On React Native use RNGInput from gform-react/native: same formKey model, but element receives TextInputProps and the default render is a TextInput. See React Native.

Try it live

Three rendering styles side by side: the default native input, a custom element with inline errors, and a <select>:

Loading playground…