Field
A labelled form field — label, description, control and error, with the wiring done for you.
import { ... } from '@corndev/ui' Where requests are sent.
Use an http or https URL.
HTML
No JavaScript required — these classes are the whole component.
<div class="corn-form-field">
<label class="corn-label" for="secret">Client secret</label>
<p class="corn-form-description" id="secret-description">Kept in the keychain</p>
<input class="corn-input" id="secret" type="password" aria-describedby="secret-description" />
<p class="corn-form-error" id="secret-error">Required</p>
</div>
Classes
| Class |
|---|
.corn-form |
.corn-label |
.corn-form-field |
.corn-form-description |
.corn-form-error |
Attributes
| On | Attribute | Values |
|---|---|---|
.corn-form-field | data-layout | inline |
.corn-form-error | data-visible | present or absent |
Vanilla JS
Optional. createField only adds and removes the classes and attributes above, so the HTML is equivalent. Reach for it when you need to change options at runtime.
import { createField } from '@corndev/ui';
import '@corndev/ui/theme';
const el = document.querySelector('#target');
const instance = createField(el, { /* options */ });
Options
| Option | Type | Default | Description |
|---|---|---|---|
error | string | undefined | Message shown beneath the control. Marks the control invalid while it is set; clear it by passing undefined. |
React
Field — from @corndev/ui-react.
Presentational, not bound to useForm: it wires labelling and description, never value or validation state, so it suits a field driven by a form hook and one driven by a parsed document equally. useForm / useField keep working alongside it. It renders exactly one wrapper element, .corn-form-field, which is a flex column carrying min-inline-size: 0 so a field nested in a Fieldset inside a flex column shrinks rather than overflowing.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
label | React.ReactNode | undefined | Names the control. |
description | React.ReactNode | undefined | A hint under the label — what the field wants, before anything is wrong with it. |
error | React.ReactNode | undefined | Shown beneath the control, and marks it invalid while set. |
required | boolean | false | Marks the control required and shows the marker beside the label. |
htmlFor | string | undefined | Id of the control, when you are rendering one with an id of its own rather than letting Field assign one. |
layout | 'stacked'|'inline' | undefined | Where the label goes. Left unset, Field asks the control: a Checkbox, Radio or Switch — or a raw <input type="checkbox"> — puts its label beside it, everything else above it. Set it only to overrule that. |
className | string | undefined | Merged with the Corn UI classes. |
children | React.ReactNode | required | The control. |
React usage
<Field label="Server URL" description="Where requests are sent." error={error} required>
<Input value={url} onChange={e => setUrl(e.target.value)} />
</Field>
The control is the child, so any of them works: Input, Select, Textarea,
Checkbox, Combobox or NumberInput. Field doesn’t own the value; it owns
the part that is easy to get wrong and silent when it is:
- the control’s
id, and the label’sforpointing at it aria-describedbypointing at the description and the erroraria-invalidwhile there is an errorrequiredon the control, with a marker beside the label
An unconnected label looks exactly like a connected one, until a screen reader announces the field as unlabelled.
Checkboxes and switches
A checkbox, radio or switch gets its label beside it rather than above it,
because the control says which kind it is. Pass layout="inline" or
layout="stacked" to choose yourself. Clicking an inline label toggles the
control, and the cursor shows it.
With a form
Field is presentational, so it works with useField from Form. Pass the
error to Field and the rest to the control:
import { Form, useField, Field, Input, Button } from '@corndev/ui-react';
function EmailField() {
const { error, ...input } = useField('email');
return (
<Field label="Email" error={error} required>
<Input type="email" {...input} />
</Field>
);
}
<Form initialValues={{ email: '' }} validate={v => (v.email ? {} : { email: 'Required' })} onSubmit={save}>
<EmailField />
<Button type="submit">Sign in</Button>
</Form>