# Forms (/docs/forms)



A Luke UI form uses the native HTML `<form>` element. Compose it from Luke UI fields and buttons.

apps/docs/src/examples/forms/build-a-form.tsx

```tsx
import { Box } from '@luke-ui/react/box';
import { Button } from '@luke-ui/react/button';
import { Text } from '@luke-ui/react/text';
import { TextField } from '@luke-ui/react/text-field';
import type { FormEvent } from 'react';
import { useState } from 'react';

export default () => {
	const [submittedEmail, setSubmittedEmail] = useState('');

	function handleSubmit(event: FormEvent<HTMLFormElement>) {
		event.preventDefault();
		const data = new FormData(event.currentTarget);
		const email = data.get('email');
		setSubmittedEmail(typeof email === 'string' ? email : '');
	}

	return (
		<Box display="flex" flexDirection="column" gap="sp16" maxInlineSize="22rem">
			<form onReset={() => setSubmittedEmail('')} onSubmit={handleSubmit}>
				<Box display="flex" flexDirection="column" gap="sp16">
					<TextField
						description="We will send the receipt to this address."
						isRequired
						label="Email address"
						name="email"
						type="email"
					/>
					<Box display="flex" gap="sp8">
						<Button type="submit">Submit</Button>
						<Button appearance="subtle" type="reset">
							Reset
						</Button>
					</Box>
				</Box>
			</form>
			{submittedEmail ? <Text elementType="p">Submitted: {submittedEmail}</Text> : null}
		</Box>
	);
};
```

## Visible labels [#visible-labels]

Pass a visible `label` to `TextField` and `ComboboxField`. Luke UI associates the label with its
control.

Pass the visible checkbox label through `Checkbox` children. Use `aria-label` only when surrounding
content already names the control.

A placeholder disappears after someone enters a value. Do not use a placeholder as the only label.

## Descriptions [#descriptions]

Pass `description` when someone needs information before they complete a field. Luke UI associates
the description with its control.

Keep each description to one sentence. Luke UI keeps it visible and associated with the control when
an error message appears.

## Required fields [#required-fields]

Set `isRequired` when someone must complete a field. The browser blocks submission until the field
has a value.

Set `necessityIndicator="label"` to append “(required)” to a field label. The default indicator is
an icon.

Read [Required fields](/components/forms/text-field#required-fields) for both indicators.

## Controlled and uncontrolled fields [#controlled-and-uncontrolled-fields]

Prefer an uncontrolled field when the application needs its value only during submission. Pass a
starting value through `defaultValue` or `defaultSelected`.

Use a controlled field when the application needs its value during each render. Pass `value` and
`onChange`, then store the value in state.

For a controlled `Checkbox`, pass `isSelected` and `onChange`.

## Submit [#submit]

Pass `onSubmit` to the `<form>` element. Call `event.preventDefault()`, then read the values from
`FormData`.

```tsx
function handleSubmit(event: FormEvent<HTMLFormElement>) {
	event.preventDefault();
	const data = new FormData(event.currentTarget);
	const email = data.get('email');
}
```

Give the submit button `type="submit"`.

## Reset [#reset]

Give a button `type="reset"` to restore uncontrolled fields. The browser restores their starting
values.

An `onReset` handler must reset state that React owns.

```tsx
const INITIAL_EMAIL = '';
const [email, setEmail] = useState(INITIAL_EMAIL);

<form onReset={() => setEmail(INITIAL_EMAIL)}>
	<TextField label="Email address" name="email" onChange={setEmail} value={email} />
	<Button type="reset">Reset</Button>
</form>;
```

## Validation [#validation]

Luke UI supports browser validation, custom validation, and server validation. The
[Validation guide](/docs/validation) explains each approach.

* Set native constraints such as `isRequired`, `minLength`, `pattern`, or `type="email"` for browser
  validation.
* Pass `validate` for a custom rule. Pass a controlled `errorMessage` for feedback that must update
  on every keystroke.
* Pass `validationErrors` to React Aria's `Form` for server errors. Key each error by the field
  `name`.

Start with native form behaviour. When a form library owns the form state, read
[React Hook Form](/docs/react-hook-form) or [TanStack Form](/docs/tanstack-form).

## Continue learning [#continue-learning]

<Cards>
  <Card href="/docs/validation" title="Validation">
    Validate a field and write the message that names the fix.
  </Card>

  <Card href="/docs/react-hook-form" title="React Hook Form">
    Wire Luke UI fields to React Hook Form.
  </Card>

  <Card href="/docs/tanstack-form" title="TanStack Form">
    Wire Luke UI fields to TanStack Form.
  </Card>

  <Card href="/components/forms/text-field" title="Text Field">
    Collect a labelled line of text.
  </Card>
</Cards>
