Sora UI

Text Effect

Preset-driven staggered text reveals with blur, fade, slide, and scale animations, powered by Motion. React component.

Made by Axyl

Preset-driven staggered text reveals — blur, fade, slide, and scale animations on words and characters.

Loading...

Installation

File Structure

text-effect.tsx

Usage

Scroll-triggered headline — set scrollTrigger (built-in useInView):

import { TextEffect } from '@/components/sora-ui/texts/text-effect';

export default function HeroTitle() {
  return (
    <TextEffect
      as="h1"
      className="font-bold text-4xl tracking-tight"
      preset="fade-in-blur"
      per="word"
      scrollTrigger
    >
      Animation should not be a black box.
    </TextEffect>
  );
}

Animate immediately on mount (default) or toggle with trigger:

'use client';

import { useState } from 'react';
import { TextEffect } from '@/components/sora-ui/texts/text-effect';

export default function LiveLabel() {
  const [visible, setVisible] = useState(true);

  return (
    <button onClick={() => setVisible((current) => !current)} type="button">
      <TextEffect preset="slide" per="char" trigger={visible}>
        {visible ? 'Hide me' : 'Show me'}
      </TextEffect>
    </button>
  );
}

Use blurRevealItemVariants() when you need the old blur-reveal tuning (blur, yOffset):

import {
  blurRevealItemVariants,
  TextEffect,
} from '@/components/sora-ui/texts/text-effect';

<TextEffect
  scrollTrigger
  per="word"
  variants={{ item: blurRevealItemVariants({ blur: 4, yOffset: 12 }) }}
>
  Hello world
</TextEffect>

Props

PropTypeDefault
children?
string
-
as?
keyof JSX.IntrinsicElements
"p"
preset?
"blur" | "fade-in-blur" | "fade" | "slide" | "scale"
"fade"
per?
"word" | "char" | "line"
"word"
scrollTrigger?
boolean
false
once?
boolean
true
viewportMargin?
string
"0px 0px -10% 0px"
trigger?
boolean
true
delay?
number
0
speedReveal?
number
1
speedSegment?
number
1
variants?
{ container?: Variants; item?: Variants }
-
containerTransition?
Transition
-
segmentTransition?
Transition
-
className?
string
-
segmentWrapperClassName?
string
-

When to Use

Use TextEffect for hero headings, section titles, or any large display text where an entrance animation emphasizes the content on first impression. The scrollTrigger mode works well for below-the-fold content in marketing pages, triggering the reveal as each section scrolls into view.

Use per="word" for phrases and headings; switch to per="char" for short labels where individual character timing creates a typewriter feel.

Accessibility

The text content is always present in the DOM — animation is purely visual opacity and transform. Screen readers announce the full text immediately, regardless of animation state. When prefers-reduced-motion: reduce is set, Motion skips to the final visible state so the content appears instantly without any transition.

Credits

Inspired by motion-primitives.

Built by Axyl. A motion-first component registry for React.

Last updated: 7/26/2026