Settings Section
The repeating unit of a settings page: a title and description on the left, a card with the content on the right, and a Save and Cancel bar that appears only when something has changed. Use SettingsRow inside for toggle lists. Your onSave does the work. UI permission is not security: the server must check again.
A ready-made piece, built from the library's own parts.
A block puts several components together into something you would otherwise assemble by hand. It never fetches data: you give it the data, or answer its callbacks. It is copied into your project like any component, with the parts it uses, so you can change anything.
Basic
Profile
Your name and the email address we write to.
Stacked
Company
Shown on your invoices.
Split
Company
Shown on your invoices.
Toggles
Notifications
Choose what we email you about.
Danger
Delete workspace
This removes the workspace and everything in it for all members.
States
Billing email
Try saving a changed address: the server refuses it.
Time zone
Try saving a change: the server is unavailable.
Permissions
Company
Shown on your invoices.
ClassNames
Display name
How your name appears to others.
Settings page
Settings
Profile
Your name and the email address we write to.
Notifications
Choose what we email you about.
Delete account
Removes your account and your personal data.
Installation
npx rdloom add settings-sectionCopies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes.
Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/settings-section.jsonWorks, but without upgrade tracking.
Usage
import { FormTextField, SettingsSection } from "@rdloom/react";
<div className="flex w-full justify-center">
<div className="w-full max-w-4xl">
<SettingsSection
title="Profile"
description="Your name and the email address we write to."
defaultValues={{ name: "Lena Fischer", email: "lena.fischer@example.com" }}
onSave={async () => {
await wait(600);
}}
>
<div className="flex flex-col gap-4">
<FormTextField name="name" label="Name" isRequired />
<FormTextField name="email" label="Email" type="email" isRequired />
</div>
</SettingsSection>
</div>
</div>API Reference
Defined by the spec. Components also accept the props of the React Aria component they wrap.
| Prop | Type | Default |
|---|---|---|
titlerequiredThe section's heading, e.g. "Profile". | string | none |
descriptionOne or two sentences on what the section controls. | string | none |
headingLevelHeading level of the title, 2 to 6. | number | 2 |
childrenThe content of the card: form fields (when onSave is set they are connected to the section's form), SettingsRow lines, or any content. | node | none |
orientationstacked puts the title and description above the card, full width. split puts them in a left column beside the card from the md breakpoint up and stacks them on a phone. | "stacked" | "split" | "stacked" |
tonedanger marks a section with destructive actions: a danger-colored border and title. The color is never the only signal: say it in the title. | "default" | "danger" | "default" |
defaultValuesThe starting values of the section's form. Needed with onSave. Fields inside are connected by name (FormTextField, FormSwitch, Field). | Record<string, any> | none |
onSaveSaves the changed values; yours, async. Resolve for success. Return { fieldErrors } or { formError } to show a problem and keep the changes. Without onSave the section has no footer. | (values: Record<string, any>) => void | SubmitResult | Promise<void | SubmitResult> | none |
onCancelCalled after the person discarded their changes with Cancel. | () => void | none |
saveLabelThe text of the save button. | string | "Save changes" |
successMessageThe status message shown and announced after a save that worked. | string | "Changes saved" |
permissionsWhat the app allows: edit. Disabled makes the content read-only: every field inside is disabled and the reason is shown above the content and linked to it. Hidden renders nothing; disabled keeps the control reachable (aria-disabled) with the reason shown and read, and nothing runs. This only changes what people see: the server must check again. | Permissions<"edit"> | none |
classNamesExtra class names for single parts, so you can restyle one part without editing the file. Keys: root, header, title, description, card, content, footer, saveButton, cancelButton, reason, status. | Partial<Record<"root" | "header" | "title" | "description" | "card" | "content" | "footer" | "saveButton" | "cancelButton" | "reason" | "status", string>> | none |
Accessibility
Role region, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.
Keyboard
- Tab: moves through the fields, then Cancel and Save changes when they are shown
- Enter in a text field: saves
Screen readers announce
- "Profile, region"
- "Unsaved changes" when the first change is made
- "Save changes, button"
- "Changes saved" after saving
What your code must do
- The section is a region named by its heading, so it can be reached from a landmark list
- The Save and Cancel bar appears only when a value differs from the last saved one; a polite status says "Unsaved changes" when it appears
- After a save that worked a status says the success message and focus moves to it, because the Save button has gone
- Cancel puts the saved values back and moves focus to the first field
- A failed save keeps the changes, moves focus to the error summary and links each problem to its field
- When editing is not allowed every field is disabled and the reason is visible text linked to the content with aria-describedby
Block contract
- Data
- The starting values of the section's form and an async onSave; the content comes from the app as children.
- Data states
- ready
- Permissions
- edit
- Events
- onSave, onCancel
- You can replace
- classNames for each part; orientation and tone; title, description, saveLabel and successMessage; any Form fields or SettingsRow lines as children; useSettingsSection() lets your own controls read the read-only state
Guidelines
Use it when
- A settings page made of several groups of related options, each with its own Save
- Toggle lists where each switch saves at once (without onSave, with SettingsRow)
- A danger zone with tone="danger"
Avoid it when
- A single long form with one Save: use Form
- A form in a dialog or sheet: use Dialog or Sheet with Form
Don't
- Treating the permissions prop as protection: UI permission is not security, so the server must check again
- Saving from inside the component: onSave is yours
- Putting a Save button inside the children: the footer already has one
- Hiding the reason when editing is not allowed: say why, so people know whom to ask
Design tokens
The semantic tokens this component uses. Change them once and every component follows; see Design tokens.
--rd-color-surface-default--rd-color-surface-subtle--rd-color-border-default--rd-color-text-default--rd-color-text-muted--rd-color-feedback-danger--rd-color-feedback-success--rd-elevation-raised--rd-radius-overlay