Skip to content
Tier

Form layout

Pro

Lays out a form's fields: stacked, or with the labels in a column beside the controls. Put Fields in it, grouped by FormSection where a long form has parts. A horizontal form keeps its labels in a column only while its container is wide enough (36rem), and stacks them on a narrow one, so the same form works in a page and in a side panel.

Markdown
Version 5.2.0· other versions

This page is for 5.2.0, the current release. An earlier release’s page is the Markdown that release published, so the docs match the version you have installed (candor-lock.json records it).

Not in the docs of 1.3.0, 1.4.0, 1.5.0, 1.5.1, 1.6.0, 1.7.0, 1.8.0, 1.8.1, 1.9.0, 1.9.1, 2.0.0, 2.1.0, 2.2.0, 2.3.0, 2.4.0, 2.5.0, 2.5.1, 2.6.0, 2.6.1, 2.6.2, 2.7.0, 2.8.0, 2.9.0, 2.10.0, 3.0.0, 4.0.0, 4.0.1, 4.0.2 and 4.1.0: the page came later, or had another name then.

Docs pages weren’t kept for 1.0.0, 1.0.1, 1.1.0, 1.1.1, 1.2.0 and 1.2.1: they are kept from 1.3.0 on, and before that only llms.txt was published.

Preview

Loading the preview

Install

With the Candor CLI

First time? Get started sets up the registry and theme. Or with shadcn directly: npx shadcn@latest add @candor/form-layout

Brings in: stack, tokens, utils · npm: @base-ui/react@^1.9.0

Usage

TSX · The example above
"use client"

import { FormLayout, FormSection } from "@/components/candor/form-layout"
import { Field } from "@/components/candor/ui/field"
import { Input } from "@/components/candor/ui/input"
import { Textarea } from "@/components/candor/ui/textarea"

// Labels sit in a column while the container is wide enough, and stack on a narrow one.
export default function Example() {
  return (
    <FormLayout orientation="horizontal" className="w-full max-w-2xl">
      <FormSection title="Agent" description="How the agent is named in the app.">
        <Field label="Name" required>
          <Input name="name" />
        </Field>
        <Field label="Instructions" hint="What it should and shouldn't do." optional>
          <Textarea name="instructions" rows={3} />
        </Field>
      </FormSection>
      <FormSection title="Escalation">
        <Field label="Email" hint="Where it sends what it can't decide.">
          <Input name="email" type="email" />
        </Field>
      </FormSection>
    </FormLayout>
  )
}

<FormLayout>

PropTypeDefaultDescription
orientation"vertical" | "horizontal" | undefined"vertical"`vertical` (the default) puts each label above its control; `horizontal` puts labels in a column beside the controls on a wide container.
labelWidthkeyof typeof labelWidths | undefined"md"How wide the label column is in a horizontal form: `sm` 8rem, `md` 11rem (the default), `lg` 14rem.
renderuseRender.RenderProp | undefined—The element to draw instead of a `<div>`: `render={<form onSubmit={…} />}` to make it the form.
classNamestring | undefined—Classes for the element, applied last, so they win over the props.
childrenReact.ReactNode | undefined—The fields and sections.

Also takes the props of React.ComponentProps<"div">.

<FormSection>

A part of a long form, as a fieldset named by its legend, so a screen reader says which part a field is in. The description is read with the part.

PropTypeDefaultDescription
titlerequiredstring—The part's name, as its legend: "Contact details".
descriptionstring | undefined—A sentence under the title saying what the part is for.
classNamestring | undefined—Classes for the fieldset, applied last.
childrenReact.ReactNode | undefined—The part's fields.

Also takes the props of Omit<React.ComponentProps<"fieldset">, "title">.