Forms
Numa uses @tanstack/react-form with GForm and field components (GInput, GPassword, etc.) from @numa/ui.
GForm
GForm is a declarative form builder. Pass an inputs array and actions array; GForm renders the form, wires TanStack Form, and handles validation.
How it works
- GForm calls
useAppForminternally withdefaultValuesderived from yourinputs - For each input config, it renders a
form.AppFieldand mapstypeto the correct field component - Validation runs via TanStack Form validators —
required: trueadds a simple required check - On submit,
onSubmit(values)receivesRecord<string, string>with all field values
Basic example
<GForm
name="login"
direction="vertical"
gap={4}
inputs={[
{
name: "email",
type: "email",
label: "Email",
placeholder: "Email",
bg: "primary",
required: true,
},
{
name: "password",
type: "password",
label: "Password",
placeholder: "••••••••",
bg: "primary",
required: true,
},
]}
onSubmit={(values) => mutate({ body: values })}
actions={[{ label: "Login", variant: "default", submit: true, loading: isPending }]}
/>Props reference
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | "g-form" | Form name and id |
inputs | GFormInputConfig[] | [] | Input configs (ignored when children is used) |
children | ReactNode | — | Custom form content (takes priority) |
direction | "vertical" | "horizontal" | "vertical" | Flex direction for inputs |
gap | number | 6 | Spacing between inputs |
cols | 1 | 2 | 3 | 4 | — | Grid columns (actions span full width) |
onSubmit | (values) => void | — | Called with all field values on submit |
actions | GFormActionConfig[] | [] | Action buttons |
defaultValues | Record<string, string> | — | Required when using children |
className | string | — | Extra classes on the form |
Input config (GFormInputConfig)
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Field name (used as form key) |
type | GFormInputType | Yes | "text" | "email" | "password" | "phone" | "textarea" | "select" |
label | string | Label above the input | |
placeholder | string | Placeholder text | |
options | GSelectOption[] | For type: "select" only | |
required | boolean | Adds “Required” validator | |
validators | { onChange?, onBlur?, onSubmit? } | Custom TanStack Form validators | |
initialValue | string | Default value |
Action config (GFormActionConfig)
| Field | Type | Description |
|---|---|---|
submit | boolean | Renders as type="submit" |
label | ReactNode | Button text |
Plus all TButtonProps: variant, size, loading, disabled, className, onClick, etc.
Layout modes
- Flex:
direction="vertical"(stacked) or"horizontal"(row, wraps) - Grid: When
colsis set (1–4), inputs use grid layout, actions span all columns
Multi-column example
<GForm
name="contact"
cols={2}
gap={4}
inputs={[
{ name: "firstName", type: "text", label: "First name", required: true },
{ name: "lastName", type: "text", label: "Last name", required: true },
{ name: "email", type: "email", label: "Email", required: true },
{ name: "phone", type: "phone", label: "Phone" },
]}
onSubmit={(values) => sendContact(values)}
actions={[{ label: "Send", submit: true }]}
/>Custom validators
{
name: "password",
type: "password",
label: "Password",
required: true,
validators: {
onChange: ({ value }) =>
value.length > 0 && value.length < 8 ? "At least 8 characters" : undefined,
},
}Manual form usage
For forms that need custom layout beyond GForm’s config, use the TanStack Form hooks directly:
const form = useAppForm({
defaultValues: { email: "", password: "" },
onSubmit: ({ value }) => { /* submit */ },
});
<form onSubmit={(e) => { e.preventDefault(); form.handleSubmit(); }}>
<form.AppField
name="email"
validators={{ onChange: ({ value }) => (!value ? "Required" : undefined) }}
>
{(field) => <field.GEmail label="Email" placeholder="Email" />}
</form.AppField>
<button type="submit">Submit</button>
</form>Field components
| Component | Use Case |
|---|---|
field.GInput | Text input |
field.GEmail | Email with validation |
field.GPassword | Password with visibility toggle |
field.GPhoneNumber | Phone number |
field.GTextarea | Multi-line text |
field.GSelect | Select (requires options) |
Last updated on