> ## Documentation Index
> Fetch the complete documentation index at: https://api.fanvue.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# CyclingText

> Cycles through a list of strings with a slide-in/slide-out animation.

```tsx theme={null}
import { CyclingText } from "@fanvue/ui";
import type { CyclingTextProps, CyclingTextSizing } from "@fanvue/ui";
```

## Examples

## Default

<Frame>
  <iframe src={"https://main--697a1b6dd4dad73ee9c0e5f5.chromatic.com/iframe.html?id=components-cyclingtext--default&viewMode=story&shortcuts=false&singleStory=true&globals=theme:light"} width="100%" height="280" style={{border: "none", borderRadius: "8px"}} loading="lazy" title="CyclingText — Default" />
</Frame>

```tsx theme={null}
import { CyclingText } from "@fanvue/ui";

const STAGES = [
  "Thinking",
  "Reading messages",
  "Drafting reply",
  "Connecting the dots",
  "Almost there",
];

<CyclingText
  items={STAGES}
  intervalMs={1800}
  transitionMs={380}
  direction="up"
  sizing="longest"
/>
```

## Fake Placeholder

<Frame>
  <iframe src={"https://main--697a1b6dd4dad73ee9c0e5f5.chromatic.com/iframe.html?id=components-cyclingtext--fake-placeholder&viewMode=story&shortcuts=false&singleStory=true&globals=theme:light"} width="100%" height="280" style={{border: "none", borderRadius: "8px"}} loading="lazy" title="CyclingText — Fake Placeholder" />
</Frame>

```tsx theme={null}
import { CyclingText } from "@fanvue/ui";

<div className="w-80 rounded-md border border-border-default bg-background-primary px-3 py-2 text-content-tertiary">
  <CyclingText
    items={["Search creators…", "Find a fan…", "Look up a transaction…", "Browse posts…"]}
  />
</div>
```

## Sized To Current

<Frame>
  <iframe src={"https://main--697a1b6dd4dad73ee9c0e5f5.chromatic.com/iframe.html?id=components-cyclingtext--sized-to-current&viewMode=story&shortcuts=false&singleStory=true&globals=theme:light"} width="100%" height="280" style={{border: "none", borderRadius: "8px"}} loading="lazy" title="CyclingText — Sized To Current" />
</Frame>

```tsx theme={null}
import { CyclingText } from "@fanvue/ui";

const STAGES = [
  "Thinking",
  "Reading messages",
  "Drafting reply",
  "Connecting the dots",
  "Almost there",
];

<CyclingText items={STAGES} sizing="current" />
```

## Inside A Button

<Frame>
  <iframe src={"https://main--697a1b6dd4dad73ee9c0e5f5.chromatic.com/iframe.html?id=components-cyclingtext--inside-a-button&viewMode=story&shortcuts=false&singleStory=true&globals=theme:light"} width="100%" height="280" style={{border: "none", borderRadius: "8px"}} loading="lazy" title="CyclingText — Inside A Button" />
</Frame>

```tsx theme={null}
import { Button, CyclingText, SpinnerIcon } from "@fanvue/ui";

const STAGES = [
  "Thinking",
  "Reading messages",
  "Drafting reply",
  "Connecting the dots",
  "Almost there",
];

<Button variant="secondary">
  <SpinnerIcon className="mr-2 size-4 animate-spin" />
  <CyclingText items={STAGES} />
</Button>
```

## Inside A Status Row

<Frame>
  <iframe src={"https://main--697a1b6dd4dad73ee9c0e5f5.chromatic.com/iframe.html?id=components-cyclingtext--inside-a-status-row&viewMode=story&shortcuts=false&singleStory=true&globals=theme:light"} width="100%" height="280" style={{border: "none", borderRadius: "8px"}} loading="lazy" title="CyclingText — Inside A Status Row" />
</Frame>

```tsx theme={null}
import { CyclingText, SpinnerIcon } from "@fanvue/ui";

const STAGES = [
  "Thinking",
  "Reading messages",
  "Drafting reply",
  "Connecting the dots",
  "Almost there",
];

<div className="inline-flex items-center gap-2 text-content-tertiary">
  <SpinnerIcon className="size-4 animate-spin" />
  <CyclingText items={STAGES} />
</div>
```

## Props

## CyclingText

Cycles through a list of strings with a slide-in/slide-out animation. Lives inline so it can sit inside divs, spans, buttons, or as a fake placeholder overlay.

<ParamField path="items" type="readonly string[]" required>
  Strings to cycle through, in order. Cycles back to the first after the last.
</ParamField>

<ParamField path="announceChanges" type="boolean" default="false">
  When `true`, updates are exposed to assistive technologies via `aria-live="polite"`. Leave `false` for decorative or frequently changing copy so screen readers are not interrupted on every cycle.
</ParamField>

<ParamField path="direction" type="&#x22;up&#x22; | &#x22;down&#x22;" default="up">
  Direction the outgoing item slides.
</ParamField>

<ParamField path="intervalMs" type="number" default="2100">
  Milliseconds to wait after the previous transition finishes before starting the next one.
</ParamField>

<ParamField path="labelClassName" type="string">
  Class applied to each visible label span (current + incoming). Use this for effects that have to sit on the text element itself, e.g. `background-clip: text`.
</ParamField>

<ParamField path="onActiveIndexChange" type="((index: number) => void)">
  Called with the index of the settled, fully-visible item — once on mount and again whenever it changes (after each transition completes). Lets a parent stay in lockstep with what is on screen (e.g. to act on the item the user is currently looking at) without running a second, drifting timer of its own.
</ParamField>

<ParamField path="paused" type="boolean" default="false">
  When true, freezes on the current item — no further cycling until cleared.
</ParamField>

<ParamField path="sizing" type="&#x22;longest&#x22; | &#x22;current&#x22;" default="longest">
  How the wrapper sizes itself horizontally.

  * `longest` reserves space for the longest item — no width jitter as items cycle.
  * `current` shrinks/grows with each item, animating width between cycles.
</ParamField>

<ParamField path="transitionMs" type="number" default="200">
  Slide and cross-fade duration in milliseconds.
</ParamField>

## Exported types

Also exported from `@fanvue/ui`: `CyclingTextProps`, `CyclingTextSizing`.

***

**Setup:** [Installation](/docs/ui/installation) · [Theming](/docs/ui/theming)
