← Blog

Typewriter effect in Remotion

Build a typewriter effect in Remotion — text revealed from the frame number with slice(), a caret that holds solid while typing and blinks when idle, and an eased typing rate that reads as a person rather than a machine.

A typewriter effect in Remotion is text.slice(0, count), where count comes from the frame number through interpolate(). Ease the count instead of typing at a constant rate, keep the caret solid while it types, and blink it only once the line is done.

The minimum

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

export const Typewriter = ({ text }: { text: string }) => {
  const frame = useCurrentFrame();
  const start = 10;
  const end = start + text.length * 2; // ~15 characters a second at 30fps

  const count = Math.floor(
    interpolate(frame, [start, end], [0, text.length], {
      extrapolateLeft: "clamp",
      extrapolateRight: "clamp",
    }),
  );

  return (
    <AbsoluteFill style={{ alignItems: "center", justifyContent: "center" }}>
      <p style={{ fontSize: 64, fontFamily: "monospace", whiteSpace: "pre" }}>
        {text.slice(0, count)}
        <span style={{ opacity: 1 }}>▍</span>
      </p>
    </AbsoluteFill>
  );
};

Two details that matter:

  • Math.floor, then slice. A character is either there or it is not. Do not fade letters in; a half-transparent glyph reads as a rendering fault.
  • whiteSpace: "pre". Without it, a trailing space collapses and the caret jumps back a character every time a word ends.

Make the rate human

Nobody types at a constant rate. Measured on a real recording, typing accelerates through the first word and settles over the last few characters — an ease-in-out sine fits it closely. Pass that as the easing:

import { Easing, interpolate } from "remotion";

const count = Math.floor(
  interpolate(frame, [start, end], [0, text.length], {
    easing: Easing.inOut(Easing.sin),
    extrapolateLeft: "clamp",
    extrapolateRight: "clamp",
  }),
);

A bonus of the eased count: any length of copy finishes in the same window, so a longer line does not push the rest of your timeline back.

The caret: solid while typing, blinking when idle

const blinkOn = Math.floor((frame - end) / 15) % 2 === 0; // half a second at 30fps
const caretOpacity = frame < end || blinkOn ? 1 : 0;

A caret that blinks mid-word makes the whole line look like it is flickering. Operating systems hold it solid while keys are pressed; do the same. And compute the blink from the frame — a CSS @keyframes blink runs on wall-clock time, and a render does not.

If the camera follows the text

Pin a camera to the character count and it lurches once per keystroke. Pin it to the continuous curve underneath — the same interpolate() without the Math.floor — and it glides, while the caret hops onto its mark each frame.

Ready-made typing components

snapcn has three components built on this, each installed as source:

ComponentWhat it does
Type MorphA headline types under a caret, drops its lead in one frame, morphs letter by letter, ends on a colour flood
Prompt SendA prompt box types a brief on an eased curve while the camera rides the caret, then a pointer sends it
Prompt ZoomA prompt types itself and, mid-sentence, cuts in a single frame into the caret and keeps going
npx shadcn@latest add @snapcn/type-morph

For a terminal rather than a headline, see terminal and code-typing animation in Remotion.

Next: kinetic typography in Remotion.

FAQ

How do you make a typewriter effect in Remotion?

Map the frame number to a character count with interpolate(), clamp it, round it down, and render text.slice(0, count). Put a caret element after the text and drive its opacity from the frame as well.

Why does my Remotion typing animation look robotic?

A constant rate is the giveaway. People accelerate into a sentence and slow down at the end, so ease the character count, for example with Easing.inOut(Easing.sin), instead of adding a fixed number of characters per frame.

Should the caret blink while text is typing?

No. On real systems the caret stays solid while keys are pressed and only blinks once typing stops. Blinking mid-word makes the text look like it is flickering.

Can I use a CSS animation for the caret blink?

No. A Remotion render does not run on wall-clock time, so a CSS blink will not line up with the frames. Compute the blink from the frame number.