SVG Animation in Remotion: The Complete Guide
SVG Animation in Remotion: The Complete Guide
SVG is the most versatile format in the web developer’s animation toolkit. It scales infinitely without pixelation, it is just markup, and — crucially for Remotion — it is native React. Every <svg>, <path>, <circle>, and <g> element is a first-class React component, which means you can bind Remotion’s frame-driven values directly to SVG attributes with no third-party library required.
This guide covers everything from the classic stroke-drawing trick to animated data charts, icon sequences, and performance considerations for production renders.
Why SVG Works So Well With Remotion
In traditional video tools, animating a vector illustration means keyframing inside a proprietary timeline. You export, re-import, and lose the connection to your data. In Remotion, SVG lives in your component tree. Your animation logic is TypeScript. Your data is just a JavaScript object passed as props.
This architecture has three practical benefits:
- Data-driven by default. A bar chart, a route map, a progress ring — the shape is always a function of data, not a hand-drawn keyframe.
- Version-controllable. SVG markup and animation parameters are text files. You can diff, branch, and code-review every change.
- Infinitely parameterizable. Swap colors, text, shapes, or entire datasets without touching the timeline.
Rendering SVG Directly in a Remotion Component
The simplest starting point: write SVG inline inside your composition.
import { useCurrentFrame, interpolate } from "remotion";
export const CircleGrow: React.FC = () => {
const frame = useCurrentFrame();
const radius = interpolate(frame, [0, 30], [0, 80], {
extrapolateRight: "clamp",
});
return (
<svg width={1920} height={1080} viewBox="0 0 1920 1080">
<circle cx={960} cy={540} r={radius} fill="#6366f1" />
</svg>
);
};
useCurrentFrame() returns the current frame number, starting at zero. interpolate() maps that frame number to any numeric range — here, the circle radius grows from 0 to 80 over 30 frames. The extrapolateRight: "clamp" option stops the value from exceeding 80 once you pass frame 30.
This pattern — frame → interpolate → SVG attribute — is the foundation of all Remotion SVG animation.
The Stroke-Dashoffset Path Drawing Trick
The most iconic SVG animation effect is a path that appears to draw itself onto the screen. It relies on two SVG presentation attributes: stroke-dasharray and stroke-dashoffset.
stroke-dasharray sets the length of the dash pattern on a stroke. If you set it equal to the total length of the path, you get a single dash that covers the entire path. stroke-dashoffset shifts that dash along the path. By animating stroke-dashoffset from the full path length down to zero, the path appears to draw itself.
Here is a complete working example:
import { useCurrentFrame, interpolate } from "remotion";
// Measure this value with path.getTotalLength() in a browser
const PATH_LENGTH = 512;
export const DrawingPath: React.FC = () => {
const frame = useCurrentFrame();
const dashOffset = interpolate(frame, [0, 60], [PATH_LENGTH, 0], {
extrapolateRight: "clamp",
});
return (
<svg width={1920} height={1080} viewBox="0 0 800 400">
<path
d="M 100 200 C 200 50, 600 350, 700 200"
fill="none"
stroke="#f43f5e"
strokeWidth={4}
strokeDasharray={PATH_LENGTH}
strokeDashoffset={dashOffset}
/>
</svg>
);
};
The path draws itself over 60 frames (2 seconds at 30 fps). To apply an easing curve, pass an easing option to interpolate:
import { Easing } from "remotion";
const dashOffset = interpolate(frame, [0, 60], [PATH_LENGTH, 0], {
extrapolateRight: "clamp",
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
});
Finding the Path Length
The challenge with this technique is knowing PATH_LENGTH in advance. The cleanest approach for production work is to measure it once during development:
# Paste your SVG path into a browser console:
const p = document.createElementNS("http://www.w3.org/2000/svg", "path");
p.setAttribute("d", "your-d-attribute-here");
document.body.appendChild(p);
console.log(p.getTotalLength()); // e.g. 512.34
Round up and store it as a constant in your component file.
Animating SVG Transforms
SVG elements have a transform attribute that accepts translate(), rotate(), scale(), and matrix(). In React, you use the transform prop (as a string) or the individual transform presentation attributes.
Rotation
const rotation = interpolate(frame, [0, 60], [0, 360], {
extrapolateRight: "clamp",
});
<g transform={`rotate(${rotation}, 960, 540)`}>
{/* child elements rotate around the point 960, 540 */}
</g>
Scale with a transform-origin equivalent
SVG does not support transform-origin the same way CSS does. The standard workaround is to translate to the origin, scale, then translate back:
const scale = interpolate(frame, [0, 30], [0, 1], {
extrapolateRight: "clamp",
easing: Easing.out(Easing.back(1.7)),
});
const cx = 960;
const cy = 540;
<g transform={`translate(${cx}, ${cy}) scale(${scale}) translate(${-cx}, ${-cy})`}>
<circle cx={cx} cy={cy} r={80} fill="#6366f1" />
</g>
The Easing.back() function adds an overshoot — the shape briefly scales past 1.0 before settling, which gives the entrance a lively spring-like feel without using the full spring physics system.
Combined Transform with spring()
For even more organic motion, combine SVG transforms with Remotion’s spring() function:
import { spring, useCurrentFrame, useVideoConfig } from "remotion";
export const BounceIn: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const scale = spring({
frame,
fps,
config: { stiffness: 200, damping: 15 },
from: 0,
to: 1,
});
return (
<svg width={1920} height={1080} viewBox="0 0 1920 1080">
<g transform={`translate(960, 540) scale(${scale}) translate(-960, -540)`}>
<rect x={860} y={490} width={200} height={100} rx={12} fill="#6366f1" />
</g>
</svg>
);
};
Building Animated Data Charts
SVG is ideal for data visualization in Remotion because shapes are mathematical functions of data. Here is a horizontal bar chart that animates each bar entering from the left:
import { useCurrentFrame, interpolate, Sequence } from "remotion";
const DATA = [
{ label: "Q1", value: 420 },
{ label: "Q2", value: 680 },
{ label: "Q3", value: 540 },
{ label: "Q4", value: 810 },
];
const MAX_VALUE = Math.max(...DATA.map((d) => d.value));
const BAR_HEIGHT = 60;
const BAR_GAP = 20;
const MAX_WIDTH = 700;
const Bar: React.FC<{ value: number; index: number; label: string }> = ({
value,
index,
label,
}) => {
const frame = useCurrentFrame();
const targetWidth = (value / MAX_VALUE) * MAX_WIDTH;
const width = interpolate(frame, [0, 40], [0, targetWidth], {
extrapolateRight: "clamp",
easing: Easing.out(Easing.cubic),
});
const y = index * (BAR_HEIGHT + BAR_GAP);
return (
<g>
<rect x={150} y={y} width={width} height={BAR_HEIGHT} rx={6} fill="#6366f1" />
<text x={140} y={y + BAR_HEIGHT / 2} textAnchor="end" dominantBaseline="middle" fill="#fff" fontSize={20}>
{label}
</text>
<text x={160 + width} y={y + BAR_HEIGHT / 2} dominantBaseline="middle" fill="#a5b4fc" fontSize={18}>
{value}
</text>
</g>
);
};
export const BarChart: React.FC = () => {
return (
<svg width={1920} height={1080} viewBox="0 0 1000 400">
{DATA.map((d, i) => (
<Sequence key={d.label} from={i * 10}>
<Bar value={d.value} index={i} label={d.label} />
</Sequence>
))}
</svg>
);
};
Each bar is wrapped in a <Sequence from={i * 10}>, which delays the start of that bar’s animation by 10 frames per bar. useCurrentFrame() inside each <Bar> returns a frame count relative to the sequence start — so the interpolation always reads [0, 40] regardless of when the sequence begins.
Using <Img> for External SVG Files
Sometimes you have a complex SVG icon, logo, or illustration that you want to load from a file rather than inline in JSX. Remotion provides the <Img> component, which waits for the asset to fully load before the frame renders — preventing frames from rendering with missing assets.
import { Img, staticFile } from "remotion";
export const LogoReveal: React.FC = () => {
return (
<Img
src={staticFile("logo.svg")}
style={{ width: 400, height: 200 }}
/>
);
};
Place logo.svg in the public/ folder of your Remotion project. staticFile() resolves the correct path whether you are previewing locally or rendering in the cloud.
The limitation of <Img> is that you cannot manipulate the internal SVG DOM — you cannot animate individual paths within the loaded file. If you need to animate internal elements, inline the SVG markup in your JSX instead.
Complex Icon Animation Example
Here is a checkmark icon that draws itself and then fills with color — a common pattern for success states in SaaS product videos:
import { useCurrentFrame, interpolate, spring, useVideoConfig } from "remotion";
const CHECKMARK_LENGTH = 45; // measure with getTotalLength()
export const AnimatedCheckmark: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
// Circle scales in first
const circleScale = spring({
frame,
fps,
config: { stiffness: 180, damping: 14 },
from: 0,
to: 1,
durationInFrames: 25,
});
// Checkmark draws after circle appears
const dashOffset = interpolate(frame, [20, 50], [CHECKMARK_LENGTH, 0], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
return (
<svg width={1920} height={1080} viewBox="0 0 200 200">
<g transform="translate(100, 100)">
<circle
r={70}
fill="#22c55e"
transform={`scale(${circleScale})`}
/>
<path
d="M -25 0 L -8 18 L 30 -20"
fill="none"
stroke="white"
strokeWidth={6}
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray={CHECKMARK_LENGTH}
strokeDashoffset={dashOffset}
/>
</g>
</svg>
);
};
The circle scales in using spring physics (frames 0–25), and the checkmark stroke draws from frame 20 to frame 50. The overlap (frames 20–25) makes the sequence feel continuous rather than a two-step process.
Performance Tips for SVG Animation
1. Keep SVG complexity low
Complex paths with hundreds of nodes render at every frame. Simplify paths in your SVG editor before bringing them into Remotion. The d attribute of a simplified path is shorter, parses faster, and the resulting SVG paints faster per frame.
2. Avoid filters for large areas
SVG filters (<feGaussianBlur>, <feDropShadow>) are expensive to render, especially at 1080p or 4K. Use CSS filter or Remotion’s built-in blur where possible, and test render times early if you need SVG filters on large elements.
3. Use will-change: transform carefully
In a browser preview, will-change: transform on SVG elements promotes them to a compositor layer. In a headless render (Chrome-based), this can help — but it is not a substitute for reducing actual geometric complexity.
4. Profile with --log=verbose
When rendering with the Remotion CLI, --log=verbose prints per-frame render times. If certain frames spike, that is usually an SVG with high complexity or an expensive filter being applied at that frame.
5. Pre-calculate path lengths at build time
Rather than measuring getTotalLength() manually in the browser, write a small Node.js script using svgpath or the @svgdotjs/svg.js library to compute lengths at build time and embed them as constants. This makes your codebase self-documenting and avoids manual measurement errors.
Putting It All Together
SVG animation in Remotion follows a single mental model: frame → numeric value → SVG attribute. Every technique in this guide — path drawing, transforms, data charts, icon sequences — is a variation on that pattern. useCurrentFrame() gives you the raw frame count. interpolate() or spring() converts it to a meaningful number. The SVG attribute consumes that number.
The power emerges when you combine these building blocks: a <Sequence> to stagger timing, a spring() for physics-based entrance, stroke-dashoffset for a path reveal, and React’s map() to generate data-driven shapes — all in the same component tree, all version-controlled, all parameterizable.
Get Production-Ready SVG Animations Faster
Building SVG animations from scratch is educational, but production deadlines are real. RenderComp offers a library of Remotion templates with polished SVG animation components — data charts, icon reveals, path-drawing intros, and more — all written in clean TypeScript and ready to drop into your project. Browse the template library at rendercomp.com and ship faster without sacrificing quality.
FAQ
Q1: Does Remotion support SVG animation libraries like GSAP or Anime.js?
Remotion renders frame by frame in a headless browser, so timeline-based JavaScript animation libraries that rely on requestAnimationFrame will not work as expected — they won’t advance between frames. The correct approach is to drive all animation state from useCurrentFrame() directly, using interpolate() and spring(). Remotion’s model is fundamentally different: instead of the animation library controlling time, you control time by controlling the frame number.
Q2: How do I get the length of an SVG path for the stroke-dashoffset trick?
Open the SVG in a browser and run document.querySelector('path').getTotalLength() in the DevTools console. Alternatively, you can use a Node.js script with a library like svgpath at build time. The value is the total arc length of the path in SVG user units.
Q3: Can I animate an SVG loaded from a file (not inlined)?
If you load an SVG with Remotion’s <Img> component, you cannot animate its internal elements — it is treated as an opaque image. To animate internal paths, circles, or groups, inline the SVG markup directly in your JSX component.
Q4: Can I use CSS animations on SVG elements inside Remotion?
CSS @keyframes animations will not work reliably in Remotion because the renderer does not advance CSS animation state between frames — it renders each frame as a static snapshot. Drive all animation with useCurrentFrame() and React state, not CSS keyframes.
Q5: What is the best way to animate a complex SVG illustration with many layers?
Group related elements in <g> tags, then animate the <g> transform for group-level motion (position, scale, rotation). Animate individual elements only when they need independent motion. This approach keeps your animation logic organized and reduces the number of interpolated values computed per frame.
Q6: Can I render SVG-based animations at 4K in Remotion?
Yes. SVG is resolution-independent, so it renders crisply at any output size. Set your composition width and height to 3840×2160 (4K) and ensure your SVG viewBox is defined — Remotion will scale the composition to the output resolution automatically. Render times will be longer at 4K, but visual quality is perfect.
Q7: How do I animate text along an SVG path?
Use the SVG <textPath> element with an <animateMotion> or, better for Remotion, compute the text offset position with interpolate() and apply it via the startOffset attribute of <textPath>. Drive startOffset from useCurrentFrame() to move text along the path in sync with your composition timeline.
Q8: Is it better to use inline SVG or the <Img> tag for icons?
For animated icons, always use inline SVG — you need direct access to the element attributes. For static decorative illustrations where file organization matters more than animation, <Img src={staticFile("...")} /> is cleaner and keeps your component code shorter.
Now available
Get 1,000+ Remotion Templates
Pay once — no subscription. Lifetime updates. TypeScript-first.
View pricing →