React Examples

Common animation patterns using the SplitText React component.

Basic Fade In

The onSplit callback fires after the text is split, giving you access to the split elements for animation.

Fade in each word

<SplitText
  onSplit={({ words }) => {
    animate(
      words,
      { opacity: [0, 1], y: [20, 0] },
      { delay: stagger(0.05), duration: 0.5 }
    );
  }}
>
  <p>Fade in each word</p>
</SplitText>

Character Reveal

Access chars instead of words to animate each character individually. Use shorter delays for smoother character animations.

Character by character

<SplitText
  onSplit={({ chars }) => {
    animate(
      chars,
      { opacity: [0, 1], scale: [0.5, 1] },
      { delay: stagger(0.02), duration: 0.3 }
    );
  }}
>
  <p>Character by character</p>
</SplitText>

Emoji Support

Fetta correctly handles compound emojis like family groups, flags, and skin tone modifiers โ€” each emoji is treated as a single character.

Family: ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ Flag: ๐Ÿ‡ฏ๐Ÿ‡ต Skin: ๐Ÿ‘‹๐Ÿฝ

<SplitText
  onSplit={({ chars }) => {
    animate(
      chars,
      { opacity: [0, 1], scale: [0.5, 1] },
      { delay: stagger(0.05), duration: 0.3 }
    );
  }}
>
  <p>Family: ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ Flag: ๐Ÿ‡ฏ๐Ÿ‡ต Skin: ๐Ÿ‘‹๐Ÿฝ</p>
</SplitText>

Nested Elements

Fetta preserves inline HTML elements like <a>, <em>, and <strong> when splitting text. All attributes (href, class, id, data-*, etc.) are maintained on the split output.

Click this link or see emphasized and bold text

<SplitText
  onSplit={({ chars }) => {
    animate(
      chars,
      { opacity: [0, 1], y: [10, 0] },
      { delay: stagger(0.02), duration: 0.3 }
    );
  }}
>
  <p>
    Click <a href="#">this link</a> or see <em>emphasized</em> and <strong>bold</strong> text
  </p>
</SplitText>

Line by Line

Lines are detected based on actual rendered positions, so this works with any text that wraps naturally. Great for paragraphs and longer content.

This paragraph animates line by line. Each line slides in from the left.

<SplitText
  onSplit={({ lines }) => {
    animate(
      lines,
      { opacity: [0, 1], x: [-20, 0] },
      { delay: stagger(0.1), duration: 0.6 }
    );
  }}
>
  <p>
    This paragraph animates line by line.
    Each line slides in from the left.
  </p>
</SplitText>

Masked Line Reveal

Use the mask option to wrap elements in a clipping container with overflow: clip. This creates clean reveal animations where content slides into view from outside its bounds.

Each line reveals from below with a clipping mask for a clean effect.

<SplitText
  onSplit={({ lines }) => {
    animate(
      lines,
      { y: ["100%", "0%"] },
      { delay: stagger(0.1), duration: 0.5 }
    );
  }}
  options={{ type: "lines", mask: "lines" }}
>
  <p>Each line reveals from below with a clipping mask.</p>
</SplitText>

Responsive Split

Enable autoSplit to automatically re-split text when the container resizes. Lines are recalculated to match the new width.

If you want to re-trigger animations on resize, use the onResize callback.

This text reflows naturally at any width, with lines recalculated on resize.

<SplitText
  autoSplit
  onSplit={({ lines }) => {
    animate(
      lines,
      { opacity: [0, 1], y: [12, 0] },
      { delay: stagger(0.08), duration: 0.4 }
    );
  }}
>
  <p>This text reflows naturally at any width, with lines recalculated on resize.</p>
</SplitText>

With Auto-Revert

Set revertOnComplete to automatically restore the original HTML when the animation finishes. Return your animation from the onSplit callback - Fetta handles extracting the .finished promise.

Animating

Auto-revert after animation

<SplitText
  onSplit={({ chars }) =>
    animate(chars, { opacity: [0, 1], y: [10, 0] }, { delay: stagger(0.02), duration: 0.3 })
  }
  revertOnComplete
>
  <p>Auto-revert after animation</p>
</SplitText>

Scroll-Triggered

Use inView to detect when the element enters the viewport, and onInView to trigger the animation. Set elements to invisible in onSplit so they're hidden until the scroll trigger fires. Use onLeaveView to reset styles when scrolling away.

Scroll down

Reveals on scroll

<SplitText
  onSplit={({ words }) => {
    words.forEach(w => {
      w.style.opacity = '0';
      w.style.transform = 'translateY(30px)';
    });
  }}
  inView={{ amount: 0.5 }}
  onInView={({ words }) =>
    animate(words, { opacity: [0, 1], y: [30, 0] }, { delay: stagger(0.03) })
  }
  onLeaveView={({ words }) => {
    words.forEach(w => {
      w.style.opacity = '0';
      w.style.transform = 'translateY(30px)';
    });
  }}
>
  <p>Reveals on scroll</p>
</SplitText>

Scroll-Driven

Link animations to scroll progress using Motion's scroll() function. The animation plays as the target element moves through the viewport.

Scroll to animate

Each word reveals as you scroll through this container

import { scroll, animate } from 'motion';

const targetRef = useRef<HTMLDivElement>(null);

<div ref={targetRef}>
  <SplitText
    onSplit={({ words }) => {
      const target = targetRef.current;
      if (!target) return;

      const animation = animate(
        words.map((word, i) => [
          word,
          { opacity: [0, 1], y: [20, 0] },
          { duration: 0.5, at: i * 0.1, ease: 'linear' }
        ])
      );

      scroll(animation, { target, offset: ['start 85%', 'start 20%'] });
    }}
    options={{ type: 'words' }}
  >
    <p>Each word reveals as you scroll</p>
  </SplitText>
</div>

On this page