| Field | Value |
|---|
| ID | FRD-043 |
| Owner | David Holmes |
| Status | Draft |
| Priority | P1 — Recipes |
| Size | M (Recipe) |
| Target Release | v2.0.0 |
| Last Updated | 2026-05-26 |
| Related | ADR-027 (Default Tech Stack) |
Document Summary
Create an MDX recipe in Storybook that teaches developers how to compose a Settings page using design system components. The recipe covers tabbed/sectioned layouts, form state management with dirty detection, save/cancel button states, field-level and form-level validation, and toast feedback on save. It provides copy-pasteable code for a realistic “Account Settings” page with profile, notification, and security sections.
Introduction
Overview
Settings pages are the second most common page pattern in B2B SaaS after resource list pages. They combine multiple form sections (often in tabs), persistent form state, dirty tracking, validation, and save/cancel workflows. Without a canonical recipe, developers reinvent this pattern with inconsistent approaches to dirty detection, button states, and error handling.
Goals
- Provide a single MDX recipe showing the complete Settings page composition pattern.
- Demonstrate tabbed section organization using the Tabs component.
- Show dirty detection that enables/disables the Save button and warns on unsaved navigation.
- Show field-level validation (inline errors) and form-level validation (toast on submit failure).
- Include save/cancel states: disabled save when clean, loading spinner during save, success/error toast.
- Use realistic sections: Profile, Notifications, Security.
Non-Goals
- Building new form components or validation libraries.
- Server-side settings persistence or API design.
- Implementing a settings framework or state management library.
- Nested settings pages (e.g., team settings with sub-pages).
Scope
In Scope
| Item | Description |
|---|
| MDX recipe page | src/docs/recipes/settings-page.mdx with Storybook sidebar entry |
| Tab layout | Tabs component organizing Profile, Notifications, Security sections |
| Form composition | TextField, Select, Switch, Checkbox for various setting types |
| Dirty detection | Pattern for tracking whether any field has changed from its initial value |
| Save/Cancel states | Disabled save when clean, loading during save, cancel resets to initial values |
| Validation | Field-level inline errors, form-level validation summary |
| Toast feedback | Success toast on save, error toast on failure |
| Overview page update | Add Settings recipe entry to src/docs/recipes/00-overview.mdx |
Out of Scope
| Item | Reason |
|---|
| Settings data fetching | Backend-specific; recipe covers the form composition |
| Role-based settings visibility | Authorization logic varies by application |
| Multi-tenant settings | Complex pattern that warrants a separate recipe |
| File upload in settings (avatar) | File upload patterns are a separate recipe |
Users and Pain Points
| User | Pain Point |
|---|
| Application developer | No reference for how to wire dirty detection with save/cancel button states |
| Application developer | Unclear whether to use field-level or form-level validation (answer: both) |
| Application developer | Tab navigation with unsaved changes causes silent data loss |
| Application developer | Save button is always enabled even when no changes are made — wasted API calls |
| New team member | No canonical “this is how we build a settings page” guide |
Definitions
| Term | Definition |
|---|
| Dirty detection | Comparing current form values against initial values to determine if any changes have been made |
| Field-level validation | Validation errors shown inline next to the field that failed |
| Form-level validation | Validation that applies to the form as a whole (e.g., “At least one notification channel must be enabled”) |
| Unsaved changes guard | A warning dialog that appears when the user tries to navigate away with unsaved changes |
Current State
- The
src/docs/recipes/00-overview.mdx page lists “Settings Page” as a planned recipe.
- All required components exist:
tabs.tsx, text-field.tsx, select.tsx, checkbox.tsx, toast.tsx, button.tsx, dialog.tsx.
- Switch/toggle functionality exists in the component library.
- No recipe MDX file exists yet.
- No code examples exist for the settings page composition pattern.
Proposed Solution
Create src/docs/recipes/settings-page.mdx with the following structure:
- Introduction — What this recipe builds, when to use it, when not to use it.
- Component inventory — Table listing every component used with links.
- Tab layout — Tabs configuration for section organization.
- Profile section — TextField for name/email, Select for timezone, avatar placeholder.
- Notifications section — Switch toggles for email/SMS/push, frequency Select.
- Security section — Password change fields, two-factor authentication toggle.
- Dirty detection — Custom hook pattern comparing current vs. initial form state.
- Save/Cancel workflow — Button states (disabled, loading, enabled), cancel reset, success/error toast.
- Validation — Field-level inline errors and form-level toast.
- Unsaved changes guard — Dialog warning on tab switch or page navigation with dirty state.
- Full composition — Complete code block.
Requirements
| ID | Requirement | Priority |
|---|
| REQ-01 | Recipe is a single MDX file in src/docs/recipes/ | Must |
| REQ-02 | Recipe uses Tabs for section organization | Must |
| REQ-03 | Recipe demonstrates dirty detection with save/cancel button states | Must |
| REQ-04 | Recipe shows field-level validation with inline errors | Must |
| REQ-05 | Recipe shows save success and error feedback via Toast | Must |
| REQ-06 | All code blocks are copy-pasteable and self-contained | Must |
| REQ-07 | Recipe shows unsaved changes guard on navigation | Should |
| REQ-08 | Recipe uses at least 3 different form control types (TextField, Select, Switch) | Must |
Functional Requirements
| ID | Description | Acceptance |
|---|
| FR-01 | MDX file renders in Storybook without errors | pnpm build-storybook succeeds |
| FR-02 | All code blocks compile when extracted | Manual verification |
| FR-03 | Recipe overview page lists Settings recipe as “Available” | Status updated |
| FR-04 | Save button is disabled when form is clean | Shown in code example |
| FR-05 | Cancel button resets form to initial values | Shown in code example |
Non-Functional Requirements
| ID | Description | Target |
|---|
| NFR-01 | Recipe page load time | Under 2 seconds |
| NFR-02 | Individual code block line count | Under 60 lines per block |
| NFR-03 | Full composition line count | Under 200 lines |
API/Interface Requirements
| Interface | Requirement |
|---|
| MDX file | Must use “ |
| Code blocks | Must use fenced tsx code blocks with imports |
| Dirty detection hook | Must be a self-contained useDirtyForm or equivalent pattern |
Accessibility Requirements
| ID | Requirement |
|---|
| A11Y-01 | Tab sections must use proper role="tabpanel" (handled by Tabs component) |
| A11Y-02 | Form fields must have visible labels and associated error messages via aria-describedby |
| A11Y-03 | Save/Cancel buttons must have descriptive text (not just icons) |
| A11Y-04 | Unsaved changes dialog must trap focus and be dismissible with Escape |
Content and Documentation Requirements
| ID | Requirement |
|---|
| DOC-01 | Update src/docs/recipes/00-overview.mdx to mark Settings recipe as available |
| DOC-02 | Each section must have a 1-2 sentence explanation |
| DOC-03 | Include “When to use” and “When not to use” callouts |
Dependencies
| Dependency | Type | Risk |
|---|
| Tabs component | Internal | Must support controlled tab index for unsaved changes guard |
| TextField component | Internal | Must support error and helperText props for validation |
| Toast component | Internal | Must support success and error variants |
| Dialog component | Internal | Used for unsaved changes guard |
Risks and Tradeoffs
| Risk | Impact | Mitigation |
|---|
| Dirty detection pattern may not cover all form libraries | Limited applicability | Show a vanilla React pattern; note that form libraries (React Hook Form, TanStack Form) have built-in dirty tracking |
| Settings page structure varies widely across apps | Recipe may not fit all cases | Focus on the most common pattern (tabbed sections); note alternatives |
| Unsaved changes guard requires router integration | Framework-specific | Show the dialog logic; note that router integration varies |
Open Questions
| # | Question | Status |
|---|
| 1 | Should the recipe use TanStack Form for validation or plain React state? | Open |
| 2 | Should the unsaved changes guard use beforeunload for browser navigation? | Open |
| 3 | Should we show a “Settings saved” banner in addition to a toast? | Open |
Acceptance Criteria
LLM Handoff Instructions
When an LLM agent picks up this FRD:
- Read
src/docs/recipes/00-overview.mdx for recipe conventions.
- Read component source for:
tabs.tsx, text-field.tsx, select.tsx, checkbox.tsx, toast.tsx, button.tsx, dialog.tsx.
- Read each component’s stories to understand established usage patterns.
- Create
src/docs/recipes/settings-page.mdx with the section structure from the Proposed Solution.
- Sample data: “Account Settings” with Profile (name, email, timezone), Notifications (email/SMS/push toggles, digest frequency), Security (password change, 2FA toggle).
- Dirty detection: show a
useDirtyForm(initialValues, currentValues) hook pattern that returns { isDirty, changedFields, reset }.
- All code blocks must use public package import paths.
- After creating, run
pnpm build-storybook to verify.
- Update
src/docs/recipes/00-overview.mdx.
Decision Log
| Date | Decision | Rationale |
|---|
| 2026-05-26 | Use Tabs for section organization rather than accordion or vertical nav | Tabs are the most common settings page pattern and the design system Tabs component is well-tested |
| 2026-05-26 | Show vanilla React state for dirty detection | Framework-agnostic; form library users can adapt the pattern |
Document History
| Date | Version | Author | Changes |
|---|
| 2026-05-26 | 0.1 | David Holmes | Initial draft |