Skip to content

FormError

<FormError> reads errors.root from the surrounding react-hook-form context and renders it as a destructive banner at the top of the form. Use for whole-form errors — server 4xx/5xx, conflicts, network failures — not for per-field validation.

import from "@na/ui/components/FormError" ▶ Open in Storybook packages/ui/src/components/FormError.tsx

When to use

  • The server rejected the form (“A workspace with this name already exists”).
  • A network request failed during submit and the user should retry.
  • Cross-field validation that can’t be attributed to one field (“Start date must be before end date” — though preferring per-field where possible is better).

Don’t use FormError for: per-field validation (use <FormMessage /> inside the FormField), transient feedback after a successful action (Sonner), or inline guidance (Alert).

Default

Set errors.root via form.setError('root', { message: '…' }) and FormError picks it up automatically.

import { FormError } from '@na/ui/components/FormError';
<Form {...form}>
<form onSubmit={form.handleSubmit(async (values) => {
try {
await api.create(values);
} catch (err) {
form.setError('root', { message: extractErrorMessage(err) });
}
})}>
<FormError /> {/* Renders only when errors.root is set */}
<FormField />
<Button type="submit">Save</Button>
</form>
</Form>

With explicit children

If you want a static banner unrelated to errors.root, pass children. The component still requires a Form context but ignores errors.root when children are provided.

<FormError>This integration is in beta and may change.</FormError>

API

Prop Type Default Description
children ReactNode Override message. When set, FormError ignores errors.root.
className string Forwarded to the banner.

Design guidelines

✓ Do

  • Place FormError at the top of the form, before any FormField.
  • Set errors.root when the form-level submit fails (network, 409, 500).
  • Lead the message with what happened, not a stack trace. "Couldn't save: name is taken."

✗ Don't

  • Use FormError for per-field validation. That's FormMessage inside FormField.
  • Render a generic "An error occurred." with no actionable detail. The user can't do anything with that.
  • Render FormError outside a Form context. Without it the component returns null.

Accessibility

  • Renders with role="alert" and aria-live="assertive" — screen readers announce the error when it appears.
  • The icon is aria-hidden; the message text is the announceable name.
  • Form — The provider that supplies the error state.
  • FormMessage — Per-field error.
  • Alert — When the message isn’t tied to a form.

▶ Open FormError stories in Storybook