shadimage
Remocn
componentanimated-bar-chartsource: ok · refreshed

Animated Bar Chart

Bars spring up from the baseline in a staggered cascade.

npx shadcn add "https://www.remocn.dev/r/animated-bar-chart.json"
free

No selection tags yet — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light
remotion

Files1 · source cached 10/8/2026, 9:36:24 AM

registry/remocn/animated-bar-chart/index.tsx
"use client";

import { spring, useCurrentFrame, useVideoConfig } from "remotion";

export interface AnimatedBarChartProps {
  data?: number[];
  labels?: string[];
  width?: number;
  height?: number;
  barColor?: string;
  gap?: number;
  staggerFrames?: number;
  speed?: number;
  className?: string;
}

export function AnimatedBarChart({
  data = [35, 60, 45, 80, 55, 70, 90, 65],
  labels,
  width = 1000,
  height = 500,
  barColor = "#0ea5e9",
  gap = 16,
  staggerFrames = 6,
  speed = 1,
  className,
}: AnimatedBarChartProps) {
  const frame = useCurrentFrame() * speed;
  const { fps } = useVideoConfig();

  const padding = 60;
  const labelSpace = labels ? 40 : 0;
  const innerWidth = width - padding * 2;
  const innerHeight = height - padding * 2 - labelSpace;
  const max = Math.max(...data);
  const barWidth = (innerWidth - gap * (data.length - 1)) / data.length;
  const baseY = padding + innerHeight;

  return (
    <div
      className={className}
      style={{
        position: "absolute",
        inset: 0,
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
        fontFamily:
          "var(--font-geist-sans), -apple-system, BlinkMacSystemFont, sans-serif",
      }}
    >
      <svg width={width} height={height} viewBox={`0 0 ${width} ${height}`}>
        {/* Baseline */}
        <line
          x1={padding}
          x2={padding + innerWidth}
          y1={baseY}
          y2={baseY}
          stroke="#27272a"
          strokeWidth={2}
        />

        {data.map((value, index) => {
          const targetHeight = (value / max) * innerHeight;
          const x = padding + index * (barWidth + gap);
          const y = baseY - targetHeight;

          const scaleY = spring({
            frame: frame - index * staggerFrames,
            fps,
            config: { damping: 12, stiffness: 100, mass: 0.8 },
            from: 0,
            to: 1,
          });

          return (
            <g key={index}>
              <rect
                x={x}
                y={y}
                width={barWidth}
                height={targetHeight}
                rx={6}
                fill={barColor}
                style={{
                  scale: `1 ${scaleY}`,
                  transformOrigin: `${x + barWidth / 2}px ${baseY}px`,
                  transformBox: "fill-box",
                  filter: `drop-shadow(0 4px 16px ${barColor}55)`,
                }}
              />
              {labels?.[index] && (
                <text
                  x={x + barWidth / 2}
                  y={baseY + 28}
                  fill="#a1a1aa"
                  fontSize={16}
                  textAnchor="middle"
                  style={{
                    opacity: scaleY,
                    fontFamily:
                      "var(--font-geist-sans), -apple-system, BlinkMacSystemFont, sans-serif",
                  }}
                >
                  {labels[index]}
                </text>
              )}
            </g>
          );
        })}
      </svg>
    </div>
  );
}

More from Remocn

Browse all components in this library →