Video components
Install components from the registry to copy their source into your project:
pnpm odori add component \
@odori/stage \
@odori/title-reveal \
@odori/code-proof \
@odori/terminal \
@odori/browser-demo \
@odori/connection-story \
@odori/end-cardpnpm odori catalog components prints the catalog with each component's aspect ratios, minimum duration, and reduced-motion behavior. Registry dependencies install with the component that needs them.
Use the component catalog to preview components and inspect their props, timing, and source.
- videos/
- components/
- stage/
- stage.tsx
- stage.preview.tsx
- title-reveal/
- title-reveal.tsx
- title-reveal.preview.tsx
- code-proof/
- code-proof.tsx
- code-proof.preview.tsx
- terminal/
- terminal.tsx
- terminal.preview.tsx
- end-card/
- end-card.tsx
- end-card.preview.tsx
- stage/
- components/
Use the components as ordinary React:
<Scene id="proof" duration="8s">
<Stage grid={false}>
<CodeProof
code={'export const preview = "instant";'}
focus={[1]}
language="tsx"
title="videos/launch/video.tsx"
/>
</Stage>
</Scene>Temporal contracts
Video components need more than prop types. Registry metadata describes:
- supported aspect ratios
- recommended and minimum duration
- natural entrance and exit frames
- content limits, such as maximum headline length
- reduced-motion behavior
- required fonts and audio
odori test and the registry test suite check these contracts, while the copied implementation stays fully editable.
Scale to the video format
Components multiply their design values by useDesignScale(), which measures the shorter side of the frame against a 1080px reference. The same component reads correctly at 1920x1080, 1080x1920, and 1080x1080 without a separate vertical variant.
const scale = useDesignScale();
<div style={{fontSize: 116 * scale, letterSpacing: "-0.045em"}}>{title}</div>;Component development in Studio
The component source stays independent from its development fixture. A sibling *.preview.tsx file defines representative props, controls, duration, canvas size, and edge cases for Studio. The fixture is similar to a Storybook story with a deterministic frame clock.
Organize components
Studio groups components by category paths, such as Interface/Controls. It resolves categories in this order:
Directory path. A component at videos/components/interface/controls/combobox/ belongs to Interface/Controls.
Category configuration. Add category.json to customize a directory's display name and order:
{"name": "Product UI", "order": 1}order is the only way to sequence your own families; without one they follow the ones the registry ships, alphabetically.
Preview metadata. A fixture can declare its category when the directory doesn't supply one. Registry components use this to retain their category after installation:
export default defineComponentPreview({
title: "Combobox",
category: "Interface/Controls",
// ...
});Use fixture metadata for components distributed outside your project.
Reuse application components
Videos can import deterministic components from an application's root components/ directory. Keep those components under their existing product ownership instead of duplicating them for video.
Add an adapter under videos/components/product/ when the application component expects routing, live data, or browser interaction. The adapter supplies frozen data and disables behavior that depends on wall-clock or application state.
Initial component library
| Family | Components |
|---|---|
| Typography | Title reveal, metric callout, captions |
| Developer proof | Terminal, code proof |
| Interface | Browser demo |
| Narrative | Connection story |
| Brand | Stage, end card |
Install the agent quickstart
Install the quickstart skill to let your agent set up a project and choose components:
npx skills add allenzhou101/odori \
--skill odori-quickstart \
--yesWhere components come from
odori add fetches and caches the published registry. Its output lists the registry URL and installed files:
$ pnpm odori add component cursor-focus
registry: https://odori.dev/r/v1
create videos/components/cursor-focus/cursor-focus.tsx
create videos/components/cursor-focus/cursor-focus.preview.tsx
ok @odori/cursor-focus to videos/components/cursor-focus/Use --dry-run to list files without writing them. Downloads are verified against published hashes. Offline, the command uses the cache or the registry bundled with the CLI.
Point registryUrl in odori.config.ts at a fork or a pinned version, or set ODORI_REGISTRY for one command.
Installing with the shadcn CLI
The registry is shadcn-compatible, so any project can take the source:
npx shadcn@latest add https://odori.dev/r/v1/title-reveal.jsonThe shadcn CLI copies component source but doesn't register audio cues in your brand. Use odori add for automatic cue registration, or add the mapping yourself.
Three kinds of entry
The registry supports three entry types:
| Kind | What it is | What odori add does |
|---|---|---|
| Component | React that renders frames | Copies source into videos/components/<name>/ |
| Cue | Synthesis that renders samples | Copies source, and registers the cue in your brand |
| Asset | A music or sound file | Fetches the file into public/, verifies it against the published hash, and registers the cue by URL |
Use odori add component <asset> --force to replace an installed audio file. Asset files aren't handled by odori diff, odori update, or the shadcn installer.
Source ownership
Commit odori.lock.json to track installed versions. The CLI uses it to distinguish local edits from upstream changes:
odori diff # what changed, in your project and upstream
odori diff terminal --full # the diff itself
odori update # apply upstream changes to untouched componentsUpdates are opt-in and source-aware. A component that is both edited locally and changed upstream is reported as diverged and left alone until you pass --force.