Skip to content

Design system

Documentation

DesignPatterns / Settings

Patterns / Settings

Beta

Settings

A settings pattern groups related preferences, makes unsaved changes visible, and reports the result of saving without moving the person away from context.

On this page

Preview

Settings pattern

Group related settings, make unsaved changes visible, and keep saving feedback next to the action that caused it.

Workspace settings

Keep identity and notification defaults in one predictable place.

Workspace identity

This name appears in invitations and project headers.

Notifications

Choose where workspace activity replies should go.

Replies from activity emails use this address.

Workspace settings with changed, saving, success, and error states

When to use

Use Settings when a person edits durable preferences rather than a one-off record. Give each group a short heading and description so a long page remains scannable.

States

Settings pattern states and required feedback
StateWhat to showAction contract
changedA visible unsaved marker and enabled Save/Discard actions.Keep the person on the same group after editing.
savingA stable-size loading button and disabled duplicate actions.Prevent repeat submits while the request is in flight.
savedA short status message near the actions.Use the same verb as the action: “Settings saved.”
errorA field error for invalid input or a service error for a failed save.Explain what can be corrected or when to try again.

Layout

  • Keep the section heading, explanation, fields, and actions in one surface when they belong to the same decision.
  • Disable Save only when there are no changes or a save is already in flight; do not disable it merely because a field is optional.
  • Discard restores the last saved snapshot, not an empty default.
  • Use a page for settings that need navigation or a multi-step workflow; do not force a large settings area into a Dialog.
settings-example.tsx
"use client";

import { useState } from "react";

import { Button } from "@phuctech/ui/components/button";
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@phuctech/ui/components/card";
import { Field, FieldLabel } from "@phuctech/ui/components/field";
import { Input } from "@phuctech/ui/components/input";

export function WorkspaceSettings() {
  const [name, setName] = useState("PhucTech");
  const [savedName, setSavedName] = useState("PhucTech");
  const changed = name !== savedName;

  return (
    <Card>
      <form onSubmit={(event) => { event.preventDefault(); setSavedName(name); }}>
        <CardHeader><CardTitle>Workspace settings</CardTitle></CardHeader>
        <CardContent>
          <Field>
            <FieldLabel htmlFor="workspace-name">Workspace name</FieldLabel>
            <Input id="workspace-name" value={name} onChange={(event) => setName(event.target.value)} />
          </Field>
        </CardContent>
        <CardFooter>
          <Button type="submit" disabled={!changed}>Save changes</Button>
          <Button type="button" variant="outline" disabled={!changed} onClick={() => setName(savedName)}>Discard</Button>
        </CardFooter>
      </form>
    </Card>
  );
}

Accessibility

  • Use headings for groups and keep their descriptions adjacent to the controls they explain.
  • Keep status feedback in an aria-live="polite" region so it is announced without interrupting editing.
  • Do not communicate unsaved, error, or success state by color alone; include text.
  • Preserve focus after saving and after validation so keyboard users do not lose their place.

Do and don’t

Do

Show the changed state as soon as a value differs from the saved snapshot and make recovery obvious.

Don’t

Auto-save every keystroke for settings with consequential effects or silently discard edits on navigation.

  • Field keeps each setting’s label and validation message connected.
  • Dialog is for a focused subtask, not a full settings page.
  • Button carries Save, Discard, and loading semantics.