---
title: Field
description: Label, help text, the right control for the field's type, and the inline error, from one `name`.
preview: field
---
# Field

`Field` renders one schema field completely: [Label](/docs/components/label-help-error.md), [Help](/docs/components/label-help-error.md), the control chosen by `field.type`, and [FieldError](/docs/components/label-help-error.md). It is what `Form` uses for every field when you give it no children.

## Preview
<!-- preview: field -->

## Usage
```tsx
import { Form, Field, FieldControl, Submit } from 'kibbleforms';

<Form schema={schema} client={kibble}>
  <Field name="name" />                       {/* label + help + control + error */}
  <Field name="seats" className="gap-1">      {/* children replace the control only */}
    <FieldControl name="seats" />
  </Field>
  <Submit />
</Form>
```

## Props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | `string` | required | The field key. Throws if the key is not in the form's schema. |
| `children` | `ReactNode` | the control for `field.type` | Replaces the control slot only; label, help and error still render. Put your own input here using `useField(name).inputProps`. |
| `className` | `string` | `flex flex-col gap-1.5` | Merged onto the wrapper. |
| …`<div>` attributes | | | Spread on the wrapper. |

Rendered: `<div data-kb="field" data-type="<field.type>">` containing `[data-kb="label"]`, `[data-kb="help"]` (when the schema has `help`), the control, and `[data-kb="error"]` (when invalid).

`FieldControl` (`name` only) renders just the control. Which control a type gets:

| Field type | Control | data-kb |
| --- | --- | --- |
| `short_text`, `email`, `phone`, `url`, `number` | [Input](/docs/components/input.md) | `input` |
| `long_text` | [Textarea](/docs/components/textarea.md) | `textarea` |
| `dropdown` | [Select](/docs/components/select.md) | `select` |
| `single_choice`, `multiple_choice` | [Choice](/docs/components/choice.md) | `choice` |
| `yes_no` | [YesNo](/docs/components/yes-no.md) | `yesno` |
| `rating` | [Rating](/docs/components/rating.md) | `rating` |
| `date` | [DatePicker](/docs/components/date-picker.md) | `date` |
| `consent` | [Consent](/docs/components/consent.md) (the row is its own label, so no `Label`; `Field` adds `Help` and `FieldError` under it) | `consent` |
| `hidden` | `<input type="hidden" data-type="hidden">` with no wrapper, label or error (`className` does not apply) | `hidden` |

Fields hidden by a `showWhen` condition render nothing at all (see [Conditional logic](/docs/conditional-logic.md)).

## Variants and states
The preview above has a required field and a number field: submit empty to see the error state, which sets `aria-invalid` on the control and renders `FieldError` below it. Typing clears the error for that field.

## Customization
- Spacing between label, control and error is the wrapper's `gap-1.5`; override with `className="gap-1"` (tailwind-merge drops the default).
- Target a type with CSS: `[data-kb="field"][data-type="long_text"] { … }`.
- To change the structure (help below the control, label inside the control…), compose `Label`, `Help`, `FieldControl` and `FieldError` yourself, or build on `useField` from `kibbleforms/headless`.

## Accessibility
`Field` wires the pieces: the label's `htmlFor` targets the control (`aria-labelledby` for radio groups), help and error ids go into the control's `aria-describedby`, required fields get `aria-required="true"` (not the native `required`, so Kibble's messages show instead of the browser's), and invalid fields get `aria-invalid="true"`.

## Related
[Form](/docs/components/form.md), [Label, Help, FieldError](/docs/components/label-help-error.md), [Headless: useField](/docs/components/headless.md), [Schema: field types](/docs/schema.md)
