snapcn
41 components
DocsComponentsTemplatesVideo EditorNewShowcaseMarketplaceRoadmapChangelog

Getting started

IntroductionInstallationAgent skill
GitHub

Star snapcn

Free, open-source components.

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

Star on GitHubXFollow

MIT licensed · own your code.

© 2026 snapcn

Loading
snapcn
41 components
DocsComponentsTemplatesVideo EditorNewShowcaseMarketplaceRoadmapChangelog

Getting started

IntroductionInstallationAgent skill
GitHub

Star snapcn

Free, open-source components.

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

Star on GitHubXFollow

MIT licensed · own your code.

© 2026 snapcn

DocsComponentsTemplatesVideo EditorNewShowcaseMarketplaceRoadmapChangelog
Getting startedIntroductionInstallationAgent skill

Channel Thread

A work chat filling itself in — an avatar, a name in bold, a quiet timestamp and messages as plain lines; three dots hold a line until the words arrive, and the transcript scrolls once per arrival and is perfectly still in between

Not bubbles: a transcript. An avatar, a name in bold, a quiet timestamp, and the messages under them as plain lines — the shape every team tool has settled on.

Messages land one at a time. A second message from the same person joins the group without repeating the name. When somebody new starts typing, three dots hold their line until the words arrive.

How it moves

The transcript never creeps. Between arrivals it is byte-identical frame to frame — measured at 0.00px for twenty-nine frames at the head of the reference and another eight in the middle. Everything that looks like life comes from the arrivals themselves.

It scrolls only when it has to. A line moves the transcript by however far it falls past the anchor, which is zero until the thread is tall enough to reach it. That is one constant instead of a per-message "does this one scroll" flag, and it is what reproduces the reference's second message landing with the stack measurably motionless.

A longer move takes less time. Measured twice: the two-row scroll runs 1.60× faster in normalised time than the one-row scroll. That is a browser's native smooth-scroll heuristic, and the component reads one curve faster the further it has to go rather than pretending both moves are the same speed.

The room is made before the person arrives. The scroll starts three frames before the avatar, the name and the first dot are painted. Three frames is nothing to watch and everything to match — and the ellipsis is timed from that moment, not from the scroll, or it sits a step out of phase for its whole life.

The two clocks

beats is the frame each message's words land on. opens is the frame its row appears. Splitting them is what lets a pause be a pause: a group's row shows typing dots until its words arrive, and a later message's row simply waits.

Notes

Messages are single lines — they do not wrap, because wrapping would break the measured baseline grid the whole scene is laid out on. Keep a line to about thirty characters at 16:9 and it will not reach the frame edge.

The scene ships dark, alone in the registry. A transcript is read against the room it is in, and on the warm off-white page the scroll fade — which is the whole depth of the scene — turns the oldest line into a pale smudge that reads as a rendering fault rather than as distance. mode="light" is one prop away and still resolves from your tokens.

Avatars default to two of the twenty-four photos the registry already ships in public/avatars. Faces are the difference between a chat that reads as a chat and one that reads as a wireframe.

avatars is split by |, not by comma: a data: URL has a comma of its own right after the encoding, and splitting on it hands the browser half a URL and an <img> that renders a broken-image glyph where a face should be. Leave an entry blank and that person gets their initial on a tile tinted with your accent.

The avatar holds the render until the picture has actually loaded. Without that a frame can be captured before it arrives and the square comes out empty — invisible in the Player, where an image simply appears, and silent in an mp4 until somebody watches it.

Installation

pnpm
$ pnpm dlx shadcn@latest add @snapcn/channel-thread

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

Component code

The exact file shadcn add copies into your project.

Usage

// src/Root.tsx
import { Composition } from "remotion";
import { ChannelThread } from "@/components/snap-cn/channel-thread";

const Chat = () => (
  <ChannelThread
    script="Launch video by Thursday?|We have nothing shot.;Already done.|Built it out of snapcn."
    people="rhea 9:41 AM;sam 9:42 AM"
  />
);

export const RemotionRoot = () => (
  <Composition
    id="ChannelThread"
    component={Chat}
    durationInFrames={110}
    fps={30}
    width={1280}
    height={720}
  />
);

Props

PropTypeDefaultDescription
script
string—The conversation. Groups split by `;`, a group's messages by `|`
people
string—One `name time` per group, split by `;`
beats
string"0,12,60,84"Frame each message's words land on, in order
opens
string"0,12,37,72"Frame each row appears on. A row waits here — showing dots if it heads a group — before its words land
avatars
string"/avatars/07.jpg|/avatars/13.jpg"One image per group, split by `|`. A blank entry falls back to the person's initial
fontFamily
string"Default"Barlow unless you name one of the loaded faces
theme
Partial<SnapCnTheme>—Design-system token overrides. Anything you leave out falls back to the shadcn defaults
mode
"light" | "dark""dark"Which end of the design system to resolve tokens against

More in this category

  • Follower RushAn X-style follower notification that piles up — avatars stack in and the count explodes, then the row bends into an undulating wave of faces

Get the next component in your inbox

New free components as they ship, and the pro pack first when it lands. One email a week at most, never a sponsored one.