# Rotating Text

> Cycles through words letter by letter with slide, flip, blur, scale or drop swaps, on an optional highlight block that resizes to fit each word.

Swaps between a list of texts on a timer. Each letter leaves and enters on its own, staggered from the first, last, center or a random letter. Pick slide, flip, blur, scale or drop and roll up or down. Turn on the highlight to set the text on a colored block that springs to the width of each word and picks a readable text color.

<demo-rotating-text>



</demo-rotating-text>

## Installation

```bash
npx shadcn-vue@latest add "https://nxui.geoql.in/r/rotating-text.json"
```

## Usage

```vue
<script setup lang="ts">
  import { useTemplateRef } from 'vue';
  import RotatingText from '~/components/ui/rotating-text/RotatingText.vue';

  const rotator = useTemplateRef('rotator');

  function onNext(index: number) {
    console.log('now showing', index);
  }
</script>

<template>
  <p class="flex items-center gap-[0.2em] text-5xl font-black">
    <span>Creative</span>
    <RotatingText
      ref="rotator"
      :texts="['thinking', 'coding', 'components!']"
      effect="drop"
      highlight
      highlight-color="#facc15"
      :stagger-duration="0.04"
      split-level-class="overflow-hidden pb-1"
      @next="onNext"
    />
  </p>
  <button type="button" @click="rotator?.next()">Next</button>
</template>
```

## Props

<table>
<thead>
  <tr>
    <th>
      Prop
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        texts
      </code>
    </td>
    
    <td>
      <code>
        string[]
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Texts to cycle through
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        effect
      </code>
    </td>
    
    <td>
      <code>
        'slide' | 'flip' | 'blur' | 'scale' | 'drop'
      </code>
    </td>
    
    <td>
      <code>
        'slide'
      </code>
    </td>
    
    <td>
      How letters swap: slide, roll over like a drum, dissolve, zoom, or drop in
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        direction
      </code>
    </td>
    
    <td>
      <code>
        'up' | 'down'
      </code>
    </td>
    
    <td>
      <code>
        'up'
      </code>
    </td>
    
    <td>
      Which way letters roll
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        highlight
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Set the text on a colored block that resizes to fit each text
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        highlight-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#a855f7'
      </code>
    </td>
    
    <td>
      Block color; the text turns black or white for contrast
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        rotation-interval
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        2000
      </code>
    </td>
    
    <td>
      ms between texts
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        blur
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Blur (px) during the swap; the blur effect uses 10 when this is 0
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        stagger-duration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Seconds between letters
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        stagger-from
      </code>
    </td>
    
    <td>
      <code>
        'first' | 'last' | 'center' | 'random' | number
      </code>
    </td>
    
    <td>
      <code>
        'first'
      </code>
    </td>
    
    <td>
      Where the stagger starts; a number starts at that letter index
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        split-by
      </code>
    </td>
    
    <td>
      <code>
        'characters' | 'words' | 'lines' | string
      </code>
    </td>
    
    <td>
      <code>
        'characters'
      </code>
    </td>
    
    <td>
      Animate letters, words, lines, or parts split by a custom separator
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        loop
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Go back to the first text after the last
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        auto
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Rotate on a timer; when off, drive it through the exposed methods
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        pause-on-hover
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Hold the current text while the pointer is over it
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        initial
      </code>
    </td>
    
    <td>
      <code>
        VariantType
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Custom enter state for each letter (replaces the effect)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        animate
      </code>
    </td>
    
    <td>
      <code>
        VariantType
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Custom rest state
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        exit
      </code>
    </td>
    
    <td>
      <code>
        VariantType
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Custom leave state
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        transition
      </code>
    </td>
    
    <td>
      <code>
        Transition
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Custom motion-v transition for each letter and the layout
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        animate-presence-mode
      </code>
    </td>
    
    <td>
      <code>
        'wait' | 'sync' | 'popLayout'
      </code>
    </td>
    
    <td>
      <code>
        'wait'
      </code>
    </td>
    
    <td>
      <code>
        AnimatePresence
      </code>
      
       mode: wait for the old text to leave, or swap at once
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        animate-presence-initial
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Animate the first text on mount
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        split-level-class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Classes for each word wrapper, e.g. <code>
        overflow-hidden
      </code>
      
       to clip slide and drop
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        element-level-class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Classes for each letter
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Classes for the root
    </td>
  </tr>
</tbody>
</table>

## Events

<table>
<thead>
  <tr>
    <th>
      Event
    </th>
    
    <th>
      Payload
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        next
      </code>
    </td>
    
    <td>
      <code>
        (index: number)
      </code>
    </td>
    
    <td>
      Fired each time the visible text changes
    </td>
  </tr>
</tbody>
</table>

## Exposed methods

Reach them through a template ref.

<table>
<thead>
  <tr>
    <th>
      Method
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        next()
      </code>
    </td>
    
    <td>
      Show the next text (wraps when <code>
        loop
      </code>
      
       is on)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        previous()
      </code>
    </td>
    
    <td>
      Show the previous text (wraps when <code>
        loop
      </code>
      
       is on)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        jumpTo(index)
      </code>
    </td>
    
    <td>
      Show the text at <code>
        index
      </code>
      
       (clamped)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reset()
      </code>
    </td>
    
    <td>
      Go back to the first text
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The current text sits in a visually hidden span; the animated letters are `aria-hidden`, so screen readers announce whole words.
- Motion follows `prefers-reduced-motion` through `MotionConfig reduced-motion="user"`, and the highlight block stops springing its width.
- The highlight picks black or white text from the block color's luminance, so any color stays readable.
- Ported from [React Bits](https://reactbits.dev/text-animations/rotating-text) by David Haz.
