frontend-design-patterns
Guide for designing and decomposing UI screens into components with proper data flow and API integration following project conventions. Use when designing new features, breaking down complex UIs, planning component structure, or connecting components to APIs. All examples follow @project-conventions with form/logic separation, atomic design, and API cores/hooks structure.
SKILL.md
| Name | frontend-design-patterns |
| Description | Guide for designing and decomposing UI screens into components with proper data flow and API integration following project conventions. Use when designing new features, breaking down complex UIs, planning component structure, or connecting components to APIs. All examples follow @project-conventions with form/logic separation, atomic design, and API cores/hooks structure. |
name: frontend-design-patterns description: Guide for designing and decomposing UI screens into components with proper data flow and API integration following project conventions. Use when designing new features, breaking down complex UIs, planning component structure, or connecting components to APIs. All examples follow @project-conventions with form/logic separation, atomic design, and API cores/hooks structure. keywords: screen-design, list-screen, form-screen, filter, table, data-flow, dashboard, wizard, component-decomposition, tabbed-form, detail-screen, url-sync
Frontend Design Patterns
Overview
This skill provides systematic approaches for analyzing UI designs and breaking them down into well-structured, maintainable React components with proper data flow and API integration.
When to Use This Skill
Use this skill when:
- Designing a new feature/screen from scratch
- Breaking down a complex UI design into components
- Planning component structure and data flow
- Deciding how to handle filters, tables, forms
- Figuring out state management strategy
- Connecting components to APIs
Screen Type Patterns
Different screens need different approaches:
1. List/Table Screen
For: Managing collections (users, orders, products)
- Filters + Table + Pagination
- Server-side sorting/filtering
2. Form Screen
For: Creating or editing records
- Single form with validation
- Load data for edit mode
3. Multi-Step Form (Wizard)
For: Complex forms split into steps
- Progress indicator
- Step-by-step validation
4. Tabbed Form
For: Settings, profiles with multiple sections
- Independent forms per tab
- Tab state in URL
5. Detail/View Screen
For: Viewing record details
- Read-only display
- Organized into sections
6. Dashboard
For: Overview/metrics/analytics
- Metric cards + Charts + Tables
- Date range filtering
➡️ See Screen Patterns for complete examples of each type
Design Analysis Framework
Step 1: Identify Screen Type
Choose the pattern that fits your use case:
- Managing a list? → List/Table Screen
- Creating/editing? → Form Screen
- Complex multi-step entry? → Multi-Step Form
- Multiple independent sections? → Tabbed Form
- Just viewing info? → Detail Screen
- Overview with metrics? → Dashboard
Step 2: Break Down Components
For each major section, determine:
-
What data does it need?
- Props from parent?
- API data?
- Local state?
-
What actions can users perform?
- Button clicks
- Form submissions
- Filters/sorting
-
How does it communicate?
- Callbacks to parent
- URL parameters
- Shared context
Filter Component Design
Key Questions
When designing filters, determine:
-
Input Types
- Text search →
<input type="text"> - Status/Category → Static
<select>or API-driven - Date → Single date, date range, datetime, datetime range
- Number → Single input or range (min/max)
- Multi-select → Checkboxes, multi-select dropdown
- Text search →
-
Data Source
- Static options → Define in component
- API-driven → Fetch with React Query
-
Layout
- Grid layout (responsive columns)
- Flex layout (inline)
- Collapsible advanced filters
-
Default Values
- Empty (all filters cleared)
- Preset (default to specific values)
- From URL (read from search params)
-
Validation
- Required fields?
- Range validation (min < max)?
- Format validation?
-
Actions
- Search button
- Reset/Clear button
- Export button
Quick Decision Guide
// Text input - for search/free text
<input type="text" value={filters.search} onChange={...} />
// Static select - for predefined options (status, type, etc.)
<select value={filters.status}>
<option value="">All</option>
<option value="active">Active</option>
<option value="inactive">Inactive</option>
</select>
// API-driven select - for dynamic options (users, departments, etc.)
const { data: departments } = useQuery({ queryKey: ['departments'], queryFn: fetchDepartments })
<select value={filters.department}>
{departments?.map(d => <option key={d.id} value={d.id}>{d.name}</option>)}
</select>
// Date range - for filtering by date
<DateRangePicker value={filters.dateRange} onChange={...} />
// Number range - for price, quantity, etc.
<input type="number" placeholder="Min" value={filters.min} />
<input type="number" placeholder="Max" value={filters.max} />
➡️ See Filter Patterns for complete examples
Table Component Design
Key Questions
When designing tables, determine:
-
Columns
- What columns to display?
- Which are sortable?
- Any special formatting (badges, avatars)?
-
Features
- Sorting (client or server-side)
- Pagination (client or server-side)
- Row selection (checkboxes)
- Row actions (edit, delete, view)
-
Performance
- Virtual scrolling needed?
- How many rows typically?
Quick Decision Guide
// Simple column
{ accessorKey: 'name', header: 'Name' }
// Badge column
{
accessorKey: 'status',
cell: ({ getValue }) => <Badge>{getValue()}</Badge>
}
// Actions column
{
id: 'actions',
cell: ({ row }) => (
<div className="flex gap-2">
<button onClick={() => handleEdit(row.original)}>Edit</button>
<button onClick={() => handleDelete(row.original.id)}>Delete</button>
</div>
)
}
// Server-side sorting & pagination
const { data } = useQuery({
queryKey: ['users', filters, pagination, sorting],
queryFn: () => fetchUsers({ ...filters, ...pagination, ...sorting })
})
➡️ See Table Patterns for complete examples
Data Flow Strategies
Choose the right pattern for your use case:
Pattern 1: Local State + Props
Best for: Simple parent-child communication
function Page() {
const [filters, setFilters] = useState({})
return (
<>
<Filters filters={filters} onChange={setFilters} />
<Table filters={filters} />
</>
)
}
Pattern 2: URL as Source of Truth
Best for: Shareable URLs, browser back/forward support
const { search } = useSearch()
const filters = { search: search.q, status: search.status }
// Update URL when filters change
navigate({ search: newFilters })
Pattern 3: React Query
Best for: Server-driven data with caching
const { data } = useQuery({
queryKey: ['users', filters],
queryFn: () => fetchUsers(filters)
})
Pattern 4: Context
Best for: Deep component trees, shared state
const FiltersContext = createContext()
// Provider in parent, useContext in children
Pattern 5: Context + Reducer
Best for: Complex state with multiple actions
const [state, dispatch] = useReducer(reducer, initialState)
<Context.Provider value={{ state, dispatch }}>
➡️ See Data Flow for detailed patterns
URL Sync Pattern
For shareable state (filters, pagination), sync with URL:
// Read from URL
const { search } = useSearch()
const filters = {
search: search.q,
status: search.status,
}
// Write to URL
const updateFilters = (newFilters) => {
navigate({
search: {
q: newFilters.search || undefined,
status: newFilters.status || undefined,
}
})
}
Complete Examples
➡️ See Real Examples for:
- User Management Screen (complete implementation)
- Product List with Advanced Filters
- Order Dashboard with Metrics
Quick Reference
Screen Types
| Use Case | Pattern | Reference |
|---|---|---|
| Manage collection | List/Table Screen | Screen Patterns |
| Create/edit record | Form Screen | Screen Patterns |
| Complex data entry | Multi-step Form | Screen Patterns |
| Multiple sections | Tabbed Form | Screen Patterns |
| View details | Detail Screen | Screen Patterns |
| Overview/metrics | Dashboard | Screen Patterns |
Components
| Need | Solution | Reference |
|---|---|---|
| Text search input | <input type="text"> | Filter Patterns |
| Static dropdown | <select> with options array | Filter Patterns |
| API-driven dropdown | useQuery + <select> | Filter Patterns |
| Date picker | <DatePicker> or <DateRangePicker> | Filter Patterns |
| Table with sorting | TanStack Table + server-side sorting | Table Patterns |
| Table with pagination | TanStack Table + server-side pagination | Table Patterns |
| Row selection | enableRowSelection + checkbox column | Table Patterns |
| Shareable filters | URL sync pattern | Data Flow |
| Complex state | Context + Reducer | Data Flow |
Best Practices
-
Follow project conventions - IMPORTANT: Always follow @project-conventions
- Separate form display from submit logic (form in child, submit in parent)
- API structure: cores/ for functions, hooks/ for React Query
- File naming: kebab-case (user-form.tsx, create-user.ts)
- Import components from atoms/molecules folders
- Feature components in containers/{feature}/components/
-
Break down by functionality first - Identify major sections before coding
-
Identify data sources early - Know what's static vs API-driven
-
Plan state management - Choose the right pattern for your needs
-
Design component APIs - Think about props/callbacks before implementing
-
Consider URL sync - For filters, pagination, sorting
-
Handle loading/error states - Always show loading and error UI
-
Keep components focused - Single responsibility principle
-
Optimize re-renders - Use React.memo, useMemo, useCallback when needed
Common Patterns Summary
Basic Filter + Table Page
function Page() {
const [filters, setFilters] = useState({})
const { data } = useQuery({
queryKey: ['items', filters],
queryFn: () => fetchItems(filters)
})
return (
<>
<Filters filters={filters} onChange={setFilters} />
<Table data={data} />
</>
)
}
With URL Sync
function Page() {
const navigate = useNavigate()
const { search } = useSearch()
const filters = { ...search }
const updateFilters = (newFilters) => {
navigate({ search: newFilters })
}
// Query automatically refetches when URL changes
const { data } = useQuery({
queryKey: ['items', filters],
queryFn: () => fetchItems(filters)
})
return (
<>
<Filters filters={filters} onChange={updateFilters} />
<Table data={data} />
</>
)
}
Resources
Core Guides
- Architecture Patterns - ⭐ Form/Logic separation, API structure, file organization
- Screen Patterns - 6 common screen types overview
- Filter Patterns - All input types and layouts
- Table Patterns - Sorting, pagination, selection
- Data Flow - State management strategies
Complete Examples (Following Project Conventions)
- Form Screen - ⭐ Create/edit with validation (shows full structure)
- List/Table Screen - User management with filters
- Multi-Step Form - Wizard with progress
- Tabbed Form - Settings with tabs
- Detail Screen - Read-only view
- Dashboard - Metrics and charts