# Get started

Build and submit a working form in three steps. This example uses the native
preset, Zod, and the default validation timing.

:::tip
Does your application already use React Hook Form (RHF)? Keep your schemas and
migrate one form at a time. Read [Migrate from React Hook
Form](/form-please/migrate-from-react-hook-form).
:::

## Install

Install Form, Please, RHF, and a Standard Schema library. This
example uses Zod.

:::code-group
```sh [npm]
npm install form-please react-hook-form zod
```

```sh [pnpm]
pnpm add form-please react-hook-form zod
```

```sh [yarn]
yarn add form-please react-hook-form zod
```

```sh [bun]
bun add form-please react-hook-form zod
```
:::

React 18 and React 19 are supported peer dependencies.

:::steps
## Define the data contract

Create a Standard Schema for the schema input. The schema output can contain
more values than the schema input.

```tsx
// [!include ~/snippets/profile-form.tsx:schema]
```

The schema input is `{ name: string; email: string }`. Generated fields always
edit the schema input.

## Define the generated fields

Connect each schema path to a control from the native preset:

```tsx
// [!include ~/snippets/profile-form.tsx:definition]
```

`path` must exist in the schema input. The selected control must accept the
value at that path. `required: true` changes the UI state only. See
[`required` is UI-only](/form-please/how-it-works#required-is-ui-only).

## Bind and render the form

Create the form binding inside a React component. Supply one complete schema
input as `defaultValues`.

```tsx
// [!include ~/snippets/profile-form.tsx:component]
```

`kit.AutoForm` renders the native `<form>`, an error summary, and the generated
fields. Add `kit.Submit` as a child to render the submit button.

After a valid submit, `value` contains the schema output.
The `onSubmit` callback keeps `value` in React state, and the `<pre>` element
shows it. Use the `input` callback property when you also need the schema
input.

All JavaScript package entries are React client modules. In a Next.js App
Router project, put `"use client"` at the start of the component file.
:::

## See the result

This is the form from step 3. Enter a name and an email, and then click
**Save profile**. The output shows the schema output with `slug`. Submit empty
fields to see the validation errors.

The live Get started form runs only in a browser. It renders the ProfileForm component from step 3. After a valid submit, it shows the schema output, including slug.

Source: [docs-site/src/snippets/profile-form.tsx](https://github.com/r13v/form-please/blob/main/docs-site/src/snippets/profile-form.tsx)

<details>
  <summary>Copy the complete first form</summary>

  ```tsx twoslash
  // [!include ~/snippets/profile-form.tsx]
  ```
</details>

## Put fields in columns

Import the optional layout stylesheet once in your application entry point:

```ts
import "form-please/layout.css"
```

Then put the fields in a section and set `columns`:

```tsx
const profileDefinition = kit.defineForm(profileSchema, (ui) => [
	ui.section("profile", {
		title: "Profile",
		columns: 2,
		children: [
			ui.field("name", { control: "text", label: "Name", required: true }),
			ui.field("email", {
				control: "text",
				label: "Email",
				props: { type: "email", autoComplete: "email" },
				required: true,
			}),
		],
	}),
])
```

The section puts `name` and `email` in one row. When the section is narrower
than `40rem`, it shows one column. Set `span: "full"` on a field to give it the
full row. See [Configure columns and spans](/form-please/styling#configure-columns-and-spans).

The stylesheet adds only grid and spacing rules. The native preset has no
visual theme. Replace it with your own controls and slots, or use the Material
UI preset.

## Use React Hook Form next to the fields

The form binding keeps the complete RHF API in `form.api`. In `ProfileForm`,
read a value with `useWatch` after the `kit.useForm` call:

```tsx
import { useWatch } from "react-hook-form"

const name = useWatch({ control: form.api.control, name: "name" })
```

Render `name` as a child of `kit.AutoForm`. `register`, `Controller`, and
`useFormState` also work there, because `kit.AutoForm` supplies RHF
`FormProvider`. Read [You keep the React Hook Form
API](/form-please/how-it-works#you-keep-the-react-hook-form-api).

## Next steps

* [Edit a definition in the playground](/form-please/playground). See the form, the output,
  and the TypeScript errors change. Its scenarios show a transform, conditional
  fields, an array, and a mistyped path.
* [Keep a total in sync with `beforeUpdate`](/form-please/middleware). Write the rule once
  in the definition. It can also cancel a change before it commits.
* [Learn the rules that each form follows](/form-please/how-it-works).
