Skip to content

Forms & inputs

Asking and setting: forms, filters, permission matrices, steppers, allocations, seat maps, controls, procedures and preferences.

Installation & usage

npx @youj/cli add forms-settings

Also installs button, combobox, field, form, icon-alert-triangle, icon-check, icon-undo, input-group, kbd, radio-group, segmented-control, select, spinner, switch, tag-input, youj-core, youj-presets, youj-ui-core.

The CLI copies the block to blocks/forms/settings.tsx with every component it uses, so it’s yours to edit.

Every prop is optional and falls back to the demo, so it renders as you see it until you pass your own. Give it your data, rewrite or translate its strings through copy, and connect its actions:

settingsForm-view.tsx
'use client'

import { SettingsForm, type SettingsFormProps } from '@/blocks/forms/settings'

export function SettingsFormView({ groups, value }: Pick<SettingsFormProps, 'groups' | 'value'>) {
  return (
    <SettingsForm
      groups={groups}
      value={value}
      copy={{ title: 'General' }}
      onValueChange={(values) => {
        // your code
      }}
      onSave={async (values, changed) => {
        // await your API here; the block shows progress until it settles
      }}
      onDirtyChange={(dirty) => {
        // your code
      }}
    />
  )
}

Reference

Props12 props, 3 types

Props

SettingsForm props
PropType
themeThemeInput

Wraps the block in this theme. Default: the theme around it.

–
embeddedboolean

A panel inside another page: an h2 instead of the page's h1 (its groups an h3), and no page around it.

default true

true
fillboolean

Take the whole width it's given instead of stopping at its natural width (768px at 100% scale).

default false

false
copyPartial<SettingsFormCopy>

Any of the block's strings, to rewrite or translate.

–
localestring

Picks the plural of its counts (BCP 47). Default: the app's locale (YoujProvider), else en-US.

–
groupsSettingsGroup[]

The settings, in groups.

default a workspace's name, defaults and security

a workspace's name, defaults and security
valueSettingsValues

The saved settings, controlled: pair it with onValueChange. A new value updates every field you haven't changed.

–
defaultValueSettingsValues

The saved settings to start from, when the block owns them.

default the demo workspace's

the demo workspace's
onValueChange(values: SettingsValues) => void

Called with the settings once a save goes through.

–
onSave(values: SettingsValues, changed: string[]) => Promise<unknown> | unknown

Saves the settings (all of them, and the names of those that changed). Reject with { fields: { name: message } } to put messages under fields, or with an Error to say why in the bar. Default: a short demo wait.

–
onDirtyChange(dirty: boolean) => void

Called when unsaved changes appear or go, to guard your router's own navigation.

–
guardboolean

Ask before the tab closes and hold links elsewhere in the app while changes are unsaved.

default true

true

SettingsGroup

SettingsGroup props
PropType
idRequiredstring–
titleRequiredstring–
descriptionstring–
fieldsRequiredSettingsField[]–

SettingsOption

SettingsOption props
PropType
valueRequiredstring–
labelRequiredstring–
descriptionstring

A second line (choice cards), or text the search also matches (a searchable select).

–

SettingsSaveError

SettingsSaveError props
PropType
fieldsRecord<string, string>–
messagestring–
Strings28 keys for copy

Strings

copy takes any of these keys; the rest keep their defaults. A key whose text holds a value (a count, a name) takes a function.

titledescriptionfootersavedchangesdiscardsavesavingsavedLabelsaveFailedinvaliddiscardedundoleavesaveAndLeavediscardAndLeavefailedresetresetLabelemptyonoffrequiredtooLongleftcheckingavailablesearch
Built from12 components
Accessibilitylabels, errors, focus, keyboard, announcements

Accessibility

Every control sits in the kit's Field with its label, description and error, so it's named and described without extra wiring, and aria-invalid marks a field with a problem. Errors appear under their fields as soon as a field is left, and they're read out. Saving, sending or continuing checks every field and moves focus to the first problem. The application also gathers a step's problems in a summary at the top, which takes focus and links to each field.

The settings save bar announces the number of changes. Each Reset says what it restores, and focus goes back to the control afterwards. ⌘S and Ctrl+S save, and when a link is held, the reason is read out and focus moves to Save. The payment address lookup follows the WAI-ARIA combobox pattern: arrow keys move through the matches while focus stays in the field, and the number of matches is read out. The support request's help articles open in a new tab and say so.

Chip filters has a labelled search field, the result count is spoken once it settles, and the checklist is a labelled group with a tri-state box and a hidden summary of the range. Editing chips and moving controls stay in sync both ways.

Permission matrix lists its roles as tabs (the arrow keys move between them) and names each level picker for its area and role ("Secrets access for Engineer"); a role's tab says how many unsaved changes it holds, and the footer's count is a polite live region. The grid view is a keyboard matrix: the arrow keys move between cells, Space steps a level, and a bar for bulk changes appears for the selected rows and columns. Review opens a dialog with the diff. The owner role is locked. The stories show other worlds, with their own names and words.

Setup stepper is a Wizard with a Stepper. The config preview is a labelled region, and the option pills are a labelled group of toggles with aria-pressed.

Split allocation names each row's category, amount and percent; the allocation bar is decorative, and the status line (a live region) says in words how much is allocated, left or over, and is the reason Save is unavailable. The split can be typed, added to and removed from entirely by keyboard.

Slot sorter is a section named by its title. Every item has a slot button that names the item, its detail, the slot and how sure the sorting is, opens a menu of the slots and takes the number keys 1 to 4, so dragging is never the only way; a correction is announced with the new counts, the bar and its legend are one labelled image with the counts in text, and the picture is decorative. A slot that is off is dimmed in the menu and takes no item.

Seat plan is one ARIA grid with one tab stop: arrow keys move between seats and rows, Home and End jump to a row's ends, Ctrl+Home and Ctrl+End to the first and last seat, and Space or Enter picks or releases. Each seat is named with row, number, price and availability. The selection's remove buttons are named per seat, and the hold is role="timer" with live announcements off.

In Device controls each room tile is a button with aria-pressed and a name, and its lights are a switch labelled for the room. The thermostat is a Dial with a label and a hidden description; drag the knob or use the arrow keys. The mode switch is a labelled segmented control and the schedule marks the current slot with aria-current="time".

Procedure's share ring is an image whose label sums up the total and each part. The switch between one yield and all of it is a labelled segmented control, steps carry a hidden step number, and each timer is a toggle button with aria-pressed and a label that says whether it starts, pauses or resets and for how long. Save is a pressed toggle.

Sound settings has a labelled master switch and volume slider, and a Play button named for each event. With sounds off the pack, volume and rows go inert. Under reduced motion the page says why sounds are quiet, and previews are silent without a provider.

Effects inspector is a stack of panels, each a named section with an enable checkbox, an eye that says Hide or Show, and Remove. Every field is keyboard operable: the number fields are spinbuttons (arrow keys, with Shift and Alt for the pace, PageUp and PageDown, Home and End), the hex fields commit on Enter, and the shadow's kind and the blur's type are a menu and a segmented control. The preview is described as an image, and the CSS beside it is plain text. Under reduced motion the panels fold at once.

Cookie consent opens its preferences in a dialog that traps focus and closes with Escape. Each category is a switch, Necessary stays on, each cookie table has a visually hidden header row, and saving is announced in a status region.

The cart is a Sheet, so it traps focus and closes with Escape. Its trigger is named with the item count, and each line has a remove button and a quantity field named for its product, in a labelled list.

Under reduced motion, steps swap in place, the save bar appears without rising or shaking, and fields appear and disappear instantly. Colors and messages still show every state.

Under reduced motion pulses and pings stop and everything else changes in place.

Under reduced motion numbers and the ring are drawn in place; the timers still count.

Under reduced motion rows, rings and steps change in place, the chosen plan is simply built and the bar jumps to its new split.