Skip to content

Components

SearchField

The search input, solved once: a rounded pill or an edge-to-edge bar for the Header's search row, theme-aware and controlled or uncontrolled.

Pill (default)

The classic mobile search bar — a rounded, inset field with its own horizontal padding, ready to drop into searchContent.

Open fullscreen
9:41
app.tsx
import { AppShell, Header, Content, SearchField } from "appshell-react";
import { useState } from "react";

export default function App() {
  const [query, setQuery] = useState("");

  return (
    <AppShell safeArea>
      <Header
        behavior="reveal-search"
        logo={<span className="font-bold">Market</span>}
        searchContent={
          <SearchField
            placeholder="Search 2,400 products"
            value={query}
            onChange={setQuery}
            onSubmit={(value) => console.log("search:", value)}
          />
        }
      />
      <Content>{/* filtered results */}</Content>
    </AppShell>
  );
}

Full-width

variant="full" spans the entire row as a flat bar with hairline borders — no side margins, no rounding. Use it when search is the primary action of the screen.

header.tsx
<Header
  behavior="fixed"
  logo={<span className="font-bold">Library</span>}
  searchContent={
    // Flat bar spanning the whole row, edge to edge
    <SearchField variant="full" placeholder="Search the catalog" />
  }
/>

Where does search go on desktop?

The full-row search bar is a mobile pattern. On desktop, follow the layout: no sidebar — keep a compact pill in the Header (its centered max width handles wide screens) and give it a shortcutHint like "⌘K", bound with useSearchShortcut; docked sidebar — move the pill into the Sidebar’s topContent slot with inset={false}, where it fills the panel’s otherwise-empty top and leaves the header clean. The Docked Sidebar demo shows the whole arrangement live.

Theme awareness

Inside a theme="primary" or theme="dark" Header, the field switches to translucent light-on-dark surfaces automatically via useHeaderTheme() — no extra classes needed.

Outside a Header the theme context falls back to light, so SearchField works anywhere a search box is needed — a sidebar, a page body, a dialog.

API

PropTypeDefaultDescription
variant"pill" | "full""pill""pill" is the rounded inset field; "full" spans the row edge to edge with no surface of its own, taking the background it sits on so the search row reads as part of the header.
placeholderstring"Search"Placeholder and default accessible name.
valuestring—Controlled value.
defaultValuestring—Uncontrolled initial value.
onChange(value: string) => void—Fires on every keystroke with the new value.
onSubmit(value: string) => void—Fires with the current value when Enter is pressed.
onClick() => void—Click/tap passthrough — the natural place to open a SearchModal (a click, unlike focus, can't retrigger when the modal restores focus on close).
onFocus() => void—Focus passthrough on the input.
shortcutHintReactNode—Keyboard-shortcut chip at the right edge — "⌘K", "Ctrl K". Hidden below the sm breakpoint; pair with useSearchShortcut.
insetbooleantruePill only. Header-row insetting (padding + centered max width). Set false when the pill lives somewhere already padded — a Sidebar's topContent slot, a card.
classNamestring—Extra classes for the field surface.
inputClassNamestring—Extra classes for the inner input element.
aria-labelstring—Accessible name override (defaults to the placeholder).