Form, Label, Field, and Input Group
Use form, label, field, and input group with the current published Askr packages.
@askrjs/ui/form0.0.13@askrjs/ui/label0.0.13@askrjs/themes/form0.0.13@askrjs/themes/label0.0.13@askrjs/themes/field0.0.13@askrjs/themes/input-group0.0.13
How to use form, label, field, and input group
Form, Label, Field, and Input Group should use the standard themed surface unless the application is deliberately implementing its own design system on top of @askrjs/ui.
- Import the themed component from @askrjs/themes/components; use @askrjs/ui directly only when building a custom visual system.
- Keep form, label, field, and input group labels, state, and application actions visible at the call site instead of styling internal DOM nodes.
- Verify keyboard behavior, focus movement or return, disabled state, and both standard themes.
import { Button, Field, FieldHint, FieldLabel, Input } from '@askrjs/themes/components';
<Field>
<FieldLabel for="project-name">Project name</FieldLabel>
<Input id="project-name" name="name" required />
<FieldHint>Shown to everyone in the workspace.</FieldHint>
<Button type="submit">Save project</Button>
</Field>Purpose
This page groups four small, composable pieces that don't stand well alone: `Form` (a semantic `<form>` wrapper from `@askrjs/ui/form`), `Label` (an accessible label with no visual opinion, from `@askrjs/ui/label`), and the themes-only `Field` and `InputGroup` layout primitives that give a labeled input row consistent spacing and error/hint slots. None of these own form validation logic — they're structural and accessibility building blocks you compose around whatever validation approach you're already using.
Install and import
`Form` and `Label` come from `@askrjs/ui/form` and `@askrjs/ui/label`; `Field` and `InputGroup` are themes-only and come from `@askrjs/themes/field` and `@askrjs/themes/input-group` since there's no headless behavior to separate out — they're pure layout components. All four are also re-exported from `@askrjs/themes/components` if you're already importing your styled catalog from there.
Live examples
The examples build up a typical labeled field: a `Field` wrapping a `Label`, an `InputGroup` containing an `Input`, and optionally a `FieldHint` or `FieldError` beneath it, all inside a `Form`. Each piece is shown both alone and composed, so you can see what each one actually contributes to the final markup.
Anatomy
`Form` is just a `<form>` (or, via `asChild`, whatever element you provide) with a `data-slot="form"` attribute — it doesn't intercept submission or manage field state. `Label` renders a native `<label>` by default. `Field` is a `<div>` that accepts an `invalid` flag and optionally contains `FieldHint`/`FieldError` paragraph children. `InputGroup` is a `<div>` that visually attaches its children (e.g. an input and a leading icon) via its `attached` prop.
State model
None of these four hold state themselves — `Form` doesn't track field values, and `Field`'s `invalid` prop is something you pass in based on validation you're already running elsewhere, not something the component derives. This is intentional: form state and validation belong to whatever data layer or form library you're using, and these pieces only handle structure and semantics around it.
Keyboard and accessibility
`Label`'s accessibility contract prioritizes `aria-label` over `textContent` for computing the accessible name, and its `for`/`htmlFor` association follows native label semantics — clicking the label focuses the associated control the same way a native `<label for>` does. Neither `Field` nor `InputGroup` changes tab order or keyboard behavior; they're layout-only, so keyboard behavior is entirely inherited from whatever control you place inside them.
Styling and tokens
`Field` toggles an invalid visual treatment off its `invalid` boolean prop rather than a `data-state` string, so you can drive it directly from your validation result. `InputGroup` takes an `orientation` (`horizontal`/`vertical`) and an `attached` boolean that removes the gap and shared borders between grouped children, which is the look you want for a search box with a leading icon or a trailing button.
API
`Form` takes only `children` plus standard `BoxProps<'form'>`. `Label` takes `children` and, in its non-`asChild` form, `htmlFor` plus the rest of native label props. `Field` takes `invalid` and native `div` props; `FieldHint` and `FieldError` take native paragraph props and are purely presentational — the error text itself is whatever you render as children. `InputGroup` takes `attached` and `orientation`; `InputGroupText` renders inline decorative or label text inside the group and supports `asChild` like most other parts in the library.
Edge cases
Because `Field`, `FieldHint`, `FieldError`, and `InputGroup` don't manage any state, there's no automatic wiring between `invalid` on `Field` and the appearance of `FieldError` — if you want the error message to only show when the field is invalid, that's a condition you write yourself in JSX. Note also that the broader `Field*` family you'll see in the styled catalog (`FieldContent`, `FieldGroup`, `FieldLegend`, `FieldSet`, `FieldTitle`, and so on) lives in `@askrjs/themes/components` as additional composition helpers beyond the core `Field`/`FieldHint`/`FieldError` trio documented here.
Related pages
For the individual controls you'll typically place inside a `Field`, see Input, Textarea, Checkbox, Select, and Switch. For native browser form fallbacks and OTP-style multi-box input, see Native Select and Input OTP under experimental controls.