
Onboarding Videos
FreeOptimize and manage onboarding demo videos seamlessly.
Free · Opens the source repo
What Onboarding Videos does
The Onboarding Videos skill is designed to streamline the process of adding, replacing, and optimizing looping demo videos within the onboarding experience of your application. This skill specifically targets the bento grid layout used in the WelcomeScreen component, which displays feature cards that can include short, muted, looping screen recordings. By following the provided guidelines, developers can ensure that these videos play smoothly without lag, enhancing the overall user experience during onboarding.
The skill includes a detailed workflow for optimizing existing clips using an ffmpeg-based script. This script addresses common issues that can lead to playback lag, such as improperly positioned moov atoms and excessive video resolutions. By re-encoding videos to a maximum width of 1000 pixels and applying the faststart option, the skill ensures that videos are ready for immediate playback, minimizing stutter during user interactions.
In addition to optimization, the skill provides clear instructions for adding new clips or replacing existing ones. This includes naming conventions, file placements, and the necessary code changes to integrate the media into the WelcomeScreen component. The skill also emphasizes the importance of keeping the video poster frames in sync with the first frame of the video, thus avoiding any visual discrepancies during playback.
Overall, this skill is particularly useful for developers working on applications that require a polished onboarding experience with video content. It simplifies the process of video management and optimization, allowing teams to focus on creating engaging content without worrying about technical playback issues.
When to use it
Use this skill when adding or updating demo videos in your application's onboarding flow, especially if users report lag or sync issues.
When not to use it
This skill may not be suitable for applications that do not utilize video content in their onboarding process or for teams that prefer a different video management approach.
What you can build with it
Adding a New Onboarding Video
When you need to introduce a new feature, use this skill to record and optimize the video, ensuring it meets the required specifications.
Replacing an Existing Demo Clip
If a current demo video is outdated, simply replace it with a new file of the same name and run the optimization script to keep everything in sync.
Improving Playback Performance
If users report lag during onboarding, utilize this skill to optimize existing videos and enhance the overall performance of the onboarding experience.
How to install Onboarding Videos
View source1. Install with the skills CLI
npx skills add posthog/posthog/onboarding-videos --agent claude-code2. Or install it manually
Download the skill folder and drop it into ~/.claude/skills/ for all projects, or .claude/skills/ to scope it to one repo. Restart Claude Code so it picks up the new skill.
Anthropic's agentic coding CLI, and the reference implementation of Agent Skills. Drop a skill folder into ~/.claude/skills and Claude Code loads it automatically whenever a task matches the skill's description. Claude Code docs
Inside SKILL.md
Written by posthogOnboarding bento demo videos
The first onboarding step (WelcomeScreen) shows a bento grid of feature cards.
A card with a media entry plays a short, muted, looping screen recording; cards
without one show a static placeholder. The featured (large, top-left) card is the
only one that plays at a time — hover just moves the highlight.
| Thing | Where |
|---|---|
| Clips + posters | packages/ui/src/features/onboarding/assets/<feature>-<light|dark>.{mp4,jpg} |
Wiring (MEDIA map, startTime) | packages/ui/src/features/onboarding/components/WelcomeScreen.tsx |
<video> element + play/seek logic | packages/ui/src/features/onboarding/components/FeatureBentoCard.tsx |
| Optimizer script | scripts/optimize-onboarding-videos.mjs (pnpm optimize:onboarding-videos) |
Why clips need optimizing (the "it feels laggy" fix)
Raw screen recordings hitch in this UI for two reasons, both fixed by re-encoding:
moovatom at the end of the file. Without faststart the player must read to EOF before it can start, so first-play and every seek stutter. We seek on mount and on every loop (FeatureBentoCardparks the clip onvideoStartTime), so this bites constantly. Fix:-movflags +faststart.- Resolution far larger than it renders. The featured slot is never wider than
~500 CSS px (the grid is
max-w-[760px]). Even at 2× retina that's ~1000px, but recordings come in at ~1876px. Decoding huge frames and downscaling them — on every play and during the framer-motion slot reflow — is wasted work. Fix: cap width at 1000px (which also matches the poster width exactly).
Bitrate/size are usually already modest; decode cost and startup are the lag, not bytes. Don't chase file size at the expense of width/faststart.
Canonical encode target
H.264 · yuv420p · ≤1000px wide (keep aspect, even height) · CRF 23 ·
+faststart · no audio. These live as constants at the top of
scripts/optimize-onboarding-videos.mjs — change them there, not ad-hoc.
Optimize existing clips
pnpm optimize:onboarding-videos # encode any clip not already optimized
pnpm optimize:onboarding-videos --dry-run # show what would change
pnpm optimize:onboarding-videos --force # re-encode all (after changing the target constants)
Requires ffmpeg (brew install ffmpeg). The script tags each output with a
comment marker, so re-runs skip already-optimized files — it's safe to run any
time, including right after dropping in a new clip. It rewrites files in place;
review the git diff --stat and the printed before/after sizes.
Add or replace a clip
-
Record light + dark variants at ≥1000px wide. The optimizer downscales to 1000px but never upscales, so anything narrower ships soft — resolution is the one thing it can't fix for you. Keep it short (~10–13s); it loops.
-
Name + drop the files as
assets/<feature>-light.mp4andassets/<feature>-dark.mp4. Two things bite every time:- Recordings almost always arrive with the light variant unsuffixed
(
foo.mp4, onlyfoo-dark.mp4is tagged). Rename it tofoo-light.mp4. - The source filename (e.g. whatever's in
~/Downloads) is irrelevant — the<feature>prefix must be the media id you'll use inWelcomeScreenand follow the existing convention, not whatever the file was called.
No
assets.d.tschange needed —*.mp4/*.jpgare wildcard modules. - Recordings almost always arrive with the light variant unsuffixed
(
-
Optimize:
pnpm optimize:onboarding-videos. -
Make the poster — the still shown before play. Use the clip's first frame and leave
startTimeat 0, so the poster, the first played frame, and the loop point are all the same with nothing to keep in sync:ffmpeg -y -i assets/<feature>-<theme>.mp4 \ -frames:v 1 -vf "scale=1000:-2:flags=lanczos" -q:v 3 \ assets/<feature>-<theme>.jpg -
Wire it up in
WelcomeScreen.tsx. For a new media id, four edits, all keyed by the same slug: import the.mp4+.jpg, add the id to theMediaIdunion, add its entry to theMEDIAmap (startTime: 0), and setmedia: "<id>"on the targetFeatureDef.
Replacing an existing clip
Keep the same asset filename and the wiring is untouched — none of step 5
applies. Drop the new file over assets/<feature>-<theme>.mp4, run the optimizer (a
fresh drop carries no skip-marker, so it re-encodes without --force), and
regenerate that poster (step 4). If you replace only one theme, re-check that
light and dark still share an aspect ratio — a clip with stray padding frames
differently from its sibling, and the gap shows when the user toggles theme.
Poster = first frame (and startTime = 0)
FeatureBentoCard shows the poster while a card rests, then seeks the <video> to
MEDIA[id].startTime on mount and loops back there (not necessarily to 0). The
simple, default contract: the poster is the clip's first frame and startTime is
0, so the still, the first played frame, and the loop point are all identical —
nothing to keep in sync. Keep the poster the same pixel width as the clip (1000px)
so the poster→video swap is seamless.
startTime can start/loop mid-clip if you ever need it (the code-review clip uses
3s) — but then the poster MUST be that exact frame (ffmpeg -ss <startTime> -i …),
or the still jumps the instant playback starts. Prefer the first-frame default
unless you have a specific reason.
Verify
# clips: moov should print BEFORE mdat, width ≤ 1000:
ffprobe -v trace <clip>.mp4 2>&1 | grep -o -m2 -E "type:'(moov|mdat)'"
ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 <clip>.mp4
# wiring: a grown MediaId union may need a formatter reflow, so let biome fix it:
npx biome check --write packages/ui/src/features/onboarding/components/WelcomeScreen.tsx
pnpm --filter @posthog/ui typecheck
To confirm playback feels smooth in the real app, use the test-electron-app
skill to drive the running onboarding flow.
Frequently asked questions about Onboarding Videos
Similar skills
Playwright Component Testing
Test React and Vue components in isolation with Playwright.
Fluent UI Blazor
Integrate Fluent UI components in Blazor applications effortlessly.
Build MCP App
Create interactive UI widgets for MCP servers.
Web Design Reviewer
Identify and fix design issues in websites efficiently.
Markstream Install
Seamlessly integrate Markstream for Markdown rendering.
GSAP & Framer Scroll Animation
Create advanced scroll animations effortlessly.
