lumio build
Build the extension bundle for deployment. Produces optimized client bundles for each surface and a source archive.
Usage
lumio build [--outdir <path>]
What it produces
After a successful build, the dist/ directory (or custom --outdir) contains per-target bundles and an optional assets/ directory for referenced static files:
dist/
├── editor.js <- Editor surface bundle
├── layer.js <- Layer surface bundle
├── styles.css <- Combined CSS (Tailwind or plain)
├── assets/ <- Static assets referenced by components
│ ├── logo-a1b2c3.png
│ ├── font-d4e5f6.woff2
│ └── animation-g7h8i9.lottie
├── server/ <- Only if server: true
│ └── functions.[hash].js <- Server bundle (ESM)
└── source.tar.gz <- Source archive (required by review process)
Asset handling
Images, fonts, videos, and other static files imported in your code are automatically copied to assets/ with a content hash in the filename. esbuild handles this via file loaders:
| File type | Output pattern |
|---|---|
.png, .jpg, .gif, .svg, .webp, .avif, .ico | assets/[name]-[hash].[ext] |
.woff, .woff2, .ttf, .otf, .eot | assets/[name]-[hash].[ext] |
.mp4, .webm, .ogg, .mp3, .wav | assets/[name]-[hash].[ext] |
.json, .lottie | assets/[name]-[hash].[ext] |
Import assets in your components and the bundler resolves the correct URL at runtime:
import logoUrl from "./assets/logo.png";
export default function Layer() {
return <img src={logoUrl} alt="Logo" />;
}
Example
lumio build
Building Sports Scoreboard v1.2.0...
editor 525 KB (153 KB gzipped)
layer 312 KB (98 KB gzipped)
server 44 KB
source.tar.gz assembled
Build complete → dist/
Options
| Flag | Description |
|---|---|
--outdir <path> | Output directory (default: dist) |
--no-source | Skip generating source.tar.gz (not recommended) |
CSS and Styling
lumio build supports three styling approaches. You choose during lumio init, but can switch at any time.
Tailwind CSS v4 (recommended)
If src/globals.css contains @import "tailwindcss", the build compiles it via the Tailwind CLI before bundling. Use any Tailwind utility classes in your components:
export default function Layer() {
return (
<div className="flex items-center gap-2 bg-black/80 rounded-lg p-4">
<span className="text-white text-sm font-medium">Hello</span>
</div>
);
}
Requires tailwindcss as a dev dependency (npm add -D tailwindcss@^4.3).
Plain CSS
Create src/globals.css (or src/style.css) with regular CSS. The file is copied to dist/styles.css:
.my-widget {
background: rgba(0, 0, 0, 0.8);
border-radius: 8px;
padding: 16px;
}
Import it in your component: import "./globals.css";
Inline styles
No CSS files needed. Style directly via React style props:
export default function Layer() {
return <div style={{ background: "rgba(0,0,0,0.8)", padding: 16 }}>Hello</div>;
}
Source archive
source.tar.gz contains the full source code of your extension (excluding node_modules and dist). It is uploaded alongside the bundle during lumio deploy and made available to reviewers.
CHANGELOG.md must include an entry for the version being built. If no entry is found for the current version in lumio.config.json, the CLI prints a warning — the build still succeeds, but release notes will be empty unless you fill them in via the submission wizard.
TypeScript errors
The build fails on TypeScript errors. To check for errors without building:
npx tsc --noEmit
Bundle size limits
| Plan | Max bundle size per surface |
|---|---|
| Free | 10 MB |
| Pro | 50 MB |
If a surface bundle exceeds the limit, the build fails with an error. Optimize with dynamic imports or reduce dependencies.