Skip to content

Docs

Install ZoBlocks and use your first component.

Add it to a React app in five steps, match Ant Design or Material UI, or use the loaders from Vue, Angular, Svelte and plain HTML.

Overview

  • React gets every component. A command-line tool copies each one into your project as source you own.
  • Vue, Angular, Svelte and HTML get the loaders and the switch, as web components from npm.
  • Three styles. Components look like ZoBlocks by default, and take on your Ant Design or Material UI theme with one wrapper.

What it needs

20.11+

Node.js

What the CLI runs on.

19

React

18 is partly covered.

5.7+

TypeScript

Sources ship as .tsx and .ts.

4

Tailwind CSS

CSS-first config. No tailwind.config.js.

2

npm packages

clsx and tailwind-merge. Nothing else.

No component imports a Node built-in, so nothing needs a polyfill or a server runtime. Pick your package manager on any command below; the whole page follows.

Add to a React app

For a Next.js or Vite app you already have. No app yet? Create one first, then come back.

  1. Check the @ alias

    Components import each other as @/…. Next.js apps — App Router or Pages Router — usually have this already; Vite apps need it in three files.

    tsconfig.jsonjson
    {
      "compilerOptions": {
        "paths": { "@/*": ["./src/*"] }
      }
    }
  2. Set up ZoBlocks

    init writes zoblocks.json, which records where your @/ alias points (src, or . if there is no src folder). Every later add copies source relative to it. Run it once per project.

    1. npx @zoblocks/cli init

    You should see: Created zoblocks.json

  3. Add a component

    --yes also installs the two small packages it needs.

    1. npx @zoblocks/cli add pulse-loader --yes

    You should see: write lines for the files under src/components/zoblocks, src/lib and src/styles

  4. Import the styles

    Add these lines under @import "tailwindcss"; in your global stylesheet. zoblocks-tokens.css is required — it holds the design tokens. Then add one @import for each other zoblocks-*.css that add wrote.

    @source tells Tailwind to scan the component files. Tailwind only builds classes it finds, so without it a component renders with no styling at all.

    src/app/globals.csscss
    @import "tailwindcss";
    
    /* ZoBlocks */
    @import "../styles/zoblocks-tokens.css";
    @import "../styles/zoblocks-loader.css";
    @source "../components/zoblocks";
  5. Use it

    Any page or componenttsx
    import { PulseLoader } from "@/components/zoblocks/pulse-loader";
    
    export default function Page() {
      return <PulseLoader label="Loading your records" showLabel />;
    }

    On the Next.js App Router this can be a Server Component — see Server Components below.

    1. npm run dev

    You should see: a slowly beating heart with “Loading your records” under it.

Every other component installs the same way. Its add command is at the top of its component page.

Server Components (Next.js App Router)

Components that need the browser declare their own "use client" boundary, so they work unchanged inside a Server Component. The rest — ResultValue, ClinicalStatus, AllergyChip and a few others — are pure presentation and render on the server. Either way, your page does not need "use client".

src/app/page.tsx — a Server Componenttsx
import { ResultValue, fromObservation } from "@/components/zoblocks/result-value";

export default async function Page() {
  const observation = await getObservation(); // your FHIR Observation

  // Works. No "use client" needed on the page.
  return <ResultValue value={fromObservation(observation)} now={new Date().toISOString()} />;
}

Passing a function — onOpenReport, onChange, any on… prop — needs a Client Component, because functions cannot cross from the server. Put that usage in its own file:

src/app/result-row.tsxtsx
"use client";

import { ResultValue, type ResultValueData } from "@/components/zoblocks/result-value";

export function ResultRow({ value, now }: { value: ResultValueData; now: string }) {
  return <ResultValue value={value} now={now} onOpenReport={(v) => openReport(v.id)} />;
}

The Pages Router and Vite have no Server Components, so none of this applies there.

Start a new project

Create the app, then follow the five steps above.

  1. npx create-next-app@latest my-app --ts --tailwind --eslint --app --src-dir --import-alias "@/*" --yes --use-npm
  2. cd my-app

This comes with TypeScript, Tailwind v4 and the @ alias, so step 1 is already done. It uses the App Router; for the Pages Router, change --app to --no-app. Open http://localhost:3000 in step 5.

Two commands, one alias, one stylesheet

Beyond init and add, a new project needs three things configured. The steps above do all of them:

  1. 01

    The path alias

    Components import each other as @/…. TypeScript and your bundler both need to resolve it.

    Step 1
  2. 02

    Tailwind sources and tokens

    @source so Tailwind sees the components, and the tokens stylesheet they are coloured with.

    Step 4
  3. 03

    The client boundary

    Next.js App Router only. Components that need the browser declare their own "use client"; the rest render on the server. Your pages can stay Server Components.

    Server Components

Choose a style

Pick the design your app already uses. Components never change — only the wrapper does.

The default look, with nothing to install or wrap. The zoblocks-tokens.css import from step 4 is all it needs, and it works the same in Next.js and Vite. Use this when your app has no other component library, or when you want ZoBlocks’s own design.

Vue, Angular, Svelte and HTML

The loaders and the switch ship as web components, so they work anywhere without Tailwind or the CLI. Install them, import once, and use the tag.

  1. npm install @zoblocks/loaders @zoblocks/elements
vue
// main.ts
import "@zoblocks/loaders/pulse";

// vite.config.ts — tell Vue these tags are web components
vue({ template: { compilerOptions: { isCustomElement: (tag) => tag.startsWith("zb-") } } })

<!-- any .vue template -->
<zb-pulse-loader label="Loading your records"></zb-pulse-loader>

The other loaders are /rhythm, /breath, /helix and /infusion. See each loader’s page for its attributes.

Components from npm

A few React components are npm packages instead of copied source. Install the package and import its stylesheet once — no init needed.

Signature — needs Ant Design

  1. npm install @zoblocks/signature antd
tsx
import "@zoblocks/signature/styles.css";

Tabs

  1. npm install @zoblocks/tabs
tsx
import "@zoblocks/tabs/styles.css";

CLI commands

Run with npx @zoblocks/cli (or your package manager’s equivalent, shown above).

init
Create zoblocks.json. Once per project.
add <name…>
Copy components, and what they depend on, into your project.
list
List every component you can add.
--yes
Let add install the npm packages it needs. Without it, it prints the command.
--dry-run
Show what add would write, and write nothing.
--overwrite
Replace files that already exist — how you update a component.
--cwd <dir>
Run in another folder.

Troubleshooting

The component has no styling.
Check the @source line and the zoblocks-tokens.css import from step 4.
Layout is fine but the status colours are missing.
Import the component’s own stylesheet. add lists every styles/zoblocks-*.css it writes; each needs an @import.
Cannot find module '@/…'
The @ alias is missing — step 1. On Vite it must be in vite.config.ts as well as tsconfig.
'React' is declared but its value is never read.
A known issue in a few components, reported by Vite’s strict tsc. Delete the line import * as React from "react"; in the file it names. The dev server is unaffected.
Functions cannot be passed directly to Client Components.
On the App Router, providers.tsx (Ant Design) or theme.ts (Material UI) is missing "use client" on its first line.
Event handlers cannot be passed to Client Component props.
A Server Component passed a function, such as onOpenReport. Move that usage into a file that starts with "use client" — see Server Components.
useState is not a function (or another hook error) in a Server Component.
That component does not declare "use client" yet. Render it from a file that starts with "use client", the same way.
Ant Design or MUI flashes unstyled when the page loads.
The server is not sending the library’s styles. Use AntdRegistry or AppRouterCacheProvider on the App Router, and the _document.tsx setup on the Pages Router — see Choose a style.
Pages Router: ZoBlocks uses the default antd or MUI colours, not mine.
Add the transpilePackages line to next.config.ts from the Pages Router tab of Choose a style.
No zoblocks.json, or zoblocks.json already exists.
Run init once, in the folder with your package.json. To start over, add --force.
npm warns EBADENGINE, or the CLI will not start.
Node.js is older than 20.11. Check with node -v and install the current LTS.
Browse components