Propel

Expandable Search

A collapsed icon trigger that expands into a search input on click.

Show code
import { ExpandableSearch } from "@makeplane/propel/components/expandable-search";

export default function BasicDemo() {
  return <ExpandableSearch aria-label="Search" clearLabel="Clear search" size="lg" />;
}

Installation

import { ExpandableSearch } from "@makeplane/propel/components/expandable-search";

Usage

import { ExpandableSearch } from "@makeplane/propel/components/expandable-search";

export default function BasicDemo() {
  return <ExpandableSearch aria-label="Search" clearLabel="Clear search" size="lg" />;
}

Examples

With suggestions

Pass items and AutocompleteContent (same anatomy as Autocomplete) to turn the expanded input into a filterable search box. Without them, the popup stays forced closed and it’s a plain search box.

Show code
import {
  AutocompleteContent,
  AutocompleteEmpty,
  AutocompleteItem,
  AutocompleteList,
} from "@makeplane/propel/components/autocomplete";
import { ExpandableSearch } from "@makeplane/propel/components/expandable-search";

const IMAGES = ["nginx:1.29-alpine", "node:22-slim", "postgres:18", "redis:8.2.2-alpine"];

export default function WithSuggestionsDemo() {
  return (
    <ExpandableSearch
      aria-label="Search images"
      clearLabel="Clear search"
      size="lg"
      items={IMAGES}
      mode="both"
    >
      <AutocompleteContent>
        <AutocompleteEmpty>No matches</AutocompleteEmpty>
        <AutocompleteList>
          {(image: string) => (
            <AutocompleteItem key={image} value={image} size="md">
              {image}
            </AutocompleteItem>
          )}
        </AutocompleteList>
      </AutocompleteContent>
    </ExpandableSearch>
  );
}

Sizes

size steps the collapsed trigger and expanded input row together (md, lg, xl, 2xl). The inner icon buttons sit one ladder rung below the row.

Show code
import { type AutocompleteSize } from "@makeplane/propel/components/autocomplete";
import { ExpandableSearch } from "@makeplane/propel/components/expandable-search";

const SIZES: AutocompleteSize[] = ["md", "lg", "xl", "2xl"];

export default function SizesDemo() {
  return (
    <div className="flex flex-col gap-3">
      {SIZES.map((size) => (
        <ExpandableSearch
          key={size}
          aria-label={`Search ${size}`}
          clearLabel="Clear search"
          size={size}
        />
      ))}
    </div>
  );
}

API Reference

ExpandableSearch

A collapsed icon trigger that expands into a search input on click, collapsing again on blur or Escape while empty — unless it's `required` and still invalid, in which case it stays expanded so the field remains focusable instead of collapsing back into an inert trigger. Escape restores focus to the trigger. Expand is controllable via `expanded`/`defaultExpanded`/`onExpandedChange` (separate from Autocomplete's popup `open`). Built on `Autocomplete` — pass `items` and `children` (e.g. `AutocompleteContent`) for suggestions, or omit both for a plain search box. Without `children` the popup is forced closed (no listbox ever mounts), so a plain search box never claims `aria-expanded`. Non-empty `value`/`defaultValue` starts expanded (and controlled non-empty `value` keeps it expanded); a controlled `open` is forced closed while collapsed so a popup never mounts over an inert input. The `Autocomplete` root and its input stay mounted at every state — the trigger/row swap width via an animated CSS grid track and `inert` instead of unmounting or `hidden` — so a `required`/`defaultValue` input keeps working under native form validation and keeps its own state across collapse/expand instead of resetting.

PropTypeDefaultDescription
aria-label(required)stringAccessible name applied to both the collapsed trigger and the expanded input.
clearLabel(required)stringAccessible name for the clear button shown once the input has a value. Localizable.
size(required)"md" | "lg" | "xl" | "2xl"Size, shared by the collapsed trigger and the expanded input row (same scale as `Autocomplete`). The trigger/clear `IconButton`s step one ladder rung below it (see `ICON_BUTTON_SIZE`), so the collapsed icon reads as a smaller control inside the expanded row's own box — `ExpandableSearchTrigger` pads that box up to the row's height at the same step, so toggling between them never shifts height.
items((readonly any[] | readonly Group<any>[]) & readonly Value[])The items to be displayed in the list. Can be either a flat array of items or an array of groups with items. Items to display in the autocomplete list.
placeholderstringSearch…Input placeholder shown once expanded.
expandedbooleanControlled expand state. When set, the trigger/row visibility is driven by the consumer (e.g. a `/` shortcut). Pair with `onExpandedChange`. Distinct from Autocomplete's `open` (popup).
defaultExpandedbooleanUncontrolled initial expand state. Defaults to expanded when `value`/`defaultValue` is non-empty so a pre-filled search is not trapped under an inert trigger.
onExpandedChange((expanded: boolean) => void)Called when expand state changes, in both controlled and uncontrolled mode.
childrenReactNodePopup content (e.g. `AutocompleteContent`) shown once expanded, when `items` are provided.