snapcn
97 components
DocsComponentsTemplatesVideo EditorNewMCPNewRemotion StudioNewRoadmapChangelog
GitHub

Star snapcn

Free, open-source components.

Production-ready Remotion animations, transitions and backgrounds — you own the code, free or paid.

Star on GitHubXFollow

46 MIT · 51 Pro · own your code.

© 2026 snapcn

DocsComponentsTemplatesVideo EditorNewMCPNewRemotion StudioNewRoadmapChangelog

Components

Every component in snapcn, ready to install.
Updated just now
AllText & TitlesCaptionsLogosScreens & DevicesCharts & StatsSocial ProofScenesAI Chat Input
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro

Scenes

Status Cycle

A status pill whose label rolls behind a hard clip while its width springs past the target and back, then the field crossfades to a column of chips stepping up from below

Make a video
Type
Animation
Duration
6.6s
Category
Scenes
Source
snapcn

Install

$ npx shadcn@latest add @snapcn/status-cycle

Installation

pnpm
$ pnpm dlx shadcn@latest add @snapcn/status-cycle

Needs an existing Remotion project and a components.json. Two-minute setup if this is your first snapcn component.

How it works

Component code

The exact file shadcn add copies into your project.

Usage

A prefix, a list of statuses and a list of chips. Everything else has a measured default.

// src/Root.tsx
import { Composition } from "remotion";
import { StatusCycle } from "@/components/snap-cn/status-cycle";

const Cycle = () => (
  <StatusCycle
    prefix="snapcn is"
    statuses={["animating", "transitioning", "rendering a scene", "installed"]}
    chips={["text-reveal", "phone-frame", "answer-stream", "word-flip"]}
  />
);

export const RemotionRoot = () => (
  <Composition
    id="StatusCycle"
    component={Cycle}
    durationInFrames={198}
    fps={30}
    width={1280}
    height={720}
  />
);

durationInFrames has to cover the whole arc: the intro, one statusHold per status, the crossfade, and one chipStagger per chip. The default 198 fits the default content with a hold at the end; add 30 frames per extra status and 8 per extra chip.

How the swap works

Three things happen on the same frame, and keeping them on the same frame is most of the effect.

The label rolls. The outgoing label translates up and out through the pill's top edge while the incoming one arrives from below, both hard-clipped by the pill's rounded box. There is no crossfade — the label's ink never dims — and the travel is one pill height, over 0.5s on cubic-bezier(0.45, 0, 0.55, 1): slower than the reference's 8-frame roll, whose middle frame jumps a third of the pill and reads as a flicker at 30fps.

The pill's width springs past its target. Not a spring, though: the curve is cubic-bezier(0.65, 0.2, 0.3, 1.5), and the y2 > 1 control value is the overshoot. A real spring cannot make this shape. One frame into an eleven-frame rise a spring released from rest is already 4.1% of the way there; the reference is at 0.65%, six times flatter, and no damping/stiffness pair fixes that without losing the early peak.

The line re-centres. The lockup is centred as a whole, so a widening pill pushes the prefix left and a narrowing one lets it back. The prefix does not re-animate; it only moves.

The label is laid out at its full final width and centred, and the widening pill reveals it from the middle outward — so during a morph you see the middle of the new label with both ends clipped. That is why the width is its own animated quantity and is never derived from a padded text box.

Content of any length

Every width in the scene is measured at runtime behind delayRender() — the prefix's glyph advances, each status label, each chip — so a longer status or a longer chip simply makes a wider box. Nothing is tuned to one sentence. Every other proportion is a fraction of the composition rather than a pixel, so the scene holds its shape at 720p, 1080p or a vertical crop.

Colour

Leave the colour props off and the scene paints from the design system: primary for the first field, background for the second, foreground for the pill and the ink, and a pale tint of the field for the pill's label. Pass any of them to override.

<StatusCycle
  fieldColor="#0f766e"
  pageColor="#f5f5f4"
  chipFills={["#ffffff", "#e7f5f3", "#0f766e"]}
/>

Overriding the motion

Every curve and duration is a measured default on the exported STATUS_CYCLE_MOTION, and motion is merged over it — so you override one without restating the rest.

import { Easing } from "remotion";

<StatusCycle
  motion={{
    // Flatten the overshoot out of the width morph.
    widthEase: Easing.bezier(0.4, 0, 0.2, 1),
    // And give the chips a slower step.
    scrollTau: 0.12,
  }}
/>

Props

PropTypeDefaultDescription
prefix
string"snapcn is"The line that does not change, to the left of the pill
statuses
string[] | string["animating", "transitioning", "rendering a scene", "installed"]Pill labels, in order. Also accepts a comma-separated string, which is what the customizer passes
chips
string[] | string["text-reveal", "phone-frame", "answer-stream", "word-flip", "orbit-gallery"]Act-2 chip labels, in order. Also accepts a comma-separated string
fieldColor
stringtheme.primaryThe field act 1 sits on
pageColor
stringtheme.backgroundThe field act 2 sits on; the crossfade runs between the two
pillColor
stringtheme.foregroundPill fill
pillLabelColor
stringa pale tint of fieldColorPill label ink
prefixColor
stringtheme.foregroundPrefix ink
chipFills
string[] | stringcard, a pale tint of the field, the fieldChip fills, cycled in order. Three of them gives the reference's period-3 rhythm
chipTextColor
stringtheme.foregroundChip ink. One colour on every chip, including the saturated one
chipBorderColor
stringmixOklch(theme.border, theme.foreground, 0.28)Chip hairline. The default is the design system's own recipe for carrying a border token up to a surface this size
fontWeight
number400Act-1 type weight
chipFontWeight
number400Act-2 type weight. Measured 400 — the chips are bigger, not heavier, and bumping this is the usual way to make them look cheap
pillRadius
number0.229 x pill heightPill corner radius in px. The default ratio is very nearly shadcn's own radius token over a 40px control (10/40 = 0.25), so it already sits in the design system. Not a stadium — h/2 fits the reference's edge two orders of magnitude worse
chipRadius
number0.162 x chip heightChip corner radius in px
chipBorderWidth
number0.017 x chip heightChip hairline width in px
pillCenterY
number0.516Pill centre as a fraction of frame height. The reference's lockup rides 1.6% low
motion
Partial<StatusCycleMotion>STATUS_CYCLE_MOTIONCurve and timing overrides: widthEase, widthSeconds, rollEase, rollSeconds, rollTravel, fadeSeconds, exitSeconds, scrollTau, letterSpring, letterStagger. Every default but the roll is fitted from frame data, so overriding one is a departure rather than a tweak
fontSize
number8.7% of the composition heightAct-1 type size in px
chipFontSize
number22.2% of the composition heightAct-2 type size in px
fontFamily
stringInterLoaded through @remotion/google-fonts, so the Player and the render agree
introFrames
number24Frames the per-letter intro cascade gets before the first status swap
statusHold
number30Frames from one status swap to the next: the 15-frame roll, then 15 frames to read the label. Keep it above the roll, or every label is read mid-flight
chipStagger
number8Frames from one chip's arrival to the next
startAt
number0Frame the scene starts on
speed
number1Playback rate. Below 1 the last chip never arrives
theme
Partial<SnapCnTheme>—Design-system token overrides
mode
"light" | "dark"—Which side of the token set to resolve

Frequently asked questions

How do I add Status Cycle to a Remotion project?

Run npx shadcn@latest add @snapcn/status-cycle. The source is copied into components/snap-cn/ (or wherever your components.json points) with anything it depends on, and you own the code from then on — there is no runtime package to keep installed.

What is Status Cycle?

Status Cycle is a Remotion status badge animation for React. A status pill whose label rolls behind a hard clip while its width springs past the target and back, then the field crossfades to a column of chips stepping up from below.

What do I need before using Status Cycle?

An existing Remotion project (npx create-video@latest) with a components.json. shadcn init does not recognise Remotion, so the installation guide has you write that file by hand — it takes two minutes. The component is MIT licensed and free.