# Text flip

Cycle through words or phrases in place, flipping the next one in as the current one flips out.

Rotates a set of words in a sentence or heading, like the framework names in this site's hero. Works in React, Vue,
Svelte, Solid, Preact and vanilla JS.

- Flips on a timer, or on demand with `next`, `prev` and `goTo`
- Sizes to the widest item, so the text around it never shifts
- Hides every item but the current one from assistive technology
- Renders the first item as plain text until hydrated, so server-rendered markup reads fine
- Respects reduced motion

## Installation

Pick your framework in the preview, then copy the prompt into Claude Code, Cursor or
another coding agent, or add the component yourself with the shadcn CLI or by hand.

Add the component with the shadcn CLI, using the registry item for your framework, styling and API (Zag or Ark UI):

```sh
# React, Panda CSS, Zag
pnpm dlx shadcn@latest add https://isbatak-labs.vercel.app/r/text-flip-basic-react-panda.json

# React, CSS, Zag
pnpm dlx shadcn@latest add https://isbatak-labs.vercel.app/r/text-flip-basic-react.json

# Vue, Panda CSS, Zag
pnpm dlx shadcn@latest add https://isbatak-labs.vercel.app/r/text-flip-basic-vue-panda.json

# Vue, CSS, Zag
pnpm dlx shadcn@latest add https://isbatak-labs.vercel.app/r/text-flip-basic-vue.json

# Svelte, Panda CSS, Zag
pnpm dlx shadcn@latest add https://isbatak-labs.vercel.app/r/text-flip-basic-svelte-panda.json

# Svelte, CSS, Zag
pnpm dlx shadcn@latest add https://isbatak-labs.vercel.app/r/text-flip-basic-svelte.json

# Solid, Panda CSS, Zag
pnpm dlx shadcn@latest add https://isbatak-labs.vercel.app/r/text-flip-basic-solid-panda.json

# Solid, CSS, Zag
pnpm dlx shadcn@latest add https://isbatak-labs.vercel.app/r/text-flip-basic-solid.json

# Preact, Panda CSS, Zag
pnpm dlx shadcn@latest add https://isbatak-labs.vercel.app/r/text-flip-basic-preact-panda.json

# Preact, CSS, Zag
pnpm dlx shadcn@latest add https://isbatak-labs.vercel.app/r/text-flip-basic-preact.json

# Vanilla, Panda CSS, Zag
pnpm dlx shadcn@latest add https://isbatak-labs.vercel.app/r/text-flip-basic-vanilla-panda.json

# Vanilla, CSS, Zag
pnpm dlx shadcn@latest add https://isbatak-labs.vercel.app/r/text-flip-basic-vanilla.json

# React, Panda CSS, Ark UI
pnpm dlx shadcn@latest add https://isbatak-labs.vercel.app/r/text-flip-basic-react-panda-ark.json

# React, CSS, Ark UI
pnpm dlx shadcn@latest add https://isbatak-labs.vercel.app/r/text-flip-basic-react-ark.json

# Vue, Panda CSS, Ark UI
pnpm dlx shadcn@latest add https://isbatak-labs.vercel.app/r/text-flip-basic-vue-panda-ark.json

# Vue, CSS, Ark UI
pnpm dlx shadcn@latest add https://isbatak-labs.vercel.app/r/text-flip-basic-vue-ark.json

# Svelte, Panda CSS, Ark UI
pnpm dlx shadcn@latest add https://isbatak-labs.vercel.app/r/text-flip-basic-svelte-panda-ark.json

# Svelte, CSS, Ark UI
pnpm dlx shadcn@latest add https://isbatak-labs.vercel.app/r/text-flip-basic-svelte-ark.json

# Solid, Panda CSS, Ark UI
pnpm dlx shadcn@latest add https://isbatak-labs.vercel.app/r/text-flip-basic-solid-panda-ark.json

# Solid, CSS, Ark UI
pnpm dlx shadcn@latest add https://isbatak-labs.vercel.app/r/text-flip-basic-solid-ark.json

# Preact, Panda CSS, Ark UI
pnpm dlx shadcn@latest add https://isbatak-labs.vercel.app/r/text-flip-basic-preact-panda-ark.json

# Preact, CSS, Ark UI
pnpm dlx shadcn@latest add https://isbatak-labs.vercel.app/r/text-flip-basic-preact-ark.json
```

With Panda CSS, add `"@isbatak/panda-text-flip"` to `presets` in your Panda config, or import `textFlipPreset` from it and add that instead.

Preact with Ark UI uses the React components through `preact/compat`, so alias `react` and `react-dom` to `preact/compat` unless `@preact/preset-vite` already does.

## Examples

Scroll to an example to load it in the preview, and open the code view there for the full source. The settings button in
the preview changes the example live, and the snippet under it follows. Each example is its own registry item, so you
can add it with the CLI the same way.

Every item needs its zero-based `index`, in the order the items flip.

### Controls

The machine exposes `next`, `prev`, `goTo`, `play` and `pause`, and `playing` tells you whether the timer is running.
Wire them to buttons to let people step through the items or stop the animation. With Ark UI, create the machine with
`useTextFlip` and pass it to `TextFlip.RootProvider` so the buttons outside the root can use it.

With Zag (React):

```ts
const service = useMachine(textFlip.machine, {})
```

With Ark UI (React):

```tsx
<TextFlip.Root>
  <TextFlip.Item index={0}>React</TextFlip.Item>
</TextFlip.Root>
```

### Timing

Set `interval` to the time in milliseconds each item stays before the next one flips in. The default is `2000`. Set
`autoplay` to `false` to start paused, and `defaultIndex` to start on another item.

With Zag (React):

```ts
const service = useMachine(textFlip.machine, {
  interval: 1000,
})
```

With Ark UI (React):

```tsx
<TextFlip.Root interval={1000}>
  <TextFlip.Item index={0}>React</TextFlip.Item>
</TextFlip.Root>
```

## Styling

The root is an inline grid and every item sits in the same cell, so the root is as wide as the widest item. Each item
gets `data-state` set to `visible` for the first item before any flip, `entering` for the current item, `exiting` for
the one it replaced and `hidden` for the rest. The Panda recipe animates `entering` and `exiting` with a blurred slide,
and turns the animation off when people prefer reduced motion.

## Accessibility

Items that aren't shown get `aria-hidden`, so screen readers only read the current one, and they don't announce each
flip. When the words are decoration, like a list of supported frameworks, hide the root with `aria-hidden` and put the
full list in visually hidden text next to it.

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `ids` | `ElementIds` | — | The ids of the elements in the text flip. Useful for composition. |
| `interval` | `number` | `2000` | The time in milliseconds each item stays before the next one flips in. |
| `autoplay` | `boolean` | `true` | Whether the items start flipping on their own. |
| `defaultIndex` | `number` | `0` | The index of the item shown first. |
| `onIndexChange` | `(details: IndexChangeDetails) => void` | — | Function called when the shown item changes. |
| `id` | `string` | — | The unique identifier of the machine. |
| `getRootNode` | `() => ShadowRoot \| Document \| Node` | — | A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron. |

## API

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `index` | `number` | — | The index of the shown item. |
| `playing` | `boolean` | — | Whether the items are flipping on their own. |
| `next` | `() => void` | — | Show the next item, wrapping around after the last one. |
| `prev` | `() => void` | — | Show the previous item, wrapping around before the first one. |
| `goTo` | `(index: number) => void` | — | Show the item at the given index. |
| `play` | `() => void` | — | Start flipping on a timer. |
| `pause` | `() => void` | — | Stop flipping on a timer. |
| `getItemState` | `(props: ItemProps) => ItemState` | — | Returns whether an item is shown, flipping in, flipping out or hidden. |
