Skip to content
caliper ui docs

Components

Cycling placeholder

A text field whose placeholder rolls through examples of what to type, so an empty search box teaches what it can find. The roll pauses while the field has focus and the examples disappear once anything is typed.

Free
pnpm dlx shadcn@latest add @caliper/cycling-placeholder

Search

The examples change until you start typing.

For example, overdue invoices
Installs components/ui/cycling-placeholder.tsxDepends on none beyond ReactMotion shared motion tokens

When to use

Use it for

  • Search and command fields that can do more than people expect.
  • AI prompts where an example sets the tone.

Skip it when

  • Fields with one obvious input, such as email.
  • Using the examples as the field's only label; keep a visible or accessible name.

Decisions

Why it is built this way, so you know what to keep when you change your copy.

Behaviour

Pauses on focus
Moving text next to the caret is distracting, so the roll stops while the field has focus.

Usage

One stable hint
The first example is the field's real placeholder, so assistive technology hears one hint and not a changing one.

Installation

terminal
pnpm dlx shadcn@latest add @caliper/cycling-placeholder

Adds its 2 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.

Usage

This is the code behind the preview above.

example.tsx
import { Search } from "lucide-react"
import { type ReactNode } from "react"
import { CyclingPlaceholder } from "@/components/ui/cycling-placeholder"

function Panel({ title, context, className = "max-w-md", children }: { title: string; context: string; className?: string; children: ReactNode }) {
  return (
    <section className={`grid w-full gap-5 rounded-overlay border border-border bg-background p-6 shadow-resting max-sm:p-4 ${className}`}>
      <div className="grid gap-1">
        <p className="font-medium">{title}</p>
        <p className="text-sm text-muted-foreground">{context}</p>
      </div>
      {children}
    </section>
  );
}

export function CyclingPlaceholderDemo() {
  return (
    <Panel title="Search" context="The examples change until you start typing.">
      <div className="relative">
        <Search className="pointer-events-none absolute top-1/2 left-3 z-10 size-4 -translate-y-1/2 text-muted-foreground" aria-hidden="true" />
        <CyclingPlaceholder aria-label="Search" prefix="Search" examples={["overdue invoices", "Maya's last comment", "deploys this week", "refunds over $500"]} className="[&_input]:pl-9 [&_[data-slot=cp-hint]]:left-9" />
      </div>
    </Panel>
  );
}

API reference

CyclingPlaceholder

component

Also accepts the native props of its root element and a ref.

PropTypeDefaultDescription
examples *string[]—Examples shown in turn while the field is empty, such as "overdue invoices".
intervalnumber2600Milliseconds each example stays.

Accessibility

It is built on native elements and keeps aria-describedby and aria-hidden in step with state.

No custom key handling; Tab and the native activation keys do the work.

Motion

Transitions use the shared duration and easing tokens. Under prefers-reduced-motion those tokens collapse to instant and travel distances go to zero, so state changes still happen without movement.

All Text motion examples