Skip to main content

Manual Setup without MCP

Download your tokens and use Bezel Kit to generate files for your app. This works on all plans, including Free, without MCP or an AI assistant.

You'll need Node.js 22.13+ and a Bezel project with tokens. Run these commands from your app's directory, using its package manager; the examples use npm.

1. Install Bezel Kit​

npm install --save-dev @bezel-labs/bezel-kit

2. Download your tokens​

In Bezel, open your project and click Download in the app header.

Save it as design-tokens.json in your app's root directory.

3. Create bezel.json​

npx bezel init

This creates bezel.json. Generated files go in src/bezel/, or bezel/ if your app has no src folder. TypeScript projects also get context and font modules.

For a custom folder, use npx bezel init --dir <path>. To change an existing setup, edit the output paths in bezel.json.

4. Generate files and connect your app​

Add a tokens script and run it before your existing dev/build commands. This example uses Vite:

{
"scripts": {
"tokens": "bezel build",
"dev": "npm run tokens && vite",
"build": "npm run tokens && vite build"
}
}

Generate your files:

npm run tokens

Import the CSS once in your entry file, adjusting the path to match your output folder:

import './bezel/variables.css';

Use the generated CSS variables in your app. See how names are chosen if a variable is missing or has an unexpected name.

Generated files are overwritten by builds and added to .gitignore; keep handwritten code outside those output paths.

5. Live preview (optional)​

Crystal lets Bezel's preview panel apply token edits to your app live:

  • Install Crystal (recommended): enable the receiver in all builds, including production.
  • Install Crystal behind a dev flag: enable the receiver only in development.
  • Skip Crystal: use generated tokens without live preview.

If you already have Crystal configured, you can keep your existing setup.

Install Crystal (recommended)
npm install @bezel-labs/crystal

Initialize it once in your browser entry or client lifecycle:

import { startLivePreviewReceiver } from '@bezel-labs/crystal';

const receiver = startLivePreviewReceiver({
allowedOrigins: ['https://app.bezel.new']
});
Install Crystal behind a dev flag
npm install --save-dev @bezel-labs/crystal

For a Vite browser entry, place the dynamic import inside the development guard:

if (import.meta.env.DEV) {
import('@bezel-labs/crystal').then(({ startLivePreviewReceiver }) => {
const receiver = startLivePreviewReceiver({
allowedOrigins: ['https://app.bezel.new']
});

import.meta.hot?.dispose(() => receiver.stop());
});
}

For other frameworks, use their development flag and browser lifecycle. For example, Next.js uses process.env.NODE_ENV !== 'production', with receiver initialization inside a client effect.

Verify that your production build excludes the receiver.

For either option, avoid initialization during server rendering and call receiver.stop() during lifecycle cleanup to prevent duplicate listeners.

Keep the origin allowlist explicit. In Bezel's preview panel, set Preview URL to your running app or Storybook URL, then edit a token to check that it updates live.

Keep the default nameExtension in bezel.json so live preview and generated CSS use matching variable names.

For nested iframes such as Storybook, see the Crystal relay setup.

6. Update tokens manually​

After changing tokens in Bezel:

  1. Click Download again.
  2. Replace your app's design-tokens.json with the downloaded file.
  3. Run npm run tokens.

Live preview does not update your local token file. Download and rebuild to keep your changes.

Command reference​

Run npx bezel --help for available commands and flags. See Bezel Kit for configuration and output details.

Next steps​