Skip to content

FRD: Search and Filter Page Recipe

Document Summary

FieldDetails
Feature NameSearch / Filter Page Recipe
StatusDraft
OwnerDavid Holmes
ContributorsEngineering, Design
Target Releasev2.0.0 (P2)
Related LinksRoadmap item #77, ADR-027 (Default Tech Stack)
Last Updated2026-05-26

Introduction

Overview

The Search / Filter Page recipe is an MDX documentation page with copy-pasteable code examples demonstrating how to compose existing design system components into a complete search-and-filter experience. It combines global-search-bar.tsx, filter-bar.tsx, badge components, empty states, and result layout variants into a cohesive pattern. The recipe shows URL-backed filter state, saved views, and responsive behavior.

Goals

  • Provide a complete, copy-pasteable recipe for building a search/filter page using existing components.
  • Demonstrate URL-backed filter state using useUrlState helpers.
  • Show multiple result layout variants (list, grid, table).
  • Include empty state, loading state, and error state patterns.
  • Document saved views (persisted filter configurations).

Non-Goals

  • Building new primitive components (the recipe uses existing ones).
  • Implementing server-side search or filtering logic.
  • Providing a generic “search page” component that handles all use cases.
  • Building a saved-views backend or persistence layer.

Scope

In Scope

AreaDescription
MDX recipe pageStorybook documentation page with code examples
Search input integrationUsing global-search-bar.tsx with debounced input
Filter bar integrationUsing filter-bar.tsx with typed filter definitions
Badge displayActive filter badges with remove action
URL-backed stateFilters, search term, sort, and pagination in URL params
Saved viewsPattern for saving and restoring filter configurations
Result layoutsList, grid, and table variants with a layout toggle
Empty states”No results” with clear-filters action; “No data” initial state
Loading stateSkeleton-matched loading for each layout variant
Error stateError display with retry action
Responsive behaviorFilter collapse on mobile; layout adaptation
StoriesStorybook stories demonstrating the composed recipe

Out of Scope

AreaReason
New primitive componentsRecipe uses existing components
Server-side search APIConsumer responsibility
Full-text search implementationConsumer responsibility
Saved-views persistence backendConsumer responsibility
Pagination component (new)Uses existing pagination if available; otherwise shows pattern

Users and Pain Points

User Groups

UserDescriptionNeeds
Application developersEngineers building search/filter pagesA reference implementation showing how to compose existing components
DesignersTeam members reviewing search/filter UXA documented pattern they can reference for consistency
New team membersEngineers onboarding to the design systemA working example showing best practices

Pain Points

UserPain PointImpact
Application developersNo reference for combining search bar, filters, and results with URL stateEach developer invents their own composition; inconsistent UX
Application developersUnclear how to handle empty, loading, and error states in a search contextStates are missing or implemented inconsistently
Application developersFilter state is often kept in component state, not the URLUsers cannot share or bookmark filtered views

Definitions

TermDefinition
RecipeAn MDX documentation page with complete, copy-pasteable code examples showing how to compose components
URL-backed stateApplication state stored in URL search parameters so it survives page reloads and is shareable
Saved viewA named, persisted filter configuration that users can recall
Filter badgeA pill-shaped UI element showing an active filter with a remove action
Result layoutThe visual arrangement of search results: list (vertical), grid (cards), or table (rows)

Current State

Existing Behavior

global-search-bar.tsx provides a search input with keyboard shortcut support. filter-bar.tsx provides a horizontal filter bar with typed filter definitions. Both exist as standalone components without a documented composition pattern.

Current Limitations

  • No recipe showing how to combine search bar and filter bar.
  • No documented pattern for URL-backed filter state.
  • No example of empty, loading, and error states in a search context.
  • No saved-views pattern.
  • No guidance on result layout variants.

Existing Workarounds

  • Developers study other applications’ source code or build from scratch.
  • Search/filter pages across products have inconsistent behavior and appearance.

Proposed Solution

Summary

Create an MDX recipe page in Storybook documenting a complete search/filter page pattern. Include code examples for composing global-search-bar, filter-bar, badges, result layouts, and state management. Ship companion stories demonstrating the composed recipe.

Key Capabilities

  • Complete code example showing GlobalSearchBar + FilterBar + results composition.
  • URL-backed state management using useUrlState for search term, filters, sort, and pagination.
  • Filter badges rendered from active filter state with individual removal.
  • Three result layout variants (list, grid, table) with a toggle control.
  • Empty state gated on isSuccess && data.length === 0 with a clear-filters action.
  • Loading state using shape-matched skeletons per layout variant.
  • Error state with retry action.
  • Saved views: save current filters to a named view, select from saved views dropdown.

User Experience

Developers read the recipe in Storybook, copy code examples into their application, and customize the data fetching and filter definitions for their domain. The recipe shows all states and interactions so developers know what to implement.

Developer Experience

The recipe is self-contained MDX with inline code blocks. Developers can copy individual sections (just the filter bar, just the URL state, just the saved views) or the entire composition. Stories provide a visual reference for each state and layout.


Requirements

IDRequirementPriorityNotes
FR-001The recipe must show integration of GlobalSearchBar and FilterBarMustCore composition
FR-002The recipe must demonstrate URL-backed filter stateMustPer P3 design principles
FR-003The recipe must show filter badges with removalMustActive filter visibility
FR-004The recipe must include list, grid, and table result layoutsMustCommon variants
FR-005The recipe must show empty, loading, and error statesMustPer P3 design principles
FR-006The recipe should show a saved views patternShouldAdvanced feature
FR-007The recipe must be accompanied by Storybook storiesMustVisual reference

Priority Definitions

PriorityMeaning
MustRequired for this feature to ship.
ShouldImportant, but can be deferred if needed.
CouldNice to have. Not required for initial release.

Functional Requirements

IDRequirementUser BenefitPriority
FUNC-001Search input debounces queries by 300ms before updating URL statePrevents excessive re-renders and API callsMust
FUNC-002Filters update URL search parameters on changeShareable, bookmarkable filter stateMust
FUNC-003Active filters render as badges below the filter barUsers see what is active and can remove individual filtersMust
FUNC-004”Clear all filters” action resets all filters and search termQuick reset pathMust
FUNC-005Layout toggle switches between list, grid, and table viewsUsers choose their preferred layoutMust
FUNC-006Empty state shows when isSuccess && data.length === 0Correct state gating per P3 principlesMust
FUNC-007Loading state uses shape-matched skeletons for each layout variantVisual consistency during loadingMust
FUNC-008Error state shows an error message with a retry buttonRecoverable error experienceMust
FUNC-009Saved views: user can name and save current filter stateRecall frequently used filter setsShould
FUNC-010Saved views: user can select a saved view to apply its filtersQuick navigation to common filter setsShould
FUNC-011On mobile, filter bar collapses into a “Filters” button that opens a sheet or drawerResponsive designMust

Non-Functional Requirements

IDRequirementCategoryPriority
NFR-001Recipe code examples must be copy-pasteable without modification beyond data fetchingMaintainabilityMust
NFR-002Recipe must use only existing design system components (no new primitives)MaintainabilityMust
NFR-003All recipe examples must pass TypeScript strict modeCorrectnessMust
NFR-004Storybook stories must render without errors in both light and dark themesCompatibilityMust
NFR-005Filter state changes must update the URL without a full page reloadPerformanceMust

API / Interface Requirements

Public API

This is a recipe (documentation), not a shipped component. No new public API is exported. The recipe demonstrates composition of existing APIs.

NameTypeDescriptionRequired
Recipe MDX pagedocumentationStorybook MDX page with code examplesYes
Recipe storiesstoriesStorybook stories showing the composed patternYes

Example Usage

// From the recipe: composing search + filter + results
function SearchPage() {
const [search, setSearch] = useUrlState("q", "");
const [filters, setFilters] = useUrlState("filters", {});
const [layout, setLayout] = useUrlState("layout", "list");
const { data, isLoading, isSuccess, isError, refetch } = useQuery({
queryKey: ["search", search, filters],
queryFn: () => fetchResults(search, filters),
});
return (
<div>
<GlobalSearchBar value={search} onChange={setSearch} />
<FilterBar filters={filterDefinitions} value={filters} onChange={setFilters} />
<ActiveFilterBadges filters={filters} onRemove={(key) => removeFilter(key)} />
<LayoutToggle value={layout} onChange={setLayout} />
{isLoading && <ResultsSkeleton layout={layout} />}
{isError && <ErrorState onRetry={refetch} />}
{isSuccess && data.length === 0 && <EmptyState onClearFilters={clearAll} />}
{isSuccess && data.length > 0 && <ResultsList data={data} layout={layout} />}
</div>
);
}

API Notes

  • The recipe uses useUrlState for URL-backed state; document the expected helper shape.
  • GlobalSearchBar and FilterBar are imported from the design system.
  • Result layouts and skeletons are shown as inline code for consumers to customize.

Accessibility Requirements

IDRequirementNotes
A11Y-001Search input must have an accessible labelaria-label or associated &lt;label&gt;
A11Y-002Filter badges must be keyboard-removable (Enter/Space on the remove button)Standard button behavior
A11Y-003Layout toggle must use role="radiogroup" with role="radio" buttonsCorrect ARIA for toggle group
A11Y-004Empty state must be announced to screen readers when it appearsrole="status" or aria-live="polite"
A11Y-005Loading state must have aria-busy="true" on the results containerScreen readers know content is loading
A11Y-006Error state must be announced to screen readersrole="alert"

Checklist

  • Keyboard support is defined.
  • Focus behavior is defined.
  • Screen reader behavior is defined.
  • Color contrast requirements are met.
  • Reduced motion behavior is considered.
  • Semantic HTML expectations are documented.
  • ARIA usage is defined only where needed.

Content and Documentation Requirements

IDRequirementLocationPriority
DOC-001Complete recipe MDX page with all code examplesStorybook MDXMust
DOC-002”When to use this recipe” and “When not to use” guidanceStorybook MDXMust
DOC-003Section on URL-backed state managementStorybook MDXMust
DOC-004Section on state management (empty, loading, error)Storybook MDXMust
DOC-005Section on saved views patternStorybook MDXShould
DOC-006Responsive behavior notesStorybook MDXMust

Documentation Should Include

  • Overview of the pattern
  • When to use / when not to use
  • Prerequisites (which components to install)
  • Complete composition example
  • URL state management
  • Filter badges
  • Result layout variants
  • Empty, loading, error states
  • Saved views
  • Responsive behavior
  • Accessibility notes
  • Common mistakes

Dependencies

DependencyTypeOwnerStatusNotes
global-search-bar.tsxDesign SystemDavid HolmesReadyExisting component
filter-bar.tsxDesign SystemDavid HolmesReadyExisting component
Badge componentDesign SystemDavid HolmesReadyFor filter badges
Skeleton componentsDesign SystemDavid HolmesReadyFor loading states
TanStack QueryEngineeringDavid HolmesReadyData fetching in examples

Risks and Tradeoffs

Risk / TradeoffImpactMitigation
Recipe may not cover all search/filter use casesDevelopers may need to adapt significantly for complex filtersDocument the recipe as a starting point; link to component docs for advanced usage
URL-backed state adds complexity compared to local stateMore code to set up initiallyProvide a useUrlState helper or reference; explain the benefits
Saved views pattern requires consumer persistenceRecipe shows the UI pattern but not the backendClearly document that persistence is the consumer’s responsibility
Recipe code may drift from component updatesCode examples may become outdatedStories validate that the composition works; update recipe when components change

Open Questions

IDQuestionOwnerStatusResolution
Q-001Should the recipe include a sort control, or is that a separate pattern?David HolmesOpen
Q-002Should the recipe show pagination, infinite scroll, or both?David HolmesOpen
Q-003Should we ship a SearchFilterPage compound component in addition to the recipe?David HolmesOpen
Q-004Should the saved views UI use a dropdown, a sidebar section, or both?David HolmesOpen

Acceptance Criteria

IDCriteriaRelated Requirement
AC-001Recipe MDX page renders in Storybook without errorsDOC-001
AC-002Recipe shows GlobalSearchBar and FilterBar composed togetherFR-001
AC-003Recipe demonstrates URL-backed filter state with useUrlStateFR-002
AC-004Recipe shows active filter badges with remove actionFR-003
AC-005Recipe includes list, grid, and table result layout examplesFR-004
AC-006Recipe shows empty state gated on isSuccess && data.length === 0FUNC-006
AC-007Recipe shows shape-matched loading skeletons for each layoutFUNC-007
AC-008Recipe shows error state with retry actionFUNC-008
AC-009Companion stories demonstrate each layout and stateFR-007
AC-010All recipe code passes TypeScript strict modeNFR-003

LLM Handoff Instructions

Expected LLM Behavior

  • Follow the requirements and acceptance criteria in this document.
  • Do not expand scope beyond the In Scope section.
  • Respect the Out of Scope section.
  • Use existing design system components; do not create new primitives.
  • Gate empty state on isSuccess && data.length === 0 per P3 design principles.
  • Use shape-matched skeletons for loading states per P3 design principles.
  • Use useUrlState helpers for URL-backed state, not hand-rolled split/join per P3 design principles.
  • Stories should cover all three layouts and all three states (loading, empty, error).

LLM Should Not

  • Invent undocumented product behavior.
  • Create new primitive components.
  • Implement server-side search or filtering.
  • Add new dependencies beyond what the design system already uses.
  • Use inline styles instead of design system tokens.
  • Gate empty state incorrectly (e.g., data.length === 0 without checking isSuccess).

Decision Log

DateDecisionReasonOwner
2026-05-26Recipe (MDX + stories), not a shipped compound componentRecipes give developers more flexibility and understanding than opaque componentsDavid Holmes
2026-05-26URL-backed state is mandatory in the recipePer P3 design principles: filters, tabs, sort use URL stateDavid Holmes
2026-05-26Three layout variants (list, grid, table)Covers the most common search result presentationsDavid Holmes

Document History

DateAuthorChange
2026-05-26David HolmesInitial draft