← Catalog
Edit Props (live)
JSON
TEXT
config
config
config
Source
167 lines// Animated Bar Chart — bars grow in (spring, staggered) from a data array.
// Native Remotion + SVG-free flexbox, frame-driven. Data-viz is a category
// fixed-primitive tools can't easily template; this turns a JSON array of
// { label, value } into a clean animated chart.
import {
AbsoluteFill,
interpolate,
spring,
useCurrentFrame,
useVideoConfig,
} from 'remotion';
import { loadFont as loadGrotesk } from '@remotion/google-fonts/SpaceGrotesk';
const { fontFamily: GROTESK } = loadGrotesk();
type Datum = { label: string; value: number };
export type AnimatedBarChartProps = {
data: Datum[] | string;
title: string;
barColor: string;
textColor: string;
backgroundColor: string;
};
function parseData(data: Datum[] | string): Datum[] {
let raw: unknown = data;
if (typeof data === 'string') {
try {
raw = JSON.parse(
data.replace(/^```(?:json)?\s*/i, '').replace(/\s*```$/, '')
);
} catch {
raw = [];
}
}
if (!Array.isArray(raw)) return [];
return raw.slice(0, 8).map((d) => ({
label: String((d as Datum)?.label ?? ''),
value: Number((d as Datum)?.value) || 0,
}));
}
export default function AnimatedBarChart({
data = [
{ label: 'Jan', value: 32 },
{ label: 'Feb', value: 54 },
{ label: 'Mar', value: 71 },
{ label: 'Apr', value: 48 },
{ label: 'May', value: 89 },
],
title = '',
barColor = '#5b3df5',
textColor = '#ffffff',
backgroundColor = '#0a0a14',
}: Partial<AnimatedBarChartProps>) {
const frame = useCurrentFrame();
const { fps, durationInFrames, width } = useVideoConfig();
// Resolution-independent: px authored at a 1080-wide reference, scaled
// by k. k=1 at 1080 (identical output), scales cleanly at any width.
const k = width / 1080;
const bars = parseData(data);
const max = Math.max(1, ...bars.map((b) => b.value));
const exit = interpolate(
frame,
[durationInFrames - 10, durationInFrames - 1],
[1, 0],
{ extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }
);
return (
<AbsoluteFill
style={{
backgroundColor,
fontFamily: GROTESK,
opacity: exit,
padding: `${120 * k}px ${80 * k}px ${100 * k}px`,
boxSizing: 'border-box',
}}
>
{title ? (
<div
style={{
color: textColor,
fontSize: 64 * k,
fontWeight: 800,
letterSpacing: '-0.02em',
marginBottom: 48 * k,
opacity: interpolate(frame, [0, 12], [0, 1], {
extrapolateRight: 'clamp',
}),
}}
>
{title}
</div>
) : null}
<div
style={{
flex: 1,
display: 'flex',
alignItems: 'flex-end',
justifyContent: 'space-between',
gap: 28 * k,
}}
>
{bars.map((b, i) => {
const grow = spring({
frame: frame - 8 - i * 4,
fps,
config: { damping: 15, stiffness: 90 },
durationInFrames: 26,
});
const hPct = (b.value / max) * 100 * grow;
return (
<div
key={i}
style={{
flex: 1,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
height: '100%',
justifyContent: 'flex-end',
}}
>
<div
style={{
color: textColor,
fontSize: 38 * k,
fontWeight: 700,
opacity: grow,
marginBottom: 12 * k,
fontVariantNumeric: 'tabular-nums',
}}
>
{Math.round(b.value * grow)}
</div>
<div
style={{
width: '100%',
height: `${hPct}%`,
background: barColor,
borderRadius: `${10 * k}px ${10 * k}px 0 0`,
}}
/>
<div
style={{
color: textColor,
opacity: 0.7,
fontSize: 34 * k,
fontWeight: 500,
marginTop: 16 * k,
}}
>
{b.label}
</div>
</div>
);
})}
</div>
</AbsoluteFill>
);
}
Schema (Zod)
import { z } from 'zod';
const datumSchema = z.object({
label: z.string(),
value: z.number(),
});
export const Schema = z.object({
data: z
.union([z.array(datumSchema), z.string()])
.default([
{ label: 'Jan', value: 32 },
{ label: 'Feb', value: 54 },
{ label: 'Mar', value: 71 },
{ label: 'Apr', value: 48 },
{ label: 'May', value: 89 },
])
.describe('$port:json Array of { label, value } (up to 8 bars)'),
title: z.string().default('').describe('$port:text Chart title (optional)'),
barColor: z.string().default('#5b3df5').describe('$style:color Bar color'),
textColor: z.string().default('#ffffff').describe('$style:color Text color'),
backgroundColor: z
.string()
.default('#0a0a14')
.describe('$style:color Background color'),
});
Animated Bar Chart
animated_bar_chartBars grow in (spring, staggered) from a JSON array of { label, value }, each counting up to its value. Drop-in data-viz for any comparison, ranking, or trend.
Workflow Inputs (2)
- Array of { label, value } (up to 8 bars)
dataJSON - Chart title (optional)
titleTEXT
Config Fields (3)
barColortextColorbackgroundColor
Meta
- Updated
- 7/10/2026