Form
The wrapper every multi-field layout starts from.
View code
let issues: ReadonlyArray<Issue> = [];
let created = false;
function submit(event: SubmitEvent) {
event.preventDefault();
let form = event.currentTarget as HTMLFormElement;
issues = checkNewWorkspace(new FormData(form));
created = issues.length === 0;
void handle.update();
}
<Form
method="post"
issues={issues}
// The native constraints below already block an empty submit with no script;
// the schema is what catches the shapes an attribute cannot express.
noValidate
mix={[on<HTMLFormElement, "submit">("submit", submit)]}
>
{created ? <Alert color="success">Workspace created.</Alert> : null}
<TextField
label="Workspace name"
name="name"
required
description="Shown in the sidebar and on invitations."
/>
<TextField
label="Address"
name="slug"
required
description="Teammates reach the workspace at this.acme.dev."
/>
<TextField
label="Billing owner"
name="ownerEmail"
type="email"
required
description="Invoices and renewal notices go here."
/>
<Label htmlFor="region">Data region</Label>
<Select id="region" name="region">
<Select.Option value="eu" selected>
Europe — Frankfurt
</Select.Option>
<Select.Option value="us">United States — Iowa</Select.Option>
<Select.Option value="ap">Asia Pacific — Sydney</Select.Option>
</Select>
<NumberField>
<Label htmlFor="seats">Seats to reserve</Label>
<NumberField.Group>
<NumberField.DecrementButton aria-label="One fewer seat" />
<NumberField.Input id="seats" name="seats" min={1} max={200} defaultValue={10} />
<NumberField.IncrementButton aria-label="One more seat" />
</NumberField.Group>
</NumberField>
<Checkbox
name="terms"
value="accepted"
required
aria-describedby="terms-error"
aria-invalid={issueFor(issues, "terms") ? "true" : undefined}
>
I accept the data processing agreement
</Checkbox>
<FieldError id="terms-error" hidden={!issueFor(issues, "terms")}>
{issueFor(issues, "terms")}
</FieldError>
<Button type="submit">Create workspace</Button>
</Form>Installation
An ordinary dependency: install it, import the theme once, and import the component where it is used.
npm add @sdxc/uiimport "@sdxc/ui/theme.css";Usage
import { Form } from "@sdxc/ui";Renders a native <form> laying children out in a single column, and
provides Form.Context so descendant fields resolve their own
issues by name and focus the first invalid field with no client JS. An
issue with no path names no field to attach to, so it renders as its
own leading Alert instead of being silently unreachable — a refusal
decided before any field was reached still reaches the person filling
the form in.
Props
Read from the component's own types, so every prop, every default and every allowed value is listed.
| Prop | Type | Description |
|---|---|---|
issues? | ReadonlyArray<Issue> | Validation issues from a parseSafe (or equivalent Standard Schema) result, provided to every descendant field through component context. Defaults to no issues, matching a first render. |
Also accepts everything in TagProps<"form">.
Also exported
Other components this module publishes.
readFormContextresolveFieldIssue