Sora UI

Typer

A character-by-character reveal where each glyph ripples through randomized pill, accent, and outline states before settling into plain text. A React component.

Made by Axyl

A stacked headline that cascades in line by line — scroll into view or refresh to replay.

Installation

File Structure

typer.tsx

Usage

import { Typer } from '@/components/sora-ui/texts/typer';

export default function Page() {
  return (
    <Typer
      className="font-semibold text-4xl tracking-tight"
      text="Type that assembles itself."
    />
  );
}

Cascade a stacked block, one line per string:

<Typer
  text={['Type that', 'assembles itself,', 'one glyph at a time.']}
  stagger={0.15}
/>

Narrow the pool of visual states each character rolls through:

<Typer
  text="Only pills and accents."
  variations={['charFill', 'charAccentInverse', 'charBorder']}
/>

Recolor the fills, accent, and borders through the CSS variables:

<Typer
  text="Themed reveal."
  fg="#0b0b0c"
  bg="#ffffff"
  accent="#6d28d9"
  accentInk="#ffffff"
  radius="8px"
/>

Render as a different element and control it imperatively:

const handle = useRef<TyperHandle>(null);

<Typer ref={handle} as="h1" text="Controlled headline." startOnView={false} />
<button onClick={() => handle.current?.replay()}>Replay</button>

Props

PropTypeDefault
text?
string | string[]
-
variations?
TyperVariation[]
all six variations
fps?
number
20
cycles?
number
3
stagger?
number
0.15
startOnView?
boolean
true
fg?
string
theme --foreground
bg?
string
theme --background
accent?
string
"#12a150"
accentInk?
string
"#fcfcfc"
radius?
string
"5px"
as?
React.ElementType
"div"
ref?
Ref<TyperHandle>
-
className?
string
-

Accessibility

The full text is exposed as an aria-label on the root element, and every animated line span is aria-hidden — screen readers announce the plain text, never the per-character state churn.

When prefers-reduced-motion: reduce is set, the reveal loop is skipped entirely: each line mounts fully visible as plain text with no state ripple, matching the CSS reduced-motion fallback. Imperative play() / replay() / out() still resolve but there is no animation to drive.

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

Last updated: 7/31/2026