Add to an existing app
Document the components of an existing application, with declarations next to the components and one dependency tree.
Declarations sit next to the components they document, so the catalog points into the application source:
catalog: { include: ['src/components', 'src/blocks'] },Two rules for every layout
handoff-app must be a dependency of the package that holds the *.handoff.ts files. A
declaration imports handoff-app/react, and Handoff resolves that import from the declaration's
directory, not the config's. With npm, hoisting usually hides a missing dependency. With pnpm,
Handoff skips the item with Could not resolve "handoff-app/react", and the build still exits
with code 0.
Two tsconfig.json files do different jobs.
- The config nearest to the declarations type checks them. Editors read only the nearest config.
- The config in the working directory produces the property tables. Without it, Handoff reports
TypeScript config not foundand every property table is empty.
If the working directory is not the project root, you need both files.
Choose a layout
Each layout keeps one dependency tree, so the catalog renders the real components with the same
React instance. A separate install with its own node_modules is not supported.
| Layout | Working directory | Use when |
|---|---|---|
| Subdirectory (recommended) | handoff/, set by HANDOFF_WORKING_PATH | The application must not change |
| Project root | The project root | The bundler can move its public/ directory (not Next.js) |
| Workspace package | handoff/, with its own package.json | The repository is already an npm or pnpm workspace |
Subdirectory layout
Install in the application package
npm install handoff-appCreate the config
Paths are relative to the working directory, so they start with ../. entries.scss loads the
application's global stylesheet into every preview.
import { defineConfig } from 'handoff-app'
export default defineConfig({
app: { title: 'Example Design System' },
catalog: { include: ['../src/components'] },
entries: { scss: '../src/styles/main.scss' },
})npx handoff-app eject:config writes the full default config instead.
Add a tsconfig for the property tables
{
"extends": "../tsconfig.json",
"include": ["../src", "handoff.config.ts"],
"exclude": ["node_modules", "out", "public"]
}Add scripts to the application
{
"scripts": {
"handoff:dev": "HANDOFF_WORKING_PATH=handoff handoff-app start",
"handoff:build": "HANDOFF_WORKING_PATH=handoff handoff-app build"
}
}On Windows, an inline variable does not work in an npm script. Install cross-env and prefix the
command with cross-env HANDOFF_WORKING_PATH=handoff.
Declare a component and start
Write a declaration next to a component, for example src/components/button/button.handoff.ts.
See Catalog items. Then run npm run handoff:dev.
Ignore generated files
Add handoff/public/api, handoff/out, handoff/.handoff, and handoff/.vercel to
.gitignore. Commit handoff/exported if you fetch from Figma.
Project root layout
The working directory is the project root, so one tsconfig.json does both jobs. Handoff writes
its generated documentation API to public/api, so a bundler that serves public/ would copy it
into the production build. Move the bundler's static directory:
export default defineConfig({
publicDir: 'static',
})A Next.js application cannot move its public directory, so it cannot use this layout.
Workspace package layout
handoff/ has its own package.json and scripts. The root forwards to it:
{
"workspaces": ["handoff"],
"scripts": { "handoff:dev": "npm run dev --workspace handoff" }
}{
"name": "handoff",
"private": true,
"scripts": { "dev": "handoff-app start", "build": "handoff-app build" },
"dependencies": { "handoff-app": "^2.1.0" }
}The declarations in src/ belong to the application package, so both packages declare
handoff-app.
pnpm
build and build --package vercel work with the default pnpm layout. build --target registry
cannot follow pnpm symlinks when it copies runtime dependencies into the standalone bundle. Use a
flat layout:
nodeLinker: hoisted