mdxui

Search

Global search results view component

Search

The Search view provides a unified interface for searching across all resources, collections, and content in the application. It supports filtering, result grouping, and intelligent suggestions.

Type Signature

interface SearchViewProps {
  /** Page title */
  title?: string
  /** Page description */
  description?: string
  /** View layout mode */
  layout?: 'single' | 'column' | 'canvas' | 'vibe'
  /** Search query */
  query?: string
  /** Search results */
  results?: Array<{
    id: string
    type: string
    title: string
    description?: string
    url: string
    metadata?: Record<string, any>
  }>
  /** Filter by type */
  filters?: Record<string, any>
  /** Total results count */
  total?: number
  /** Show suggestions */
  showSuggestions?: boolean
  /** Page-level action buttons */
  actions?: Array<{
    id: string
    label: string
    icon?: string
    onClick?: () => void
    variant?: 'default' | 'primary' | 'destructive'
  }>
  /** Loading state */
  loading?: boolean
  /** Error state */
  error?: string
  /** Child content */
  children?: React.ReactNode
}

Usage

<App name="Acme Dashboard">
  <Shell>
    <SearchView
      query="project alpha"
      results={searchResults}
      total={42}
      showSuggestions={true}
      filters={{
        type: ['projects', 'tasks'],
        status: 'active'
      }}
    />
  </Shell>
</App>

Props

title

The page title displayed in the header. Defaults to "Search Results".

<SearchView
  title="Search"
  query="project alpha"
>

description

Optional page description.

<SearchView
  title="Search"
  description="Search across projects, tasks, and documents"
>

layout

Layout mode for the search content. Defaults to 'single'.

// Single column list (default)
<SearchView layout="single">

// Multi-column layout with filters
<SearchView layout="column">

query

The search query string entered by the user.

<SearchView
  query="project alpha"
  results={results}
/>

results

Array of search result items.

<SearchView
  query="dashboard"
  results={[
    {
      id: 'page_1',
      type: 'page',
      title: 'Dashboard Overview',
      description: 'Main dashboard with metrics and charts',
      url: '/dashboard',
      metadata: {
        lastModified: '2024-01-15',
        author: 'Jane Doe'
      }
    },
    {
      id: 'task_2',
      type: 'task',
      title: 'Update Dashboard Metrics',
      description: 'Refresh the dashboard KPIs',
      url: '/tasks/123',
      metadata: {
        status: 'in-progress',
        assignee: 'John Smith'
      }
    },
    {
      id: 'doc_3',
      type: 'document',
      title: 'Dashboard Design Specs',
      description: 'Design specifications for the dashboard',
      url: '/docs/dashboard-specs',
      metadata: {
        category: 'design',
        tags: ['dashboard', 'ui']
      }
    }
  ]}
/>

filters

Active search filters by field.

<SearchView
  query="project"
  filters={{
    type: ['projects', 'tasks'],
    status: 'active',
    assignee: 'me'
  }}
  results={filteredResults}
/>

total

Total number of matching results (for pagination).

<SearchView
  query="project"
  results={results.slice(0, 20)}
  total={156}
>
  {/* Showing 1-20 of 156 results */}
</SearchView>

showSuggestions

Display search suggestions and autocomplete. Defaults to true.

<SearchView
  query="proj"
  showSuggestions={true}
>
  <SearchSuggestions
    suggestions={[
      'project alpha',
      'project beta',
      'project management'
    ]}
  />
</SearchView>

actions

Page-level action buttons.

<SearchView
  query="project"
  actions={[
    {
      id: 'save-search',
      label: 'Save Search',
      icon: 'Bookmark',
      onClick: () => saveSearch()
    },
    {
      id: 'export',
      label: 'Export Results',
      icon: 'Download',
      onClick: () => exportResults()
    }
  ]}
/>

Search Input

Provide search input component:

<SearchView query={query}>
  <SearchInput
    value={query}
    onChange={setQuery}
    onSearch={handleSearch}
    placeholder="Search projects, tasks, documents..."
    autoFocus={true}
  />
</SearchView>

Result Grouping

Group results by type or category:

<SearchView query="design">
  <SearchResults>
    <ResultGroup title="Projects" count={5}>
      {projectResults.map((result) => (
        <SearchResult key={result.id} {...result} />
      ))}
    </ResultGroup>

    <ResultGroup title="Tasks" count={12}>
      {taskResults.map((result) => (
        <SearchResult key={result.id} {...result} />
      ))}
    </ResultGroup>

    <ResultGroup title="Documents" count={8}>
      {docResults.map((result) => (
        <SearchResult key={result.id} {...result} />
      ))}
    </ResultGroup>
  </SearchResults>
</SearchView>

Filters

Provide filter controls:

<SearchView
  query={query}
  filters={filters}
  layout="column"
>
  <SearchFilters
    filters={[
      {
        id: 'type',
        label: 'Type',
        options: [
          { value: 'projects', label: 'Projects', count: 5 },
          { value: 'tasks', label: 'Tasks', count: 12 },
          { value: 'documents', label: 'Documents', count: 8 }
        ]
      },
      {
        id: 'status',
        label: 'Status',
        options: [
          { value: 'active', label: 'Active', count: 15 },
          { value: 'completed', label: 'Completed', count: 10 }
        ]
      }
    ]}
    onChange={setFilters}
  />

  <SearchResults results={filteredResults} />
</SearchView>

Search Suggestions

Show autocomplete and suggestions:

<SearchView
  query={query}
  showSuggestions={true}
>
  {query.length > 0 && (
    <SearchSuggestions
      suggestions={[
        { text: 'project alpha', type: 'recent' },
        { text: 'project beta', type: 'suggestion' },
        { text: 'projects', type: 'category' }
      ]}
      onSelect={handleSuggestionClick}
    />
  )}
</SearchView>

Recent Searches

Display recent search history:

<SearchView query="">
  <RecentSearches
    searches={[
      { query: 'project alpha', timestamp: '2 hours ago' },
      { query: 'dashboard', timestamp: '1 day ago' },
      { query: 'design specs', timestamp: '3 days ago' }
    ]}
    onSelect={setQuery}
    onClear={clearHistory}
  />
</SearchView>

Empty States

Handle no results gracefully:

<SearchView query={query} results={results}>
  {results.length === 0 ? (
    <EmptyState
      icon="Search"
      title="No results found"
      description={`No results for "${query}". Try different keywords.`}
      action={{
        label: 'Clear Search',
        onClick: () => setQuery('')
      }}
    />
  ) : (
    <SearchResults results={results} />
  )}
</SearchView>

Highlighting

Highlight matching terms in results:

<SearchResult
  title={highlightMatches(result.title, query)}
  description={highlightMatches(result.description, query)}
/>

Sorting

Allow sorting search results:

<SearchView
  query={query}
  results={sortedResults}
>
  <SearchSort
    value={sortBy}
    onChange={setSortBy}
    options={[
      { value: 'relevance', label: 'Relevance' },
      { value: 'date', label: 'Date Modified' },
      { value: 'title', label: 'Title' }
    ]}
  />
</SearchView>

Pagination

Handle large result sets:

<SearchView
  query={query}
  results={paginatedResults}
  total={totalResults}
>
  <SearchResults results={paginatedResults} />

  <Pagination
    current={currentPage}
    total={totalPages}
    onChange={setCurrentPage}
  />
</SearchView>

App Type Specializations

Search API docs, endpoints, and logs:

<SearchView
  query="authentication"
  results={[
    {
      type: 'endpoint',
      title: 'POST /auth/login',
      description: 'Authenticate user and return token',
      url: '/docs/api/auth#login'
    },
    {
      type: 'guide',
      title: 'Authentication Guide',
      description: 'How to authenticate API requests',
      url: '/docs/guides/auth'
    }
  ]}
/>

Search users, roles, and settings:

<SearchView
  query="admin"
  filters={{ type: ['users', 'roles'] }}
  results={[
    {
      type: 'user',
      title: 'Jane Admin',
      description: 'admin@acme.com',
      url: '/users/123'
    },
    {
      type: 'role',
      title: 'Administrator',
      description: 'Full system access',
      url: '/roles/admin'
    }
  ]}
/>

Search contacts, companies, and deals:

<SearchView
  query="acme"
  results={[
    {
      type: 'company',
      title: 'Acme Corporation',
      description: '$1.2M in pipeline',
      url: '/companies/acme'
    },
    {
      type: 'deal',
      title: 'Acme - Enterprise License',
      description: '$50K, Closing Q1',
      url: '/deals/123'
    }
  ]}
/>

Provide advanced search options:

<SearchView query={query}>
  <AdvancedSearch
    onSearch={handleAdvancedSearch}
    fields={[
      { name: 'title', label: 'Title', type: 'text' },
      { name: 'type', label: 'Type', type: 'select', options: [...] },
      { name: 'dateRange', label: 'Date Range', type: 'daterange' },
      { name: 'tags', label: 'Tags', type: 'multiselect' }
    ]}
  />
</SearchView>

Saved Searches

Allow saving and loading searches:

<SearchView
  query={query}
  filters={filters}
  actions={[
    {
      id: 'save',
      label: 'Save Search',
      onClick: () => saveSearch({ query, filters })
    }
  ]}
>
  <SavedSearches
    searches={savedSearches}
    onSelect={loadSearch}
    onDelete={deleteSearch}
  />
</SearchView>

Keyboard Shortcuts

Implement keyboard navigation:

useEffect(() => {
  const handleKeyPress = (e: KeyboardEvent) => {
    if (e.key === '/') {
      e.preventDefault()
      focusSearchInput()
    }
  }

  window.addEventListener('keypress', handleKeyPress)
  return () => window.removeEventListener('keypress', handleKeyPress)
}, [])

<SearchView
  query={query}
  aria-label="Press / to search"
>

Responsive Behavior

Search adapts to screen size:

Desktop (≥1024px)

┌─────────────────────────────────────┐
│  [Search____________]  [Filters ▼]  │
├──────────┬──────────────────────────┤
│          │  Projects (5)            │
│ Filters  │  • Project Alpha         │
│ [x] Type │  • Project Beta          │
│   Projects                          │
│   Tasks  │  Tasks (12)              │
│   Docs   │  • Update docs           │
│          │  • Review code           │
└──────────┴──────────────────────────┘

Mobile (under 1024px)

┌────────────────────────┐
│  [Search_________] [⚙️]│
├────────────────────────┤
│  Projects (5)          │
│  • Project Alpha       │
│  • Project Beta        │
│                        │
│  Tasks (12)            │
│  • Update docs         │
│  • Review code         │
└────────────────────────┘

Integration with Payload CMS

Search can query multiple Payload collections:

// Search across multiple collections
<SearchView
  collections={['projects', 'tasks', 'documents']}
  query={query}
>
  <SearchResults />
</SearchView>

Best Practices

Debounce Input

Avoid excessive queries:

const debouncedQuery = useDebounce(query, 300)

useEffect(() => {
  if (debouncedQuery) {
    searchAPI(debouncedQuery)
  }
}, [debouncedQuery])

Show Loading States

Display loading indicator while searching:

<SearchView query={query} loading={isSearching}>
  {isSearching ? (
    <SearchSkeleton />
  ) : (
    <SearchResults results={results} />
  )}
</SearchView>

Highlight Matches

Make matching terms stand out:

// Good - highlighted matches
<SearchResult
  title={<HighlightedText text={result.title} query={query} />}
/>

// Bad - no highlighting
<SearchResult title={result.title} />

Show Result Counts

Help users understand scope:

<SearchView
  query={query}
  total={total}
  title={`${total} results for "${query}"`}
/>

Accessibility

Search implements accessibility features:

  • ARIA labels - Descriptive search input labels
  • Keyboard navigation - Navigate results with arrows
  • Live regions - Announce result counts
  • Focus management - Focus on results after search
<SearchView
  query={query}
  aria-label="Global search"
  role="search"
>
  <label htmlFor="search-input" className="sr-only">
    Search
  </label>
  <input
    id="search-input"
    type="search"
    value={query}
    onChange={(e) => setQuery(e.target.value)}
    aria-describedby="search-instructions"
  />
  <span id="search-instructions" className="sr-only">
    Type to search across all content
  </span>

  <div aria-live="polite" aria-atomic="true">
    {results.length > 0 && (
      <span className="sr-only">
        {results.length} results found for {query}
      </span>
    )}
  </div>

  <div role="list">
    {results.map((result) => (
      <article key={result.id} role="listitem">
        <a href={result.url}>{result.title}</a>
      </article>
    ))}
  </div>
</SearchView>

On this page