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
| Prop | Type | Description |
|---|---|---|
formKey | string | Required. Field key; also the control's name. |
element | (input: GInputState, props: GElementProps) => ReactNode | Render prop. Omit it to render a native <input>. |
type | HTMLInputType | Drives the value type (below). Default "text". |
value | depends on type | Initial value - seeds form state and is validated on mount. |
defaultValue | string | number | Uncontrolled initial value. |
required | boolean | Native required constraint. |
validatorKey | string | Look up another key's validator instead of formKey. |
validatorDeps | string[] | Field keys whose value changes also re-run this field's validator. |
minLength / maxLength | number | Length constraints (text types). |
pattern | string | Regex constraint (text types). |
min / max / step | number | Range constraints (number/date types). |
multiple | boolean | type="file" only - stores File[]. |
debounce | number | Debounce (ms) for async validation and fetch. Default 300. |
fetch | (input, fields) => void | Partial | Promise<…> | Populate the field asynchronously (below). |
fetchDeps | string[] | 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 liveGInputState(value,error,errorText,formKey, …).props- the props to spread onto your control: gform's wiring plus every pass-through prop you set onGInput.
<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.
type | value type | Type-specific props |
|---|---|---|
text, password, email, tel, url, search | string | minLength, maxLength, pattern |
checkbox | boolean | checked, defaultChecked |
number, range | number | min, max, step |
date, time, week, month, datetime-local | string | min, max, step |
color | string | - |
file | File | File[] | null | multiple |
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:
nameis always theformKey- this is howFormDataand Server Actions find the field.value/checkedare controlled by form state (type="file"is the exception - file inputs stay uncontrolled).ref,aria-invalid, andaria-requiredare injected.titlefalls back to the currenterrorText, so hovering an invalid control shows its error message - pass your owntitleto 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 sameformKeyare 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>: