---
title: Input
description: The single-line text control for short_text, email, phone, url and number fields.
preview: input
---
# Input

A styled `<input>` bound to a field. `Field` renders it for `short_text`, `email`, `phone`, `url` and `number`; import it directly when you compose a field by hand.

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

## Usage
```tsx
// kibble/forms/newsletter.ts
import { defineForm } from 'kibbleforms/schema';

export const newsletter = defineForm({
  slug: 'newsletter',
  name: 'Newsletter',
  pages: [{ fields: [{ key: 'email', type: 'email', label: 'Work email', placeholder: 'you@company.com', autocomplete: 'email', required: true }] }],
});

// Rendered by <Form> automatically, or by hand:
import { Form, Field, Input, Submit } from 'kibbleforms';

<Form schema={newsletter} client={kibble}>
  <Field name="email" />          {/* label + help + <Input /> + error */}
  <Submit />
</Form>
```

## Props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | `string` | required | The field key. |
| `type` | `string` | by field type | Overrides the HTML type: `email`→`email`, `phone`→`tel`, `url`→`url`, `number`→`text` (with `inputMode="decimal"`, or `numeric` when `integer: true`), otherwise `text`. |
| `className` | `string` | control classes | Merged onto the `<input>`. |
| …`<input>` attributes | | | Everything except `name`, `value`, `onChange` (those come from the field). `placeholder` defaults to the schema's `placeholder`. |

Renders `<input data-kb="input">`. For a `hidden` field, `Input` renders `HiddenInput` (`<input type="hidden" data-kb="hidden">`) instead.

`autocomplete` comes from the schema's `autocomplete`, else a default: `email` for email fields, `tel` for phone, `url` for url, and `name` / `given-name` / `family-name` / `organization` when a `short_text` key looks like one. Values are normalized on submit (trimmed, emails lower-cased, phones to E.164, URLs given `https://`), so the input itself never reformats what the visitor types.

## Variants and states
<!-- preview: input-states -->

```tsx
const fields = [
  { key: 'name', type: 'short_text', label: 'Full name', help: 'As it appears on your badge.', autocomplete: 'name', required: true },
  { key: 'website', type: 'url', label: 'Website', placeholder: 'acme.com', defaultValue: 'https://acme.com' },
  { key: 'seats', type: 'number', label: 'Seats', integer: true, min: 1, max: 500, required: true },
];
```

- **Required**: the label gets the required indicator and the input `aria-required`.
- **With help**: `help` renders below the label and is linked via `aria-describedby`.
- **Prefilled**: `defaultValue`, `prefill: { query }` (from the URL) or `initialValues` on `Form`.
- **Error**: after a failed submit, `aria-invalid="true"` turns the border red and `FieldError` appears. Format errors (bad email, bad URL) also show on blur; "required" waits for submit.
- **Disabled**: pass `disabled` when composing; the control dims and ignores input.

## Customization
- Reads `--kb-bg`, `--kb-fg`, `--kb-border`, `--kb-ring` (focus ring, 25 % alpha), `--kb-error` (invalid border), `--kb-muted-fg` (placeholder), `--kb-radius`.
- `className` overrides resolve by utility: `className="h-12 text-base"` replaces `min-h-10 text-[15px]`; `className="rounded-none"` replaces `rounded-kb`.
- CSS hook: `[data-kibble] [data-kb="input"]`, plus `[aria-invalid="true"]` for the error state.
- `classNames={{ input: '…' }}` on `Form` or `KibbleProvider` styles Input, Textarea, Select and DatePicker together; they share one set of control classes.

## Accessibility
Labelled by its `Label` through `htmlFor`; help and error text are in `aria-describedby`; `aria-required` and `aria-invalid` reflect state; `inputMode` gives phones the right keyboard for number fields. Enter submits (or advances a multi-step form).

## Related
[Field](/docs/components/field.md), [Textarea](/docs/components/textarea.md), [Label, Help, FieldError](/docs/components/label-help-error.md), [Schema: field types](/docs/schema.md)
