---
title: "Schema input and output"
description: "Understand how NotForm types the values users edit separately from the validated data your submit handler receives."
canonical_url: "https://notformdocs.vercel.app/docs/guides/schema-input-output"
---

> Index of every page on this site: [llms.txt](https://notformdocs.vercel.app/llms.txt)

A schema can accept one shape and produce another. NotForm keeps these two shapes distinct everywhere:

<table>
<thead>
  <tr>
    <th>
      
    </th>
    
    <th>
      Input
    </th>
    
    <th>
      Output
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      What it is
    </td>
    
    <td>
      What users edit, before parsing
    </td>
    
    <td>
      What the schema produces after validating
    </td>
  </tr>
  
  <tr>
    <td>
      Where it appears
    </td>
    
    <td>
      <code>
        form.values
      </code>
      
      , <code>
        initialValues
      </code>
      
      , <code>
        field.model
      </code>
      
      , <code>
        form.setValue()
      </code>
    </td>
    
    <td>
      The argument of <code>
        onSubmit
      </code>
      
      , and <code>
        { value }
      </code>
      
       from <code>
        form.validate()
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Type helper
    </td>
    
    <td>
      <code>
        SchemaInput<typeof schema>
      </code>
    </td>
    
    <td>
      <code>
        SchemaOutput<typeof schema>
      </code>
    </td>
  </tr>
</tbody>
</table>

## A transform changes the output

```ts
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

<table>
<thead>
  <tr>
    <th>
      Schema feature
    </th>
    
    <th>
      Effect on the shapes
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Transform or coercion
    </td>
    
    <td>
      The output type differs from the input type.
    </td>
  </tr>
  
  <tr>
    <td>
      Default value
    </td>
    
    <td>
      The key is optional in the input and required in the output.
    </td>
  </tr>
  
  <tr>
    <td>
      Trimming or normalizing
    </td>
    
    <td>
      <code>
        form.values
      </code>
      
       keeps what the user typed. The output is normalized.
    </td>
  </tr>
  
  <tr>
    <td>
      Pipes into another schema
    </td>
    
    <td>
      The output is the final stage. The input is the first.
    </td>
  </tr>
</tbody>
</table>

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.

```ts
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.

```ts
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.

```ts
import type { SchemaInput, SchemaOutput } from 'notform'

type OrderInput = SchemaInput<typeof orderSchema>
type OrderOutput = SchemaOutput<typeof orderSchema>

async function saveOrder(order: OrderOutput) {
  // ...
}
```

See [TypeScript](https://notformdocs.vercel.app/docs/guides/typescript) for more patterns.

## Next steps

- [Validation](https://notformdocs.vercel.app/docs/guides/validation) explains when the schema runs.
- [Submission and reset](https://notformdocs.vercel.app/docs/guides/submission-and-reset) explains how output reaches `onSubmit`.

## Sitemap

See the full [sitemap](https://notformdocs.vercel.app/sitemap.md) for all pages.
