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)string—Accessible name applied to both the collapsed trigger and the expanded input.
clearLabel(required)string—Accessible 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 IconButtons 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.
itemsreadonly Value[] | (readonly Group<Value>[] & 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. Nullish entries are not supported: remove them from the data before passing it. Items to display in the autocomplete list.
placeholderstringSearch…Input placeholder shown once expanded.
expandedboolean—Controlled 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).
defaultExpandedboolean—Uncontrolled 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.
childrenReactNode—Popup content (e.g. AutocompleteContent) shown once expanded, when items are provided.