Search
A flexible, fully accessible search component with support for Docked and Full-screen views, profile avatar, and elevation tokens.
The Search component allows users to input search queries and view suggestions or results. Following the Material Design 3 Expressive guidelines, this component supports flexible display modes—collapsing into a pill-shaped SearchBar with Level 3 elevation shadow and expanding into a SearchView overlay with smooth Framer Motion layout transitions.
Introduction
The MD3 Expressive Search system is a highly modular set of components designed for both simple queries and complex filtering. It seamlessly transitions between a compact, non-intrusive pill (SearchBar) and a focused results view (SearchView). Using Framer Motion's shared element transitions, the expansion feels fluid and natural, anchoring the user's focus.
Anatomy
- Search Bar (Collapsed): A pill-shaped input field (height 56dp,
CornerFull) with Level 3 elevation shadow (elevation-3) and interactive hover/press state layers. - Search View (Active): The expanded panel (
CornerExtraLarge28dp radius in Docked,CornerNonein Full-screen) that displays suggestions and results. - Leading Icon: Typically a "Search" icon when collapsed, and a "Back" arrow or custom icon when active.
- Input Field: The area for typing the query with animated placeholder support.
- Trailing Actions: Icons for triggering secondary actions (e.g., Microphone, Camera) and a smooth animated "Clear" button (
close) when text is typed. - Profile Avatar: A 30dp circular slot (
SearchBarTokens.AvatarSize = 30dp) positioned at the trailing edge for user profile imagery or initials. - Results / Suggestions List: The scrollable area below the input with keyboard arrow navigation.
Variants
Docked View
The search view expands directly below the input field, keeping the rest of the screen visible. Ideal for desktop and tablet layouts.
- Contained: The input and results share a single continuous container.
- Divided: A horizontal line separates the input from the results.
- Gap: An optional 2dp separation gap between the header and results panel (
hasGap={true}).
Full-screen View
The search view expands to cover the entire viewport using a React Portal. Best for mobile devices to provide maximum focus on results.
Features
Profile Avatar & Flexible Trailing Actions
The component supports a dedicated avatar prop alongside trailingIcon. When the query is empty, both trailing action icons and user avatars are rendered with standard spacing. When the user types, the trailing action smoothly animates into a "Clear" button while maintaining the avatar slot.
Shared Element Transitions
When the user focuses the SearchBar, it morphs into the SearchView header using a layoutId transition, ensuring a consistent visual anchor between states.
Interactive State Layer & Level 3 Elevation
The SearchBar container features MD3 Level 3 box-shadow elevation (SEARCH_ELEVATION.level3 / elevation-3) and a built-in StateLayer that provides visual feedback for hover (8% opacity) and pressed (12% opacity) interactions.
Text Alignment & Animated Placeholder
The placeholder text can be aligned ("left", "center", or "right") in the collapsed state and smoothly slides to the left as the field is focused via GPU-accelerated translateX transforms.
Usage
Basic Usage
import { useState } from "react";
import { Search } from "@bug-on/m3-expressive";
export function SearchExample() {
const [query, setQuery] = useState("");
const [active, setActive] = useState(false);
return (
<Search
query={query}
onQueryChange={setQuery}
onSearch={(q) => console.log("Search:", q)}
active={active}
onActiveChange={setActive}
placeholder="Search photos..."
>
<div className="p-4">
{/* Render search results or suggestions with role="option" */}
<p>Suggestions go here...</p>
</div>
</Search>
);
}
With Avatar and Custom Trailing Action
import { Icon, Search } from "@bug-on/m3-expressive";
<Search
query={query}
onQueryChange={setQuery}
active={active}
onActiveChange={setActive}
placeholder="Search with avatar..."
trailingIcon={<Icon name="mic" className="size-5" />}
avatar={
<img
src="/avatar.jpg"
alt="User Profile"
className="size-full object-cover"
/>
}
>
<SearchResults />
</Search>
Context Hook: Search.useSearch
Child result items inside the search view can consume the search context for keyboard navigation:
function SearchResultItem({ label, index, onSelect }: ResultItemProps) {
const { activeIndex, listboxId } = Search.useSearch();
const isHighlighted = activeIndex === index;
return (
<div
id={`${listboxId}-option-${index}`}
role="option"
aria-selected={isHighlighted}
tabIndex={-1}
className={isHighlighted ? "bg-m3-surface-container-highest" : ""}
onClick={onSelect}
>
{label}
</div>
);
}
Best Practices
Do
- Use Docked search for desktop layouts and Full-screen for mobile screens.
- Provide immediate visual feedback as the user types (live suggestions).
- Utilize
avatarfor personalized user profile search bars (e.g., Google Account search). - Use
Search.useSearch()to bindrole="option"items to the active index for accessible keyboard navigation.
Don't
- Don't hide the search entry point in deeply nested menus if search is a primary app function.
- Avoid long delays in displaying search results; display loading states when fetching async suggestions.
- Don't hardcode fixed widths on the SearchBar; allow responsive fluid layouts with
className.
Accessibility
- Roles: Correctly implements
role="search"landmark androle="combobox"input. - States: Uses
aria-expanded,aria-controls, andaria-activedescendantto communicate dropdown state to screen readers. - Keyboard Navigation: Navigate suggestions with
ArrowDown/ArrowUp, select withEnter/Space, and close withEscape. - Focus Management: Automatically manages input focus during animated state transitions.
API Reference
Search
| Prop | Type | Default | Description |
|---|---|---|---|
query | string | — | Required. Controlled search text value. |
onQueryChange | (query: string) => void | — | Required. Callback invoked when input text changes. |
onSearch | (query: string) => void | — | Required. Callback invoked when user submits search (Enter key or selection). |
active | boolean | false | Required. Controlled open/expanded state. |
onActiveChange | (active: boolean) => void | — | Required. Callback invoked when search view should open or close. |
variant | "docked" | "fullscreen" | "docked" | Expanded layout variant. |
styleType | "contained" | "divided" | "contained" | Visual style for SearchView container (contained or divided). |
placeholder | string | "Search" | Placeholder text when query is empty. |
textAlign | "left" | "center" | "right" | "left" | Alignment of placeholder text in idle state. |
hasGap | boolean | false | (Docked only) Adds a 2dp gap between header and results. |
leadingIcon | ReactNode | <SearchIcon /> | Leading icon slot. Falls back to default search icon. |
trailingIcon | ReactNode | — | Trailing icon slot (e.g. mic, camera). Automatically switches to clear button when query is present. |
avatar | ReactNode | — | Profile avatar slot (30dp circle). Rendered at the trailing edge. |
className | string | — | Additional CSS class for the SearchBar root element. |
viewClassName | string | — | Additional CSS class for the SearchView container. |
id | string | useId() | Unique ID for the search input and Framer Motion layoutId. |
aria-label | string | "Search" | Accessible label for the search landmark. |
children | ReactNode | — | Search suggestions or results rendered inside the expanded view. |
Tokens & Exports
| Export | Type | Description |
|---|---|---|
Search | React.Component | Main orchestrator component with Search.useSearch context hook. |
SearchBar | React.Component | Standalone collapsed pill SearchBar. |
SearchViewDocked | React.Component | Standalone expanded docked popup view. |
SearchViewFullScreen | React.Component | Standalone expanded full-screen overlay view. |
SearchTokens | object | Dimensional tokens (heights, avatarSize: 30, iconSize: 20, dropdownGap: 2). |
SEARCH_ELEVATION | object | Elevation utility class tokens (level3: "elevation-3"). |
SEARCH_COLORS | object | MD3 CSS custom property color references (container, leadingIcon, trailingIcon, inputText, divider, stateLayer). |
useSearchKeyboard | hook | WAI-ARIA combobox keyboard navigation hook. |