---
title: 'Text Motion'
excerpt: 'SplitText headlines, ScrambleText loading states, and number tickers.'
icon: 'Sparkles'
---

## When text moves

Text motion is high-visibility and easy to overuse. Split and scramble belong on **occasional** headlines and status moments. Number tickers fit metrics that change once per view. Always revert SplitText on cleanup so the DOM returns to plain text.

## SplitText headline

**SplitText** wraps words/chars so you can stagger them like any other targets. Split only what you animate — `words,chars` without `lines` when you do not need line boxes.

Watch characters rise in a tight stagger. Call `split.revert()` when the effect is done or the component unmounts.

<ComponentPreview replay>
  <GsapSplitTextHeadline />

  <DocsFileTabs title="Split text">

```tsx title="split-text.tsx"
import { SplitText } from 'gsap/SplitText';
gsap.registerPlugin(SplitText);

const split = SplitText.create('.headline', { type: 'words,chars' });
gsap.from(split.chars, { y: 20, autoAlpha: 0, stagger: 0.02, duration: 0.35 });
return () => split.revert();
```

```html title="split-text-markup.html"
<h3 class="gsap-headline text-xl font-bold">
  Ship motion that feels right
</h3>
<!-- SplitText splits into chars: split.chars -->
```

  </DocsFileTabs>
</ComponentPreview>

## ScrambleText

**ScrambleText** cycles random characters into the final string — classic for loading / decrypt-style status. Keep it monospace so glyph width stays stable while chars thrash.

Replay: noise settles into “Deployment complete.” Use for status lines, not body copy.

<ComponentPreview replay>
  <GsapScrambleLabel />

  <DocsFileTabs title="Scramble text">

```tsx title="scramble-text.tsx"
gsap.to('.label', {
  duration: 1.2,
  scrambleText: { text: 'Deployment complete', chars: '01', revealDelay: 0.4 },
});
```

```html title="scramble-text-markup.html"
<p class="gsap-scramble font-mono">Loading........</p>
```

  </DocsFileTabs>
</ComponentPreview>

## Number ticker

Tween a plain object’s `value` and write `textContent` in `onUpdate`. That avoids animating the DOM string directly. Use **`tabular-nums`** (or fixed width) so digits do not shift layout as 1 → 2847.

Watch the counter ease up — each frame rounds for display.

<ComponentPreview replay>
  <GsapNumberTicker />

  <DocsFileTabs title="Number ticker">

```tsx title="number-ticker.tsx"
gsap.to(obj, {
  value: 2847,
  duration: 1.5,
  ease: 'power2.out',
  onUpdate: () => { el.textContent = Math.round(obj.value).toLocaleString(); },
});
```

```html title="number-ticker-markup.html"
<div class="text-center">
  <p>Active users</p>
  <p class="gsap-ticker text-3xl font-bold tabular-nums">0</p>
</div>
```

  </DocsFileTabs>
</ComponentPreview>
