RichText

Renders mixed-style text. Style is defined as a tree of TextSpan objects, and children inherit and can override the parent span's style, so you can nest spans to build up complex formatting.

0.0s / 4.0s

Usage

import { createScene, RichText } from '@motion-script/core';

export default createScene(function* (stage) {
  stage.add(
    <RichText
      fontSize={96}
      fill="white"
      spans={[
        { text: 'Motion ' },
        { text: 'Script', fill: '#6990DD', fontWeight: 800 },
      ]}
    />
  );
});

Props

PropTypeDefaultDescription
spansTextSpan | TextSpan[][]Root span tree
fontSizenumber16Default font size in pixels
fontFamilystring'Inter'Default font family
fontWeightnumber400Default font weight (100–900)
fontStyle'normal' | 'italic''normal'Default font style
letterSpacingnumber0Default letter spacing in pixels
lineHeightnumber1.2Line height multiplier
textAlign'left' | 'right' | 'center' | 'justify' | 'start' | 'end''center'Text alignment

Size defaults

RichText defaults to width: 'hug', height: 'hug'.

TextSpan interface

Each span can carry its own style. Unset fields inherit from the parent span (or the node's top-level defaults).

interface TextSpan {
  text?: string;
  fontFamily?: string;
  fontSize?: number;
  fontWeight?: number;
  fontStyle?: 'normal' | 'italic';
  letterSpacing?: number;
  fill?: FillProp | FillProp[];
  stroke?: StrokeProp | StrokeProp[];
  children?: TextSpan[];  // nested spans that inherit this span's styles
}

Nested spans

Children inherit their parent span's styles and can override selectively:

<RichText
  fontSize={32}
  fill="white"
  spans={[
    {
      text: 'Start',
      fontWeight: 700,
      children: [
        { text: ' — inherits bold' },
        { text: ' override', fill: '#E8617C' },
      ],
    },
  ]}
/>

Animating

Animate any top-level prop with .to(). To animate individual span styles, reassign spans inside a tween:

import { createScene, RichText, createRef, tween, easeOut } from '@motion-script/core';

export default createScene(function* (stage) {
  const label = createRef<RichText>();

  stage.add(
    <RichText
      ref={label}
      fontSize={96}
      fill="white"
      spans={[
        { text: 'Motion ', fontWeight: 400 },
        { text: 'Script', fontWeight: 800, fill: '#6990DD' },
      ]}
    />
  );

  // Animate font size
  yield* label().to({ fontSize: 120 }, 0.6, easeOut);

  // Cross-fade the bold span from blue to pink
  yield* tween(0.8, (t) => {
    label().set({
      spans: [
        { text: 'Motion ', fontWeight: 400 },
        { text: 'Script', fontWeight: 800, fill: `hsl(${222 + t * 126}, 68%, 64%)` },
      ],
    });
  });
});

Draw-on animation

Like Text, animating end from 0 to 1 traces the text outline:

const label = createRef<RichText>();
stage.add(
  <RichText
    ref={label}
    fontSize={64}
    end={0}
    spans={[{ text: 'Motion ', fill: 'white' }, { text: 'Script', fill: '#6990DD', fontWeight: 800 }]}
  />
);

yield* label().to({ end: 1 }, 1.5);

Notes

  • Spans without a text field act as purely stylistic wrappers for their children.
  • fill and stroke on a span follow the same syntax as shape node fills: CSS strings, gradient objects, or arrays for layering.
  • RichText does not support word wrapping; use Text with wrap: true for that.