Schema input and output
A schema can accept one shape and produce another. NotForm keeps these two shapes distinct everywhere:
| Input | Output | |
|---|---|---|
| What it is | What users edit, before parsing | What the schema produces after validating |
| Where it appears | form.values, initialValues, field.model, form.setValue() | The argument of onSubmit, and { value } from form.validate() |
| Type helper | SchemaInput<typeof schema> | SchemaOutput<typeof schema> |
A transform changes the output
import { z } from 'zod'
const orderSchema = z.object({
quantity: z.string().transform(Number),
})
const form = useForm({
initialValues: { quantity: '2' },
onSubmit(data) {
data.quantity.toFixed(0) // data.quantity is a number
},
schema: orderSchema,
})
form.values.quantity // a string
The input control holds text, so the form value stays a string. Your handler receives the converted number. The same distinction applies to every Standard Schema validator.
Common ways input and output differ
| Schema feature | Effect on the shapes |
|---|---|
| Transform or coercion | The output type differs from the input type. |
| Default value | The key is optional in the input and required in the output. |
| Trimming or normalizing | form.values keeps what the user typed. The output is normalized. |
| Pipes into another schema | The output is the final stage. The input is the first. |
Because form.values always holds the input, the text a user typed is never silently rewritten. Normalization happens only in the data you receive on submit.
Initial values
initialValues use the input shape, and may be partial. They are the values controls display, so use the representation your controls and schema expect, not the parsed output.
useForm({
initialValues: { quantity: '2' }, // input shape, not a number
schema,
})
Though not mandatory, provide a value for every field you bind. A key missing from initialValues reaches the schema as undefined, and validators then report their generic type message instead of your custom one. Empty strings, false, and [] are the usual starting values for text, checkboxes, and lists.
Typed paths and values
Paths and values for form.field(), form.arrayField(), and form.setValue() come from the input shape. A path that does not exist is a TypeScript error, and setValue checks the value type.
form.setValue('quantity', '3') // ok
form.setValue('quantity', 3) // type error: input is a string
form.field('quantty') // type error: no such path
Naming the types
Import the helpers when you need to name a shape, for example in a function signature.
import type { SchemaInput, SchemaOutput } from 'notform'
type OrderInput = SchemaInput<typeof orderSchema>
type OrderOutput = SchemaOutput<typeof orderSchema>
async function saveOrder(order: OrderOutput) {
// ...
}
See TypeScript for more patterns.
Next steps
- Validation explains when the schema runs.
- Submission and reset explains how output reaches
onSubmit.