Search Expand

An icon button that expands into a search input with a smooth animation.

Overview

The SearchExpand component renders as a compact icon button that smoothly expands into a search input when clicked. The icon from the button becomes the leading icon in the input. Ideal for toolbars and headers where space is constrained.

The expanded state uses the same composition pattern as InputWrapper — pass an Input and optional trailing elements (clear buttons, submit buttons, etc.) as children.

Usage

import { Input, SearchExpand } from "@tilt-legal/cubitt-components/primitives";
<SearchExpand>
  <Input placeholder="Search..." size="md" variant="default" />
</SearchExpand>

Examples

Sizes

All three sizes match Button and Input height tokens exactly (sm=28px, md=34px, lg=40px). Pass the matching size to both SearchExpand and Input.

Button Variants

The collapsed button supports ghost, outline, and secondary variants.

Custom Icon

Pass any Cubitt icon via the icon prop. It appears in both the collapsed button and expanded input.

Custom Width

Control how wide the input expands with expandedWidth.

Clearable

Add a clear button as a trailing child — same pattern as InputWrapper.

Controlled

Use expanded and onExpandedChange for full control over the expand/collapse state.

URL State

SearchExpand with URL state synchronization owned by useSearch.

Keyboard & Focus

  • Click or Enter/Space on the collapsed button expands the input and auto-focuses it.
  • Escape collapses the input and returns focus to the button.
  • Blur when the input is empty collapses it automatically (configurable via collapseOnBlurWhenEmpty).
  • Blur when the input has a value keeps it expanded.

API Reference

SearchExpand

The outer container that handles the expand/collapse animation and button state. Pass Input and optional trailing elements as children.

PropTypeDefaultDescription
childrenReactNodeContent shown when expanded (Input + optional trailing elements)
iconCubittIconMagnifierIcon shown in both collapsed and expanded states
size"sm" | "md" | "lg""md"Matches Button/Input height tokens
expandedWidthstring | number"16rem"Width when expanded
variant"ghost" | "outline" | "secondary""secondary"Button variant for the collapsed state
expandedbooleanControlled expanded state
onExpandedChange(expanded: boolean) => voidCallback when expanded state changes
collapseOnBlurWhenEmptybooleantrueCollapse on blur when input is empty
collapseOnEscapebooleantrueCollapse when Escape is pressed
labelstring"Search"Accessible label for the button and input
classNamestringAdditional className on the outer container
reduceMotionbooleanfalseDisable animations (respects prefers-reduced-motion by default)
disabledbooleanfalseDisable the component

Children

SearchExpand uses the same composition pattern as InputWrapper. Pass an Input as the primary child and optional trailing elements:

<SearchExpand>
  <Input placeholder="Search..." size="md" variant="default" />
  <Button mode="icon" size="sm" variant="link" onClick={onClear}>
    <Xmark />
  </Button>
</SearchExpand>

Input props such as placeholder, size, and variant are set directly on the Input child. For search state, prefer useSearch and pass search.inputProps into the composed input. If useSearch owns URL sync, do not also pass paramName to the Input child.

On this page