React and React Native package documentation
Installing and using the generated npm package, switching themes, and font delivery.
Getting your package
Your generated npm package is available as soon as you publish a release. Find the install command in Settings under the Developer tab. The package name is the one you configured there.
pnpm add @your-org/ui # or npm install / yarn add@your-org/ui with the name shown there.Versioning and updates
Each time the design system owner publishes a release, a new version of the package is produced with a semantic version number (a patch, minor, or major bump chosen at publish time). You consume updates exactly like any other dependency: change the version in your package.json and reinstall.
pnpm add @your-org/ui@latest # or pin an exact version, e.g. @your-org/[email protected]Every version is a frozen snapshot, so a given version number always resolves to the same tokens, components, and styles. Nothing changes underneath you until you choose to move to a newer version, and the platform keeps a full release history with a change summary per version.
If registry publishing is not enabled for a project, each release still produces a downloadable build (a standard package tarball) that you can install from a local path or vendor into your repository. The package contents are identical either way.
Provider setup
Wrap the root of your application with the provider. It handles all the CSS for you; every token is injected and resolved automatically. Anything rendered inside the provider can use your design system tokens directly. On web, import the fonts CSS once at the app root so the @font-face rules apply.
// Web only: import once at the app root
import "@your-org/ui/fonts.css";
import { ZakladProvider } from "@your-org/ui";
function App() {
return (
<ZakladProvider>
{/* your app */}
</ZakladProvider>
);
}The provider works with no props; it uses your default theme automatically. To set or change the active theme, pass a theme prop using the exported Themes enum, whose members are your theme slugs (for example Themes.dark). The enum contains exactly the themes you have configured, so values are checked at compile time.
AcmeProvider. The exact name, all available theme values, and a ready-to-copy setup snippet are generated in the README included with every npm release.Fonts
How fonts are delivered depends on your tier.
Paid tiers (CDN, optional)
CDN font delivery is included on all paid tiers but is optional. When enabled, the fonts.css file references hosted URLs and no font bytes are bundled. If you prefer to self-host (for example, to satisfy strict content security policies or avoid the external dependency), you can opt out and the fonts will be bundled instead.
Free tier (self-hosted)
Font files are bundled directly into the package. The fonts.css rules reference the bundled files, so you host the fonts yourself. The import is identical in both cases; the difference is invisible to your code.
On Expo (managed or bare with Expo modules), no font setup is needed. The provider calls loadFonts() on mount and gates render on the result, so text components fall back to system fonts until loading completes. TTF files are always bundled under fonts/ and registered via expo-font automatically.
Using components
Components use namespaced variants so your IDE gives you the full set of valid values with autocompletion. The component itself doubles as the namespace, so you can reference variants directly on the import.
import { Button } from "@your-org/ui";
function Example() {
return (
<>
{/* Shorthand: variant name directly on the component */}
<Button.Primary onClick={() => save()}>
Save changes
</Button.Primary>
{/* Or via the VARIANT enum for dynamic usage */}
<Button
variant={Button.VARIANT.Secondary}
disabled
onClick={() => cancel()}
>
Cancel
</Button>
</>
);
}Each component is its own namespace. Sub-components, variant enums, and prop types are all attached directly to the component you import. One import gives you access to the whole component surface with no hunting through sub-packages.
import { Button } from "@your-org/ui";
// Sub-components
Button.Primary
Button.Secondary
// Variant enum for dynamic usage
Button.VARIANT.Secondary
// TypeScript types
Button.PropsThe component pack
Beyond the original component set, the library includes a further pack of components, grouped into two areas. Core components are general-purpose interface pieces: headers, empty states, form wrappers, tables, and more. AI components are built for chat and agent interfaces: chat messages, replies that stream in, and displays for a running tool call. Together with the original set, the full library totals 58 components.
How pack components reach a project
The component picker lists all 58 components for every project, new or existing. The picker reads one shared list with no per-project scope, so there is no adopt step, no publisher account, and no opt-in. A project you started years ago shows the same 58 choices as a project you start today.
A newly created project starts with all 58 components already added. The Setup Wizard's starter template pre-adds them, so a new project has the full set in place with no extra step.
An existing project is not changed by this. Nothing is added to it automatically, and no token is created on its behalf. An existing project keeps only the components it already had, until you add a pack component yourself from the picker, exactly the way you add any other component today.
Core components
Core components are general-purpose interface pieces. Use them in any project, the same way you use the original built-in components.
| Component | What it is |
|---|---|
| Form field wrapper | A label, an input, and helper or error text, grouped as one field. |
| Empty state | An icon, a title, a description, and one action, shown when there is no content to display. |
| Section header | A title, an optional description, and an optional action, marking the start of one section on a page. |
| Link | Text styled and bound to a destination, for navigating rather than triggering an action. |
| Callout | A short note with an icon, a title, and an explanation, set apart from surrounding content. |
| Notification item | One row in a list of notifications or tasks, with an image, a title, and a meta line. |
| Page header | The title at the top of a page, with an optional description, an action row, and an optional summary panel. |
| Top nav bar | The navigation strip that stays at the top of the page, with a brand image and slots for links and an action. |
| Code block | A fixed block of monospaced code or preformatted text. |
| Popover | A trigger that opens a small panel anchored beside itself. |
| Action sheet | A trigger that opens a list of actions from the bottom of the screen. |
| Bottom sheet | A trigger that opens a content surface from the bottom of the screen. |
| Progress overlay | A centered modal that reports the progress of one operation. |
| Image | One image at a fixed aspect ratio, with its own loading and error states. |
| Search bar | A query field with a clear action, for filtering or searching a list as you type. |
| OTP input | A single field for entering a one-time passcode or PIN. |
| NumberInput | A numeric field with buttons to step the value up or down, within a minimum and a maximum. |
| List item | A vertical list of rows, each with a thumbnail image and a label. |
| Avatar group | A row of overlapping avatars, one per person. |
| Stepper | A multi-step indicator with previous and next controls. |
| Pagination | A strip of page-number controls with previous and next controls. |
| Combobox | A query field that filters a list of options as you type, with one selected value. |
| DataTable | A sortable, multi-select grid of rows and columns, with keyboard navigation across the rows. |
| Date picker | A calendar grid for one month at a time, with previous and next month controls. |
| Stat tile | One named metric shown with an animated numeric value. |
Known limits
- Bottom sheet: has no drag-to-dismiss. It opens and closes on the trigger action only. There is no swipe gesture to dismiss it.
- Code block: has no syntax highlighting and no copy-to-clipboard action. Its text carries one text style and one color for the whole block, so coloring individual words or tokens is not something the current building blocks can express.
- Responsive switching: no component switches its own layout automatically for a smaller screen. DataTable's card layout for narrow screens is a variant you choose yourself when you use the component. It never switches on its own based on screen size or platform.
- DataTable: does not reuse the built-in Table component. Table hides its rows behind one fixed shape, and DataTable's sorting, multi-select, and keyboard navigation each need to address one row directly, so DataTable is built from smaller pieces instead.
- Popover, Action sheet, Bottom sheet, Progress overlay, and Combobox, on a native mobile app: these components have no true overlay layer, no focus trap, and no scroll lock outside the browser. The developer package intentionally does not depend on any native-only overlay library.
AI components
AI components are built for chat and agent interfaces: showing a message, a reply that streams in, or the state of a running action.
| Component | What it is |
|---|---|
| Chat message | One message in a conversation, with a sender image, a name, and the message text. |
| Thinking indicator | A pulsing icon that shows an AI or a process is working on a reply. |
| Streaming text | A block of text that types itself in as it arrives, such as an AI reply. |
| Approval card | A request for a yes-or-no decision, with a pending, submitting, failed, and resolved state. |
| Tool use display | One named tool call and its progress, from queued through running to a result or a failure. |
| Prompt bar | A growing message field with a send action, for composing a message to an AI assistant. |
Using tokens directly
All token objects are exported from a single tokens export. Destructure what you need and apply values directly to any React element via inline styles.
import { tokens } from "@your-org/ui";
const { color, spacing, foundation } = tokens;
function Card() {
return (
<div style={{
color: color.text.default,
padding: spacing.padding.default,
borderColor: color.border.default,
}}>
Custom element
</div>
);
}color and spacing are semantic tokens: CSS variables that update automatically when the active theme changes. foundation gives you the raw palette values, which are theme-invariant. All are fully typed; your IDE will suggest paths and show the resolved default-theme value for each token as a hint.
Because semantic tokens resolve to live CSS variables, elements that use color or spacing must render inside the provider. foundation values are plain literals, so they work anywhere.
Switching themes
Every theme you have configured is exported as a member of the Themes enum, keyed by its slug (lowercase, for example Themes.dark or Themes["high-contrast"]). Your IDE will suggest the available values and flag invalid ones at compile time; the enum updates with every release as you add, rename, or remove themes.
import { ZakladProvider, Themes } from "@your-org/ui";
// Pass a theme at the root — everything inside inherits it
<ZakladProvider theme={Themes.dark}>
{/* ... */}
</ZakladProvider>
// Or switch at runtime — store the active theme in state
const [activeTheme, setActiveTheme] = useState(Themes.light);
<ZakladProvider theme={activeTheme}>
<button onClick={() => setActiveTheme(Themes.dark)}>
Switch to dark
</button>
</ZakladProvider>The provider defaults to the theme you marked as default in the Themes editor. Passing no theme prop is the same as passing the default explicitly.
To render a region in a different theme from the rest of the app — an inverted box, a dark hero, a preview card — wrap it in the Theme component (singular) and pass the same Themes enum to its name prop. Everything inside resolves to that theme, no matter the active app theme.
import { ZakladProvider, Theme, Themes } from "@your-org/ui";
<ZakladProvider theme={Themes.light}>
{/* this region is always dark, even though the app is light */}
<Theme name={Themes.dark}>
<Hero />
</Theme>
</ZakladProvider>Text styles
Text styles are exported as a namespaced Text component. Using <Text> with no variant defaults to <Text.BodyDefault>. All text components are automatically responsive: font size, line height, and spacing step up or down with the active XS–XL tier unless you override it.
import { Text } from "@your-org/ui";
// <Text> with no variant is shorthand for <Text.BodyDefault>
<Text>The quick brown fox jumps over the lazy dog.</Text>
<Text.BodyDefault>The quick brown fox jumps over the lazy dog.</Text.BodyDefault>
<Text.BodySmall>The quick brown fox jumps over the lazy dog.</Text.BodySmall>
<Text.CaptionDefault>Helper caption</Text.CaptionDefault>
<Text.CaptionSmall>Helper caption</Text.CaptionSmall>The available names match the text styles you have configured in the Typography editor. Every name is a property on the Text component, fully typed, so your IDE will suggest them.
Icons
Icons live in the /icons subpath. Import the whole set as Icon and reference each one by name. Only the icons you actually use are bundled.
import * as Icon from "@your-org/ui/icons";
// Render an icon
<Icon.ArrowRight />
// Choose a variant and size
<Icon.ChevronRight variant={Icon.VARIANT.Solid} size={Icon.SIZE.Large} />
// Pass one to a component slot
<Button.Primary leadingIcon={Icon.ChevronRight.Solid}>Continue</Button.Primary>Each icon takes a variant (when it has more than one), a size (an Icon.SIZE name or a raw pixel number), and an optional title that makes it readable to screen readers.
// Smallest bundle: import only the icons you use
import { ArrowRight } from "@your-org/ui/icons";
// Data-driven names (e.g. from a CMS): use DynamicIcon
import { DynamicIcon } from "@your-org/ui/icons";
<DynamicIcon name={iconName} size={Icon.SIZE.Medium} />DynamicIcon opts into the full icon set, so it does not tree-shake. Reach for it only when the name is not known ahead of time. Passing true to a component's icon prop still uses whichever default was configured for that slot in the Components editor.
Breakpoints
The provider drives tier-aware component sizing through five default viewport breakpoints:
| Tier | Viewport width | Typical target |
|---|---|---|
| XS | < 768 px | Mobile |
| S | 768 – 1032 px | Large mobile / small tablet |
| M | 1033 – 1439 px | Tablet / small desktop |
| L | 1440 – 1919 px | Desktop |
| XL | >= 1920 px | Large desktop / 4K |
To override the defaults, pass a media prop to the provider:
import { ZakladProvider, type ZakladMediaConfig } from "@your-org/ui";
const media: ZakladMediaConfig = {
xs: { maxWidth: 639 },
s: { minWidth: 640, maxWidth: 1023 },
m: { minWidth: 1024, maxWidth: 1279 },
l: { minWidth: 1280, maxWidth: 1535 },
xl: { minWidth: 1536 },
};
<ZakladProvider media={media}>…</ZakladProvider>Cross-platform
The package is built platform-neutral from the ground up. Components are authored against a shared API with platform-specific implementations behind it: web components render HTML with CSS custom properties, and React Native components use StyleSheet values resolved from the same token set. One token vocabulary drives both, with no separate imports or conditional platform code in your app.
// One import, same component API — on web and React Native
import { Button } from "@your-org/ui";Web is fully supported through the package's browser and neutral builds (React SPAs and SSR alike). React Native and Expo are now supported too: the package ships a dedicated native build and a react-native export condition, so Metro resolves the native bundle automatically. There is no per-platform import and no conditional code in your app.
On a React Native or Expo project, install the two native peers the package leaves to you (your app already has react-native):
# Expo
npx expo install react-native-svg expo-font
# Bare React Native
npm install react-native-svg expo-fontThen wrap your app in the generated provider exactly as on web. On native the provider calls loadFonts() on mount and gates render on the result, so your fonts register through expo-font automatically (text falls back to system fonts until loading completes). The web bundle is untouched and behaves exactly as before.
import { AcmeProvider } from "@your-org/ui";
export default function App() {
return (
<AcmeProvider>
{/* your screens */}
</AcmeProvider>
);
}Exporting your tokens
The whole token set can be exported as DTCG JSON (the Design Tokens Community Group format, an open standard). Export the current live state at any time, or the exact frozen tokens of any published release.
There are two reasons it exists. The first is flexibility: if you already have your own build setup and would rather transform the raw tokens yourself, the data is right there in a portable, standard shape.
The second is the important one, and it is deliberate. It is a clean exit. If you ever want to take your system and run it entirely on your own infrastructure, your tokens are yours and they leave in an open format. The platform is built to help you create and maintain a design system, not to trap you in one. For most teams the generated package, the Figma library, and the live MCP server are a better path than wiring all of that up by hand, but if you want out, the door is open.