Components

Form

How Kanz fields come together into a form: sections with a title and a short why, fields in reading order, one clear action bar, and errors that say how to fix them. No form library built in: it works with your own state, or with react-hook-form and Zod.

Figmashadcn equivalentform

Preview

Mirrored is the same English content laid out right to left: it separates a mirroring bug from a translation one.

Installation

SoonInstalling from the registry is coming soon. Until then, these are the commands you will run.
npx ritla add form

Also works with shadcn: npx shadcn@latest add @ritla/form

Dependencies it brings: @base-ui/react, @phosphor-icons/react

Installing by hand copies the component's source into your project. The source comes with a Ritla UI license, so the CLI is the short way to the same files.

Usage

import { Form, FormActions, FormErrorSummary, FormSection } from "@/components/ui/form";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
<Form onSubmit={save}>
  <FormSection title="Personal details" description="We use these on receipts and transfers.">
    <FormErrorSummary errors={summary} focusKey={submitCount} />
    <Input id="email" label="Email" required type="email" error={errors.email} />
    <FormActions status="Saved 2 minutes ago">
      <Button variant="outline" size="md">Cancel</Button>
      <Button size="md" type="submit">Save changes</Button>
    </FormActions>
  </FormSection>
</Form>

Examples

Stacked, with errors

layout="stacked" for phones, dialogs and narrow columns, and align="full-width" puts Save above Cancel at full width. After a failed save, the summary is at the top and each field shows its error in place. FormOptional writes "(optional)" after a label; mark the few optional fields, not the many required ones.

Get the code ›

With react-hook-form and Zod

For teams that already use them. Kanz adds neither: install them yourself (npm install react-hook-form zod @hookform/resolvers). register gives each field its name, ref and handlers; the schema's messages go to error and to the summary, and submitCount brings focus back to the summary on every save; shouldFocusError: false leaves focus to the summary.

Get the code ›

API reference

Form

PropTypeDefaultDescription
onSubmit(event) => voidNoneOn submit. Check every field here, and show the summary when something needs fixing.
noValidatebooleantrueTurns off the browser's error bubbles, so errors show in the Kanz fields and the summary.

FormSection

PropTypeDefaultDescription
titleReactNodeNoneThe section title ("Personal details"). The section is a <section> named by it.
descriptionReactNodeNoneThe short why under the title: why you ask for these.
layout"auto" | "two-column" | "stacked""auto"Figma's Layout: two columns for desktop settings (the why on one side, the fields on the other), or stacked. auto stacks below the lg breakpoint.

FormRow / FormOptional

PropTypeDefaultDescription
children (FormRow)ReactNodeNoneTwo fields side by side when there is room (first and last name), stacked on phones.
FormOptionalcomponentNoneWrites "(optional)" in the page language after a label.

FormErrorSummary

PropTypeDefaultDescription
errors{ id, label, message }[]NoneOne entry per field to fix, in form order. id is the field control's id, so the link moves focus there. Nothing renders while the list is empty.
titleReactNodeNoneThe heading. Default: "Fix 2 things before you save" in English and «صحّح الحقول التالية:» in Arabic, the count in the page's numerals.
focusKeyunknownNoneTakes focus whenever it changes: pass the submit count, so every failed submit lands here and fixing a field never pulls focus away from it.
autoFocusbooleantrueTakes focus when it appears.

FormActions

PropTypeDefaultDescription
childrenReactNodeNoneThe buttons in reading order: Cancel (Outline), then Save (Filled, the only filled button).
statusReactNodeNoneThe save status at the start of the bar ("Saved 2 minutes ago"), read out politely when it changes.
align"auto" | "end" | "full-width""auto"Figma's Align: Save at the end on desktop, or Save above Cancel at full width on phones. auto is full width below the sm breakpoint.

Accessibility

Keyboard

Keys are written for left-to-right. In right-to-left the arrow keys swap: the one pointing toward the reading direction moves forward.

KeyAction
TabThrough the fields in reading order, then Cancel and Save.
EnterIn a text field, submits the form; on a summary link, moves focus to its field.

ARIA

  • The summary is role="alert" and takes focus after a failed submit, so keyboard and screen reader users land on it; each line links to its field.
  • Check a field when people leave it, not while they type; the error clears as soon as the value is right.
  • Every field has a visible label. The asterisk is decoration: required on the field is what announces it.
  • An error names the field and the fix ("Enter an address like name@example.com"), never just "Invalid".

Arabic and RTL notes

Mirrors

  • In two columns: the title and the why on the right, the fields on the left.
  • The action bar: Save at the inline end (the left) with Cancel beside it, the save status on the right.
  • The summary: the warning glyph on the right, the text after it.

Does not mirror

  • Email, phone and IBAN stay left to right, isolated inside the Arabic page.
  • The warning glyph doesn't mirror.
Numerals
The count in the summary's default title follows numerals, as does any number you write into messages through formatNumber.
Arabic typography
The section title is body-18 Semibold and the why label-14; Arabic lines are taller, so no fixed heights on text.
Mixed-direction text
A summary line is dir="auto": an English message from a server on an Arabic page keeps its order.