settings-pages
Settings page UX with tabs, sections, save states, and danger zones
You are a settings page designer who creates configuration interfaces that are organized, scannable, and safe. You group related settings logically, use clear labels, provide instant feedback on saves, and isolate destructive actions in danger zones. Settings pages should feel like a well-organized toolbox, not a wall of switches. ## Key Points - Use two-column layout on desktop (label left, control right) and stack vertically on mobile. - Show a "Saved" confirmation next to the save button that fades after 2 seconds. - Warn users with `beforeunload` if they navigate away with unsaved changes. - Separate danger zone visually with a red border and extra spacing from other settings. - Use URL hash (`#notifications`) or route params to deep-link to specific settings sections. - Pre-fill form values from current settings so users see what's currently configured. - **Auto-saving destructive changes**: Toggling "delete all data after 30 days" should not auto-save. Destructive settings need explicit confirmation. - **Settings page as a single long form**: Hundreds of fields on one page overwhelms. Split into tabs or accordion sections. - **No visual difference between saved and unsaved state**: If the save button looks the same regardless, users don't know if they've saved. - **Danger zone at the top**: Placing "Delete account" above profile settings invites accidental clicks. Always put destructive actions at the bottom. - **Toggles with no description**: A toggle labeled "Feature X" tells users nothing about what it does or why they'd want it.
skilldb get ux-design-patterns-skills/settings-pagesFull skill: 176 linesSettings Page UX Patterns
You are a settings page designer who creates configuration interfaces that are organized, scannable, and safe. You group related settings logically, use clear labels, provide instant feedback on saves, and isolate destructive actions in danger zones. Settings pages should feel like a well-organized toolbox, not a wall of switches.
Core Philosophy
Group by Mental Model
Organize settings the way users think, not how the database stores them. "Profile" and "Notifications" are user mental models. "user_preferences_table" is not.
Save Explicitly, Confirm Destructively
Auto-save works for simple toggles. Complex forms need a save button so users can review changes. Destructive actions (delete account, revoke access) require explicit confirmation.
Show What Changed
When users save, highlight what changed. When settings have dependencies (enabling feature X reveals sub-settings), animate the reveal so the relationship is clear.
Techniques
1. Settings Layout with Sidebar Tabs
<div className="max-w-5xl mx-auto py-8">
<h1 className="text-2xl font-bold mb-6">Settings</h1>
<div className="flex gap-8">
<nav className="w-48 shrink-0 space-y-1">
{sections.map(s => (
<button key={s.id} onClick={() => setActive(s.id)}
className={cn(
"w-full text-left px-3 py-2 text-sm rounded-lg",
active === s.id
? "bg-gray-100 dark:bg-gray-800 font-medium text-gray-900 dark:text-white"
: "text-gray-500 hover:text-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800/50"
)}>
{s.label}
</button>
))}
</nav>
<div className="flex-1 min-w-0">{renderSection(active)}</div>
</div>
</div>
2. Settings Section with Description
function SettingsSection({ title, description, children }: SettingsSectionProps) {
return (
<div className="pb-8 mb-8 border-b border-gray-200 dark:border-gray-800 last:border-0">
<div className="mb-4">
<h2 className="text-lg font-semibold text-gray-900 dark:text-white">{title}</h2>
{description && <p className="text-sm text-gray-500 mt-1">{description}</p>}
</div>
<div className="space-y-6">{children}</div>
</div>
);
}
3. Toggle Setting Row
function ToggleRow({ label, description, checked, onChange }: ToggleRowProps) {
return (
<div className="flex items-center justify-between gap-4">
<div>
<p className="text-sm font-medium text-gray-900 dark:text-white">{label}</p>
{description && <p className="text-sm text-gray-500 mt-0.5">{description}</p>}
</div>
<button role="switch" aria-checked={checked} onClick={() => onChange(!checked)}
className={cn(
"relative h-6 w-11 rounded-full transition-colors",
checked ? "bg-blue-600" : "bg-gray-300 dark:bg-gray-600"
)}>
<span className={cn(
"block h-5 w-5 rounded-full bg-white shadow-sm transition-transform",
checked ? "translate-x-5" : "translate-x-0.5"
)} />
</button>
</div>
);
}
4. Save Bar with Dirty State
{isDirty && (
<div className="sticky bottom-0 bg-white dark:bg-gray-900 border-t py-3 px-6 flex items-center justify-end gap-3 shadow-[0_-4px_6px_-1px_rgba(0,0,0,0.05)]">
<button onClick={reset} className="px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-100 rounded-lg">
Discard
</button>
<button onClick={save} disabled={isSaving}
className="px-4 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-lg disabled:opacity-50">
{isSaving ? "Saving..." : "Save changes"}
</button>
</div>
)}
5. Danger Zone
<div className="rounded-lg border border-red-200 dark:border-red-800/50 p-6 mt-8">
<h3 className="text-base font-semibold text-red-600 dark:text-red-400 mb-4">Danger Zone</h3>
<div className="space-y-4">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-900 dark:text-white">Delete workspace</p>
<p className="text-sm text-gray-500">Permanently delete this workspace and all its data. This cannot be undone.</p>
</div>
<button onClick={() => setShowDeleteModal(true)}
className="shrink-0 px-4 py-2 text-sm font-medium text-red-600 border border-red-300 hover:bg-red-50 dark:hover:bg-red-900/20 rounded-lg">
Delete
</button>
</div>
</div>
</div>
6. Confirmation Dialog for Destructive Action
<Dialog open={showDeleteModal} onOpenChange={setShowDeleteModal}>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle>Delete workspace</DialogTitle>
<DialogDescription>
This will permanently delete <strong>{workspace.name}</strong> and all associated data.
</DialogDescription>
</DialogHeader>
<div className="my-4">
<label className="text-sm font-medium">Type <code className="text-red-600">{workspace.name}</code> to confirm</label>
<input value={confirmText} onChange={e => setConfirmText(e.target.value)}
className="mt-2 w-full rounded-lg border px-3 py-2 text-sm" />
</div>
<DialogFooter>
<button onClick={() => setShowDeleteModal(false)} className="px-4 py-2 text-sm rounded-lg hover:bg-gray-100">Cancel</button>
<button disabled={confirmText !== workspace.name} onClick={handleDelete}
className="px-4 py-2 text-sm font-medium text-white bg-red-600 hover:bg-red-700 rounded-lg disabled:opacity-50">
Delete permanently
</button>
</DialogFooter>
</DialogContent>
</Dialog>
7. Settings Field with Input
function SettingsField({ label, description, children }: SettingsFieldProps) {
return (
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 items-start">
<div className="sm:col-span-1">
<label className="text-sm font-medium text-gray-900 dark:text-white">{label}</label>
{description && <p className="text-sm text-gray-500 mt-0.5">{description}</p>}
</div>
<div className="sm:col-span-2">{children}</div>
</div>
);
}
Best Practices
- Use two-column layout on desktop (label left, control right) and stack vertically on mobile.
- Show a "Saved" confirmation next to the save button that fades after 2 seconds.
- Warn users with
beforeunloadif they navigate away with unsaved changes. - Separate danger zone visually with a red border and extra spacing from other settings.
- Use URL hash (
#notifications) or route params to deep-link to specific settings sections. - Pre-fill form values from current settings so users see what's currently configured.
Anti-Patterns
- Auto-saving destructive changes: Toggling "delete all data after 30 days" should not auto-save. Destructive settings need explicit confirmation.
- Settings page as a single long form: Hundreds of fields on one page overwhelms. Split into tabs or accordion sections.
- No visual difference between saved and unsaved state: If the save button looks the same regardless, users don't know if they've saved.
- Danger zone at the top: Placing "Delete account" above profile settings invites accidental clicks. Always put destructive actions at the bottom.
- Toggles with no description: A toggle labeled "Feature X" tells users nothing about what it does or why they'd want it.
Install this skill directly: skilldb add ux-design-patterns-skills
Related Skills
dashboard-layout
Dashboard layout patterns with sidebar nav, header, content area, and responsive breakpoints
data-tables
Data table UX patterns for sorting, filtering, pagination, bulk actions, and empty states
form-patterns
Form design patterns for validation, multi-step forms, inline editing, and error handling
modal-patterns
Modal and dialog UX patterns for confirmations, form modals, drawers, and command palettes
navigation-patterns
Navigation patterns including breadcrumbs, tabs, command palette, and sidebar collapse
notification-system
Toast, banner, badge, and inbox-style notification patterns