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" />;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. The
element render prop section of Core Concepts walks
through it in depth, including
which props pass through.
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. |
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.
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. 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:
<GInput
formKey="status"
value="on-track"
element={(input, props) => (
<select {...props}>
<option value="on-track">On track</option>
<option value="ahead">Ahead</option>
</select>
)}
/>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:
<GInput
formKey="city"
fetchDeps={["zip"]}
fetch={async (input, fields) => {
const zip = fields.zip?.value;
if (!zip) return;
return { value: await lookupCity(zip) };
}}
element={(input, props) => <input {...props} />}
/>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.
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>: