Most design systems are written for people. Astryx is written for people and the agents working next to them — and that is why it is suddenly everywhere. meta/astryx (MIT) is Meta's open-source design system: 15,484 stars, 863 of them gained on November 8 alone, sitting at number six on GitHub's daily trending page. It grew inside Meta for eight years, where it became the company's most-used design system, powering more than 13,000 apps. The pitch is not more components — it is that the API, the docs, and the CLI are designed together so a person and an AI assistant build the same way, from the same reference.

This guide installs Astryx 0.7.11, builds a working Vite app with its components, wires the CLI into a coding agent, scaffolds a full analytics dashboard from a template, and themes the result. Every step below was run on November 8, 2026, with Node v24.22.1, Vite 8.3.6, and React 19.3.1. No account, no API key, no GPU, no model bill.

What you'll need

Node.js 22.13 or later — check with node --version — and about 30 minutes. Astryx requires React 19 or later; the Vite scaffold in Step 1 installs React 19.3.1. Everything is free and open source (MIT). The versions pinned in this guide: @astryxdesign/core 0.7.11, @astryxdesign/theme-neutral 0.7.11, @stylexjs/stylex 0.19.1, @astryxdesign/cli 0.7.11. The CLI reports 174 components; the README says 150+. Seven themes ship with the system: neutral, butter, chocolate, matcha, stone, gothic, and y2k.

Step 1 — Install Astryx

Scaffold a Vite + React + TypeScript app, then install Astryx, a theme, and the StyleX peer dependency the core package needs:

npm create vite@latest astryx-demo -- --template react-ts
cd astryx-demo
npm install
npm install @astryxdesign/[email protected] @astryxdesign/[email protected] @stylexjs/stylex
npm install -D @astryxdesign/[email protected]

Two details matter here. First, do not pin StyleX to the 0.4.x version that appears in the README's CDN example — that example is stale. Core 0.7.11 declares a peer dependency of @stylexjs/stylex@^0.19.0, and pinning 0.4.2 fails with an ERESOLVE conflict. Let npm resolve it; here it settled on 0.19.1. Second, always use the scoped CLI name. The unscoped astryx package on npm is an unrelated project; the commands below all run as npx @astryxdesign/cli …, or as npx astryx … once the CLI is a dependency (it registers an astryx binary).

Confirm the setup is healthy before you write any code. The CLI's doctor checks the Node version, the installed packages, their version alignment, and the theme wiring:

npx astryx doctor

On the demo project it reported 11 passes, 0 failures, 10 informational notes, and the summary line "All checks passed. Your Astryx setup looks healthy." It even detected the theme from the CSS import before any component was used — a useful tripwire if a later edit breaks the wiring.

Step 2 — Your first working example

Astryx ships pre-built CSS and JavaScript, so there is no build plugin, no PostCSS config, and no Babel setup. Three stylesheets, in this order, plus a theme provider. The order maps to the layer cascade: reset, then component styles, then theme token overrides.

Replace src/index.css with:

@import '@astryxdesign/core/reset.css';
@import '@astryxdesign/core/astryx.css';
@import '@astryxdesign/theme-neutral/theme.css';

body {
  font-family: var(--font-family-body);
}

The font rule is not optional. Nothing in the three stylesheets sets a document font — the theme styles prose elements, each component styles itself, and Button inherits — so without it your labels render in the browser's default serif. The theme names Figtree, but never loads it, so add the webfont link to index.html alongside a data-astryx-theme attribute, which is what the theme CSS is scoped to:

<html lang="en" data-astryx-theme="neutral">
  <head>
    <link rel="stylesheet"
      href="https://fonts.googleapis.com/css2?family=Figtree:wght@400;500;600;700&display=swap" />
  </head>

Wrap the app in the theme provider in src/main.tsx:

import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { Theme } from '@astryxdesign/core/theme'
import { neutralTheme } from '@astryxdesign/theme-neutral/built'
import './index.css'
import App from './App.tsx'

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <Theme theme={neutralTheme}>
      <App />
    </Theme>
  </StrictMode>,
)

Then build a small reading list in src/App.tsx with five components — Button, Card, Stack, Heading, and Text — using only props verified against the CLI's component docs:

import { useState } from 'react'
import { Button } from '@astryxdesign/core/Button'
import { Card, Stack, Heading, Text } from '@astryxdesign/core'

const ARTICLES = [
  {
    title: 'Why agents need a design system',
    meta: 'Components with one documented API, so a person and an agent build the same way.',
  },
  {
    title: 'Theming without wrapping',
    meta: 'A theme is a set of CSS custom property overrides — no forks, no wrapper components.',
  },
  {
    title: 'Swizzle when you need to own it',
    meta: 'Eject a component’s full source into your project and customize it directly.',
  },
]

export default function App() {
  const [saved, setSaved] = useState(0)
  return (
    <Stack padding={6} align="start">
      <Heading level={1}>Reading list</Heading>
      <Text>Three articles, tracked with Astryx components.</Text>
      <Stack gap={3} align="start">
        {ARTICLES.map((a) => (
          <Card key={a.title} maxWidth={480} elevation="low">
            <Stack gap={3} align="start">
              <Heading level={2}>{a.title}</Heading>
              <Text>{a.meta}</Text>
              <Button label="Open article" variant="primary" />
            </Stack>
          </Card>
        ))}
      </Stack>
      <Button
        label={saved === 0 ? 'Save all three' : `Saved ${saved} of 3`}
        variant="primary"
        onClick={() => setSaved(3)}
      />
    </Stack>
  )
}

Two prop facts worth knowing: Button's label is required (it doubles as the accessible name), and its variant accepts primary, secondary, ghost, and destructive — with secondary as the default, so the primary calls above are deliberate. Card takes maxWidth and an elevation of none, low, med, or high (default none); Stack takes numeric padding and gap steps from the spacing scale.

Build it. The standard scaffold script runs the TypeScript compiler first, then Vite:

npm run build   # tsc -b && vite build

It passed here in 2.48 seconds: 3,076 modules transformed, a 285.83 kB stylesheet (the three Astryx stylesheets, compiled in), and a 483.22 kB JavaScript bundle. Serve the output and confirm it responds:

npx vite preview --port 4173
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:4173/   # 200

The served page carries the theme attribute, the built CSS contains the Figtree font and the --color-text-primary token, and the bundle contains the reading-list heading. That is the whole loop — install, provider, components, build, serve — and every part of it ran.

Four-stage workflow diagram: install the packages, wrap the app in a theme provider, compose components, build and deploy
AI-generated diagram: install, provide a theme, compose components, build.

Step 3 — Make your coding agent use it

The components are the smaller half of Astryx. The larger half is the CLI, and its most important command is init:

npx astryx init

It writes the Astryx component index into your project's agent instructions — AGENTS.md and GEMINI.md in the run here (the README says AGENTS.md/CLAUDE.md; the CLI wrote GEMINI.md instead). The file tells an agent the setup it must not skip (the two CSS imports, without which components render unstyled), the workflow to follow for every page, and the rules: no raw <div> for layout, style with component props first and tokens second, never a hardcoded hex value or pixel, and a self-check that re-reads the file for violations before delivering. The workflow itself is three steps:

astryx build "your page idea"     # names the template and the parts it lacks
astryx template <name> <path>     # scaffold it; keep its frame, replace the content
astryx component <Name>           # read the props before using a component

Try the first step on an idea and watch it reason about the page:

npx astryx build "analytics dashboard"

It matched the dashboard template ("three-band analytics: headline tiles, then charts, then supporting tables, all re-reading from one global filter bar"), offered two alternatives for different shapes (dashboard-cohort-funnel for retention questions, dashboard-scorecard for report-shaped pages), and listed blocks for the parts the template lacks, such as a date-range picker with presets. That is the whole point of the system: the agent does not guess at layout. It starts from a template, fills gaps with blocks, and reaches for components last.

When the agent — or you — needs a component's contract, the CLI is the reference. List everything, then read one component in full:

npx astryx component --list    # 174 components
npx astryx component Button    # props, variants, examples, best practices

The Button doc is 161 lines: the prop table (label required, variant, size sm/md/lg, isLoading, isDisabled, icon, onClick), do/don't guidance for labels, and contrast requirements for text and icons. There is also a ranked search across components, hooks, docs, and templates — npx astryx search "chart" returned 20 of 22 matches, including the useTheme hook for reading token values in JavaScript (for chart libraries that need values, not CSS variables). For token-efficient reads, add --dense; for machine-readable output, --json.

A robot assistant handing UI components from a terminal window to a browser showing an analytics dashboard
AI-generated illustration: the CLI hands your agent the same components you use.

Step 4 — A real end-to-end use case: the analytics dashboard

A reading list proves the wiring. A dashboard proves the system. Browse the page templates first — the list includes ai-chat, login, blank, library (card grid), centered-hero, payment-form, checkout-wizard, and canvas-editor, among others — then scaffold the dashboard:

npx astryx template --list
npx astryx template dashboard src/pages/Dashboard.tsx

Two notes: the path must be relative to the project root — an absolute path is rejected — and the command prints exactly what the template needs that you do not have yet. For the dashboard it was:

Install missing dependencies: npm install recharts@"^3.9.2" @heroicons/react@"^2.2.0"

Install them, then render the template from your app and build:

npm install recharts@"^3.9.2" @heroicons/react@"^2.2.0"
# src/App.tsx
import DashboardTemplate from './pages/Dashboard'

export default function App() {
  return <DashboardTemplate />
}
npm run build

The scaffolded file is 803 lines: a three-band analytics page — headline tiles, charts, supporting tables — built from Layout, Card, Table, Grid, ProgressBar, and Divider, with recharts for the charts and Heroicons for the glyphs. The build passed in 5.17 seconds with a 1,020 kB JavaScript bundle (recharts is the bulk of it; Vite warns about the chunk size, which is expected for a chart-heavy page and harmless here). Served on port 4173, the page returns 200 and the bundle contains the template's chart data. What you have at this point is a credible analytics dashboard — filter bar, tiles, charts, tables — that you did not lay out by hand and your agent did not improvise. If the dashboard layout is wrong for your page, astryx build from Step 3 is the way to find the right template instead of reshaping this one.

Step 5 — Make it yours: themes

A theme in Astryx is a set of CSS custom property overrides — no fork, no wrapper components. Seven ship with the system. The fastest way to try one is the CLI-managed path, which installs the theme package and generates a module that wires it in:

npx astryx theme add neutral   # or butter, chocolate, matcha, stone, gothic, y2k

On the demo project this generated src/astryx-themes.ts and reported the theme's module (@astryxdesign/theme-neutral/built), stylesheet, and font stylesheet. The direct-import path from Step 2 — importing neutralTheme and the theme CSS yourself — is the same wiring done by hand, and both are valid.

For a custom theme, use defineTheme with token overrides. Token values are [light, dark] pairs — every color token uses light-dark() for automatic mode switching. This minimal theme, with two literal token overrides, was built and verified in the demo project:

import { defineTheme } from '@astryxdesign/core/theme'

export const myTheme = defineTheme({
  name: 'my-theme',
  tokens: {
    '--color-background-surface': ['#f4f3ec', '#16171d'],
    '--color-text-primary': ['#08060d', '#f3f4f6'],
  },
})

Swap it into the provider — <Theme theme={myTheme}> — and the build passes with the theme name in the bundle. The token names come from the CLI's reference (npx astryx docs tokens), which covers color, data visualization, syntax, spacing, size, border, focus, radius, shadow, and motion tokens. For a full coordinated palette instead of one-off overrides, the docs point to npx astryx theme palette generate (npx astryx docs theme creating-a-custom-theme has the walkthrough). The rule the system enforces is worth internalizing: palettes and custom colors go through the theme, never through :root overrides.

Step 6 — When you need to own a component: swizzle

Astryx components are built to be composed at any level, and the building blocks are exported directly — but sometimes you need to change the component itself. Swizzle ejects a component's full source into your project:

npx astryx swizzle Button   # copies 5 files to components/astryx/Button/

One warning, verified the hard way by reading the command's own output: swizzled components use StyleX and require a StyleX compiler in your build. Without one, importing them throws "Unexpected 'stylex.create' call at runtime" and the page does not render. The pre-built components from Step 2 need no compiler; only ejected source does. Treat swizzle as a fork, not a tweak — use it when the component's behavior genuinely differs, and prefer props and theme tokens for everything else.

When to use this vs alternatives

Versus shadcn/ui. shadcn copies component source into your repo and you own it from day one. Astryx keeps components as a dependency and reserves copying for swizzle. If you want to own every line of UI code, shadcn's model fits better; if you want versioned updates, a CLI, and agent docs, Astryx does.

Versus MUI or Chakra. Both are mature React component libraries with their own theming. Astryx's difference is not the component count — it is the agent workflow: the CLI, the generated agent instructions, the templates, and the rule that the same documented API serves a person and an agent. If your team does not use coding agents, that advantage mostly disappears, and the choice becomes a matter of taste and ecosystem.

Versus Tailwind alone. Tailwind gives you utilities, not components. Astryx gives you 174 accessible components with documented props, plus tokens that map to Tailwind utilities through the tailwind-theme.css bridge if you want both. Use Tailwind for one-off layout; use Astryx when the same patterns repeat across pages.

Versus extracting a design system. That is a different job, and this blog covered it the same day: designlang extracts a design system from any live website. The two compose naturally — extract a brand's tokens with designlang, then build with Astryx using those tokens in a custom theme.

When not to use it. Astryx requires React 19, so a React 18 codebase is out until it upgrades. For a single static page, the system's weight — a 285 kB stylesheet in the demo build — is overkill; the CDN path (npx astryx template --cdn writes a pinned, annotated page) exists for prototypes, but a hand-rolled page is simpler still. And if your build cannot run a StyleX compiler, do not swizzle.

The takeaway

Astryx is a design system that treats the coding agent as a first-class user: the CLI writes the component guide into your agent's instructions, the templates replace layout guesswork, and the same documented props serve both sides of the keyboard. Install it, prove the loop with a five-component page, let init teach your agent the system, and scaffold real pages from templates instead of composing from scratch. The whole verified path — install, provider, components, dashboard, custom theme — runs in about half an hour, costs nothing, and leaves you with a UI your agent can extend without inventing its own.

Sources