Form workflow.
Validation, pending state and recoverable saves around your own service.
Install the source
npx shadcn@latest add https://ui.bld8.dev/r/form.jsonUsage
const form = useForm({
initialValues: {name: ""},
validate: values => ({name: values.name ? undefined : "Enter a name."}),
onSubmit: (values, {signal}) => saveWorkspace(values, signal)
});
<Form form={form}>
<Field label="Name" error={form.errors.name}>
<Input name="name" value={form.values.name}
onChange={event => form.setField("name", event.target.value)} />
</Field>
<Button type="submit" loading={form.pending}>Save</Button>
</Form>In the workspace or ZIP starter, import components from @bld8/ui and include @bld8/ui/styles.css once. Registry installations use your local bld8/ source path. Wrap overlays in UIProvider.
API & behaviour
useForm accepts synchronous or async validation, an abortable submission callback and onSuccess. Return fieldErrors/error for service validation, or normalised values on success. It prevents duplicate submissions, rejects stale completions, tracks dirty/saved state and focuses the first invalid named field. FormErrorSummary shows a service error; use Field for per-field messages. useUnsavedChanges adds a browser leave warning; in-app dismissal confirmation belongs to your composition. Values should be serialisable. Changing initialValues does not reset a mounted form; call reset explicitly.
Notes to self
Test this component with real content, keyboard input and both themes. The example is a starting point; labels, error handling and behaviour still need to fit the application.