Beamline

Docs · Updated

Install Beamline

Part of the documentation that comes with Beamline's complete system (docs/INSTALL.md in the kit). Your coding agent can do all of this for you through Beamline's MCP server: connect it.

Use the tools and release descriptor from the same delivered release. React 19 belongs to the app. For 0.2 releases, select compiled packages or editable source; both share @beamline/runtime and @beamline/foundation. Keep customer brand files, presets and wrappers in the app, outside the managed source folder.

Selected 0.2 packages

From a React app with a package.json:

node /path/to/release/tools/beamline.mjs install button dialog \
  --release /path/to/release/release.json --preset prism

A local HTTP release.json URL works too. The tool verifies the descriptor's graph and selected package archives, then uses npm or pnpm. It does not download component source for this path. The descriptor names registry dependencies; an offline Beamline bundle includes its own package archives but still needs those declared registry dependencies or an existing package-manager cache.

The app's package.json lists only what was chosen: the selected components, looks, fonts and effects, plus @beamline/runtime and @beamline/foundation, each pointing at its verified archive in the app's root .beamline/packages/ cache. The internal Beamline units they use resolve through overrides (npm) or version-qualified pnpm overrides, and their registry dependencies (Radix, Motion, d3-shape for chart curves) come with the units instead of entering the app's manifest. React stays the app's: an existing react/react-dom range is never changed, and the tool stops with a message when it does not allow React 19. Dependencies the tool added earlier and no longer needs are removed; nothing else in the manifest changes.

What your own code imports belongs in your package.json: your icon library, and anything a component's props are typed with. The summary names the latter with the release's range (Form takes your react-hook-form useForm: react-hook-form@^7.89.0); add it at that range so your app and the components share one copy. With pnpm an import the app does not declare fails to resolve; npm may hoist it into reach, but declare it anyway.

For pnpm, the installer recognizes the app's packageManager, its lockfile, or its enclosing pnpm workspace. It adds version-qualified archive overrides through pnpm's own project configuration command so transitive Beamline dependencies resolve to the included files. Existing workspace members, settings and unrelated overrides remain. Previous workspace configuration bytes are retained under .beamline/package-manager/. Keep the root .beamline/ folder with the app (commit it): its manifest, lockfile and overrides reference the archives there, and its package-selection.json records what was chosen at which release. A compiled install writes nothing under src/ except the one stylesheet. This path is exercised with pnpm 11.22.0.

The tool writes one stylesheet, src/styles/beamline/beamline.css, that imports fonts, foundation, the selected looks, an applied custom style and every selected component in cascade order. Import it once in the app's entry. One entry lets the bundler include each Beamline file once; separate per-component imports each bring foundation again. The file is regenerated while it holds what the tool last wrote; once you edit it, the tool keeps your file and prints the imports it would have added. Each component's own styles.css still works on its own for an app that uses a single component.

// src/main.tsx — in a new Vite app, replace the starter's index.css (it centres #root at 1126 px and paints its own
// background) with the one-line page reset below.
import { createRoot } from "react-dom/client";
import "./styles/beamline/beamline.css";
import "./index.css";
import { App } from "./App";

createRoot(document.getElementById("root")!).render(<App />);
/* src/index.css — the app's own page reset; Beamline never resets the host page */
body {
  margin: 0;
}
// src/App.tsx
import { UIRoot } from "@beamline/runtime";
import { Button } from "@beamline/button";
import { prism } from "@beamline/look-prism";

export function App() {
  return (
    // A full-page app: look-page paints the look's page edge to edge, in dark and light.
    <UIRoot preset={prism} theme="dark" brand="oklch(0.64 0.16 155)" className="look-page" style={{ minHeight: "100dvh" }}>
      <Button onClick={() => console.log("Connect the app's save operation")}>Save</Button>
    </UIRoot>
  );
}

UIRoot paints nothing behind its children unless asked, so a root embedded in an existing page keeps that page's background; App shell paints its own page. Give a full-page root className="look-page" and the viewport's height, and remove the browser's default body margin, as above.

Some looks need explicit SVG definitions. The CLI's selected look setup and that look's README provide the exact runtime import. For example, use import { setup } from "@beamline/look-mono/runtime"; and call setup() in the application's browser entry, retaining its returned cleanup for teardown. In a React client boundary, useEffect(() => setup(), []) pairs acquisition and cleanup. Multiple roots share the definitions. Importing the look reference or mounting a chart never initializes other looks. Soft has no runtime setup.

Adding compiled roots unions them at the recorded release, and so do --preset, --fonts and --effects: install --preset glass adds Glass to an app that has Soft, with no entry ids needed. Remove one explicitly with remove --preset glass; the tool refuses while the app still imports @beamline/look-glass, and keeps at least one look. Move roots explicitly with install <id…> --upgrade --release <target>. Exact imports, package versions and setup come from the artifact. Fonts are optional and selected explicitly with --fonts <id>. A compiled package app does not need Tailwind to style Beamline. The aggregate is an explicit release selection; do not combine a legacy 0.1 aggregate with split 0.2 units. Yarn and Bun are outside this release's qualified installer path.

Every command prints a short summary; add --json for the full machine-readable result and nothing else on standard output (package-manager output goes to standard error). --dry-run shows the planned manifest and stylesheet without changing the app.

Selected editable source

node /path/to/release/tools/beamline.mjs add button dialog \
  --release /path/to/release/release.json --preset prism --install

The default managed destination is src/ui/beamline. Use --app /path/to/app when running elsewhere or --to src/ui/another-folder for a different managed subdirectory. Retrieval reads the descriptor, verified graph and selected source files directly. It never retrieves the whole source kit first.

The source tool rewrites declared internal imports to relative paths and declared runtime imports to the canonical packages. Keep your existing @/ alias. The tool does not change app aliases, Vite configuration, customer brand files or wrappers. Import a copied component relative to the file using it, for example:

import { Button } from "./ui/beamline/components/button/button";
import { UIRoot } from "@beamline/runtime";
import { prism } from "@beamline/look-prism";

Use the runtime/root and selected package styles as above. Editable components still use Tailwind v4 utilities, so enable the app's existing Tailwind v4 integration and scan the managed folder. For a Vite app with no existing integration:

npm i -D tailwindcss @tailwindcss/vite
// Add tailwindcss() alongside the existing React plugin in vite.config.ts.
import tailwindcss from "@tailwindcss/vite";
/* In the app's own entry stylesheet; paths are relative to this stylesheet. */
@import "tailwindcss";
@import "@beamline/foundation/source-theme.css";
@import "@beamline/foundation/styles.css";
@import "@beamline/look-prism/styles.css";
@source "./ui/beamline";

The release's foundation and selected look styles provide Beamline's scoped visual roles. Source CSS modules travel with their selected components. The source theme declares the utility aliases used by editable components, such as bg-surface; include it in the existing stylesheet without copying producer configuration. If the app defines the same utility names for another design system, use the compiled package path or resolve that utility-name overlap in the app. The package's optional tailwind.css uses prefixed host aliases such as bg-pui-surface.

--install installs declared packages only after the source transaction is accepted. Without it, the tool prints setup and dependencies; run deps --install later. Package-manager failure leaves truthful accepted source bytes and reports the install failure; npm/pnpm lockfiles remain authoritative for actual package resolution. A pending source proposal never installs its proposed packages beneath live callers.

Adding more entries unions the roots at the accepted release:

node /path/to/release/tools/beamline.mjs add input --app /path/to/app
node /path/to/release/tools/beamline.mjs status --app /path/to/app

Only upgrade --release <target> changes that release. remove <id> removes an intended root and recomputes shared dependencies; files still required by another root or a local import are retained. See UPGRADE.md for local changes and recovery.

The skill installer uses the same code path:

node /path/to/release/agents/skills/beamline-ui/scripts/install.mjs \
  --app /path/to/app --release /path/to/release/release.json \
  --source --entries button,dialog --preset prism

Omit --source for selected compiled packages; use --no-install for source preparation without dependency installation.

Existing 0.1 packages

A 0.1 bundle remains installable using its own original package instructions:

npm i /path/to/beamline-ui-0.1.x/package/beamline-ui-0.1.x.tgz

Use @beamline/ui/<id>, import @beamline/ui/styles.css, and render inside its UIRoot from @beamline/ui/ui-root. Do not apply 0.2 setup to 0.1 bytes. A legacy source lock containing only hashes needs its original baseline bundle and a manual migration; the new source tool refuses to invent missing baselines.

Check the actual app

Render the selected Button on a dark branded root, activate its app handler, and open the selected Dialog. Confirm the browser console is clear, existing navigation and styles still work, and customer alias/brand files are unchanged. Blocks need a sized parent. These are consumer checks; the included gallery is separate evidence.