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
| Prop | Type | Default | Description |
|---|---|---|---|
spans | TextSpan | TextSpan[] | [] | Root span tree |
fontSize | number | 16 | Default font size in pixels |
fontFamily | string | 'Inter' | Default font family |
fontWeight | number | 400 | Default font weight (100–900) |
fontStyle | 'normal' | 'italic' | 'normal' | Default font style |
letterSpacing | number | 0 | Default letter spacing in pixels |
lineHeight | number | 1.2 | Line 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
textfield act as purely stylistic wrappers for theirchildren. fillandstrokeon a span follow the same syntax as shape node fills: CSS strings, gradient objects, or arrays for layering.RichTextdoes not support word wrapping; useTextwithwrap: truefor that.