Getting Started
This guide walks you from zero to a rendered canvas, a first edit, and an exported image in under five minutes. You will need:
- Node.js β₯ 20.19
- A Chromium-based browser (Chrome, Edge, Brave, Arc) β for the studio. The CLI itself runs anywhere Node runs.
1. Scaffold a project
npx create-lerret my-canvasThe scaffolder downloads from npm and writes a new project at ./my-canvas/. Total install β including Vite and React, downloaded once and cached β typically runs in under a minute on a warm broadband connection. No global install is required.
If my-canvas already exists, the scaffolder refuses to overwrite a non-empty directory but is happy to populate an empty one.
You can also use other runners:
pnpm dlx create-lerret my-canvas
yarn dlx create-lerret my-canvas
bunx create-lerret my-canvasWhat you got
my-canvas/
βββ .lerret/
βββ config.json
βββ _fonts/
β βββ LerretFixtureMono.woff2
βββ social/
βββ instagram-square.jsx
βββ twitter-banner.jsx
βββ twitter-banner.data.json
βββ youtube-thumbnail.jsxThe .lerret/ folder is the only thing Lerret cares about. Everything else in the project is yours β Lerret never touches files outside it (the separation invariant).
| Path | What it is |
|---|---|
.lerret/config.json | Project-root config. The vars block flows down the cascade as CSS-like variables. |
.lerret/_fonts/ | Reserved folder. Any .woff2 / .woff / .ttf / .otf here is auto-registered as a CSS @font-face rule. Available to every asset by its file name. |
.lerret/social/ | A page β top-level non-underscore folders become pages on the canvas. |
*.jsx | Each component file becomes an artboard. The default export is the primary variant; named exports become extra variants. |
*.data.json | Co-located data file for an asset (here, twitter-banner.data.json). Overrides component props at runtime. |
Want a minimal start?
For an empty project β just .lerret/config.json with { "vars": {} }, no sample assets:
npx create-lerret my-canvas --no-samples2. Start the studio
cd my-canvas
npx @lerret/cli devThe Vite dev server boots and your default browser opens to the studio. You should see three artboards on the canvas: a Twitter banner, an Instagram square, and a YouTube thumbnail β each rendered at its real pixel dimensions.
Common flags
npx @lerret/cli dev --port 4000 # custom port (default: Vite's default, usually 5173)
npx @lerret/cli dev --no-open # don't auto-open the browser
npx @lerret/cli dev --folder ./ # explicit project folder (bypasses walk-up)If you do not pass --folder, the CLI walks up from the current working directory looking for the nearest .lerret/ β like git walks up looking for .git/.
3. Edit a component
Open .lerret/social/twitter-banner.jsx in your editor. Find the headline and change it:
// Before
<div style={{ fontSize: 80, fontWeight: 800, ... }}>
{headline}
</div>
// After
<div style={{ fontSize: 80, fontWeight: 800, ... }}>
Hello from my first Lerret canvas
</div>Save the file. The Twitter banner artboard re-renders in under a second. No page reload, no manual rebuild. The watcher detects the change, Viteβs React Fast Refresh swaps the module, and the canvas updates in place.
4. Add a second asset
Create a new file .lerret/social/release-card.jsx:
export const meta = {
dimensions: { width: 1200, height: 630 },
label: 'Release card',
tags: ['release', 'og'],
};
export default function ReleaseCard() {
return (
<div style={{
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: 'var(--brandColor, #3D5A80)',
color: 'white',
fontSize: 72,
fontWeight: 800,
fontFamily: 'system-ui',
}}>
v1.0 β Shipping today
</div>
);
}Save it. A new artboard appears on the canvas β at exactly 1200Γ630 β alongside the others. The watcher reports an add event, the loader incorporates the new asset into the project model, and the canvas adds the artboard.
Notice the var(--brandColor, #3D5A80). The project-root config.json declares vars.brandColor = "#3D5A80", which the studio exposes as a CSS custom property on the artboard. Change the value in config.json, save, and every artboard re-renders with the new color.
5. Add a variant
Variants are extra components in the same file, exported by name. Add this to release-card.jsx:
export const Dark = () => (
<div style={{
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: '#0A0A0A',
color: '#E0FBFC',
fontSize: 72,
fontWeight: 800,
fontFamily: 'system-ui',
}}>
v1.0 β Shipping today
</div>
);Save. A second artboard appears next to the original β labelled Dark β also at 1200Γ630. The meta export is shared across all variants in the file; the default export is the primary variant; each named function export is an extra variant.
Component-valued exports = variants. Non-function exports (constants, the
metaobject) are ignored.
6. Add data
Lerret can supply props to your component from a co-located data file β no code change required.
Create .lerret/social/release-card.data.json:
{
"default": {
"label": "v1.0 β Shipping today"
},
"Dark": {
"label": "v1.0 β Shipping today (dark mode)"
}
}Then accept label as a prop in your component:
export default function ReleaseCard({ label = 'Untitled' }) {
return (
<div style={{ /* ... */ }}>
{label}
</div>
);
}The top-level keys match your variant export names (default, Dark), so each variant gets its own data. This is per-variant data keying. If the top-level keys do not match any export name, the entire data object is applied as shared data to every variant.
There is also a .data.js form when you need computed or conditional data β see Authoring Assets for the full mechanics.
7. Export to image files
When you are happy with the canvas, render every artboard to image files from the command line:
npx @lerret/cli exportBy default, output goes to ./lerret-export/ (relative to your CWD, never inside .lerret/). Each artboard becomes a PNG, nested under folders that mirror the projectβs page/group structure:
lerret-export/
βββ social/
βββ instagram-square.png
βββ release-card.png
βββ release-card-Dark.png
βββ twitter-banner.png
βββ youtube-thumbnail.pngUseful flags:
npx @lerret/cli export --format jpg # JPG instead of PNG (jpeg also accepted)
npx @lerret/cli export --out ./assets # custom output directory
npx @lerret/cli export --flat # all images at one level, no nested folders
npx @lerret/cli export .lerret/social # scope: only this pageThe CLI boots a headless Chromium through Playwright and uses the same rendering path the in-studio export buttons use, so a lerret export run produces a pixel-faithful match to what the studio captures.
See CLI Reference for the full flag list, including the --data and --config override flags that let you re-render an entire project with different data or theming without modifying any files.
Whatβs next
- Concepts β the mental model: pages, groups, variants, the four-tier prop cascade.
- Authoring Assets β the full guide to writing components, schemas, data files, and fonts.
- Examples β eight runnable patterns to copy and adapt: OG cards, YouTube thumbnails, repo cards, release graphics, more.
- CLI Reference β every flag and exit code for
devandexport. - Deployment β local CLI, hosted browser, or self-hosted static studio.
Welcome to Lerret.