← Blog

How to animate text in Remotion

Animate text in Remotion with useCurrentFrame(), interpolate() and spring() — a word-by-word reveal from scratch, the three bugs that make scaled type look cheap, and ready-made components.

To animate text in Remotion, turn the frame number into style: useCurrentFrame() gives you the frame, interpolate() or spring() turns it into opacity and transform, and you stagger words by offsetting each one's start frame. Never use CSS transitions — a render does not play in real time.

The minimum: a word-by-word reveal

import { AbsoluteFill, interpolate, useCurrentFrame } from "remotion";

export const WordReveal = ({ text }: { text: string }) => {
  const frame = useCurrentFrame();
  const words = text.split(" ");

  return (
    <AbsoluteFill style={{ alignItems: "center", justifyContent: "center" }}>
      <h1 style={{ fontSize: 80, display: "flex", gap: "0.25em" }}>
        {words.map((word, i) => {
          const start = i * 4; // 4 frames between words
          const progress = interpolate(frame, [start, start + 12], [0, 1], {
            extrapolateLeft: "clamp",
            extrapolateRight: "clamp",
          });
          return (
            <span
              key={i}
              style={{
                opacity: progress,
                transform: `translateY(${(1 - progress) * 0.4}em)`,
              }}
            >
              {word}
            </span>
          );
        })}
      </h1>
    </AbsoluteFill>
  );
};

Three things make this work:

  • Every value is a function of frame. Remotion renders frames in parallel and out of order, so nothing may depend on the previous frame or on time.
  • Always clamp. Without extrapolateRight: "clamp", interpolate() keeps going past 1 and the words drift off forever.
  • Move in em, not pixels. The offset then scales with the font size, and the same component works at 40px and 120px.

For a spring, swap the interpolate() for spring({ frame: frame - start, fps, config: { damping: 200 } }). A damping of 200 settles without bouncing — the right default for type, which looks broken when it overshoots.

Why scaled text looks cheap, and the fixes

Opacity and translate are safe. Scale is where text animation goes wrong, and each of these was measured on rendered frames, not eyeballed.

It looks "stuck": pivot the scale on the baseline

Browsers position glyphs on whole pixels vertically. Scale a line from its centre and the baseline moves, so the text climbs the pixel grid in visible one-pixel jumps. Set transformOrigin to the baseline — measured, not guessed from line-height — and the baseline stops moving. In our tests that took vertical judder from 0.284px to 0.014px and direction reversals from 29 to 0.

It "shakes": use geometricPrecision

At every new size the font hinter re-snaps letter stems to the grid, so the letterforms themselves change shape frame to frame. text-rendering: geometricPrecision turns that off. Shape drift fell from 3.41% to 0.22%. The type looks a touch softer; that is correct, not blur.

It "freezes": go easy on the ease-out

A quint or expo ease-out spends its last third moving less than a pixel per frame. On a 30fps clock that reads as the animation stopping early and then jumping. A cubic or a critically damped spring settles in one or two frames instead of five.

And skip translateZ(0) and backface-visibility: hidden: they rasterise the text into a bitmap, which then scales like one. will-change: transform helps the live Player but hurts the render — gate it on getRemotionEnvironment().isRendering.

Ready-made Remotion text animations

If you would rather not tune this yourself, snapcn ships twelve text components with these fixes already in them. Each installs as source you own:

npx shadcn@latest add @snapcn/text-reveal
ComponentWhat it does
Text RevealLead word zooms close, falls back, the sentence pushes in word by word
Word FlipOne word cycles on a 3D flip inside a typed headline
Text BuildWords enter one at a time and the line reflows to stay centred
Word WheelA column of words spins like a reel and stops on one
Type MorphA headline types itself, then morphs letter by letter
Text SwapThe old line flies past the camera, the new one is behind it
Punch LinesFull-frame title cards that cut hard between beats
Word GatherWords arrive out of order and gather into the sentence
Text SelectA line writes itself and gets selected under a glint
Text RewriteA line is selected and its ending rewritten
Text HighlightA mark wipes a wordmark open, plus span-emphasis presets
Text SwellThe lead word floats toward you, the sentence shoves it aside

Browse them all in the text animations category, or read how to install Remotion components with the shadcn CLI.

FAQ

How do you animate text in Remotion?

Read the current frame with useCurrentFrame(), map it to opacity and transform with interpolate() or spring(), and apply the result as inline style. Split the text into words or letters and offset each one's start frame to stagger them.

Why does scaled text look jittery in a Remotion render?

Browsers snap glyphs to whole pixels vertically and re-hint letter stems at every size. Pivot the scale on the text baseline and set text-rendering to geometricPrecision; both are measurable fixes.

Should I use CSS animations or keyframes in Remotion?

No. Remotion renders frames out of order and in parallel, so every animated value must be a pure function of the frame number. CSS transitions and keyframes run on wall-clock time and will not render correctly.

Are there ready-made Remotion text animations?

Yes. snapcn ships twelve free text animation components for Remotion, installed with npx shadcn@latest add @snapcn/<name>, and the source is copied into your project.