Skip to main content
Nexus Design System
GitHub

Forms

Build a settings form from nxds components. The form comes in three versions: plain React state, React Hook Form and TanStack Form.

Field holds a label, a control, help text and an error. FieldSet and FieldLegend group related fields.

1. Choose one implementation

NeedCopyComponent foldersAdditional dependency
A small settings form with local React statesettings-form.tsx, settings-layout.tsx and settings-fields.tsxbutton, button-group/button-group-context.ts, checkbox, field, input, label, separator, spinner and lib/None beyond nxds and React
Your app uses React Hook Formreact-hook-form.tsx, settings-layout.tsx and settings-fields.tsxbutton, button-group/button-group-context.ts, checkbox, field, input, label, separator, spinner and lib/react-hook-form 7
Your app uses TanStack Formtanstack-form.tsx, settings-layout.tsx and settings-fields.tsxbutton, button-group/button-group-context.ts, checkbox, field, input, label, separator, spinner and lib/@tanstack/react-form 1
Read-only details or temporarily unavailable settingssettings-display.tsxcheckbox, description-list, field, input, label, separator and lib/None beyond nxds and React

Copy only the version your app uses. settings-layout.tsx holds the shared types, validation rules, and the status, Save and Cancel buttons. settings-fields.tsx renders the fields.

2. Connect application data

// app/settings/profile-settings.tsx
'use client';

import { SettingsForm } from '@/patterns/forms/settings-form';
import type { SettingsValues } from '@/patterns/forms/settings-layout';

import { saveProfile } from './save-profile';

type Profile = SettingsValues & { id: string };

export function ProfileSettings({ profile }: { profile: Profile }) {
  return (
    <SettingsForm
      key={profile.id}
      title="Profile settings"
      initialValues={{
        name: profile.name,
        email: profile.email,
        updates: profile.updates,
      }}
      onSave={(values) => saveProfile(profile.id, values)}
    />
  );
}

All three versions take the same props:

PropDoes
titleNames the form. Give each form on a page its own title.
initialValuesSets the starting values. Changing it later doesn’t overwrite what the user is typing. To load a different record, change the form’s key.
onSaveSaves the values. Must resolve with the saved record, or reject if the save fails.

With fetch, throw when response.ok is false. Parse the response into the SettingsValues shape before you resolve. If your API wraps the record, like { data: … }, unwrap it first.

After a save, the form shows the record you resolved with, so changes your server made replace what the user typed. Fields the form doesn’t edit are ignored.

If the save fails, the user’s edits stay so they can retry. Cancel restores the last saved values. Save is blocked while a save is running.

Local state

Edit a field to enable Save. To see validation, clear the name or enter an invalid email, then save.

Checkboxes don’t save until the user clicks Save. Use a switch only for a change that applies immediately.

Profile settings

Update your details and preferences. Changes apply when you save.

Personal details

The name other people see when you collaborate.

Used for account notices and the updates you choose below.

Preferences

Receive occasional news about new features. This preference is saved with your details.

No unsaved changes.

Form libraries

Both versions behave like the local-state form. They validate on save, focus the first invalid field, and clear an error when the field is edited.

React Hook Form

Uses useForm and useController.

Profile settings with React Hook Form

Update your details and preferences. Changes apply when you save.

Personal details

The name other people see when you collaborate.

Used for account notices and the updates you choose below.

Preferences

Receive occasional news about new features. This preference is saved with your details.

No unsaved changes.

TanStack Form

Uses useForm and useField. It detects unsaved changes with !isDefaultValue.

Profile settings with TanStack Form

Update your details and preferences. Changes apply when you save.

Personal details

The name other people see when you collaborate.

Used for account notices and the updates you choose below.

Preferences

Receive occasional news about new features. This preference is saved with your details.

No unsaved changes.

Read-only and disabled

Read-only details

Use a DescriptionList for values the user can read but not edit. Say who can change them, and show empty values as “Not provided” instead of leaving them blank.

Account details

These details are managed by your organization. Contact an administrator to change them.

Name
Priya Shah
Email
priya@example.com
Team
Not provided

Temporarily unavailable

Disable a control only when it’s temporarily unavailable. Show the reason, and link the reason to the control with aria-describedby.

Delivery settings

Delivery is paused while your address is being verified.

What your app owns

  • Permissions, server validation, loading data, leave-page warnings and saving. Replace the sample fields and validation rules with your own.
  • Error messages. A failed save shows a fixed message, never the error’s own text. To show server errors, edit the submit handler.
  • The source lives in packages/react/src/patterns/forms. Update its imports to point at your copied components.

The forms on this page don’t save anything.