Library / SDK
radix-ui/primitives avatar
radix-ui/primitives

Radix Primitives ships per-package versions, a next tag, and no changelog file

Radix Primitives is an open-source UI component library for building high-quality, accessible design systems and web apps. Maintained by @workos.

19,498 stars1,321 forksTypeScriptMIT

At a glance

What is it?
Radix Primitives is a low-level component library for accessible design systems, MIT licensed and maintained by WorkOS, published as one package per primitive across a pnpm workspace. The parts worth knowing before you contribute are in its tooling: the default test command is a watcher, the Playwright and axe suite is a separate e2e script, and the changelog lives on a website rather than in the repository.
Who is it for?
Radix Primitives fits a team that wants accessible behaviour it does not have to reimplement, and that will take primitives one at a time rather than adopting a whole design system, since the package layout is per primitive and the README states that incremental adoption is a supported route. It does not fit a team that wants styled components out of the box, since this is explicitly a low-level layer with no visual design attached.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 31 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • radix-ui/primitives19,498 ★ · +188
Star history
Repository2026-09-212026-11-14
radix-ui/primitives19.3k19.5k

188 Stars in the last 54 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

The install section is for cloning the repo, not for adding a primitive

The installation section is two commands:

bash
npm install -g pnpm

then, after installing the dependencies:

bash
pnpm install

Neither of those adds a component to a project. There is no `pnpm add` line anywhere in the README, and no package name, which tells you this section is aimed at someone who has already cloned the repository. A reader who searched for how to install a Radix component and copied these commands ends up with pnpm installed and their own project re-installed.

The consumer path is on the documentation site instead. The README points to `radix-ui.com/primitives/docs` for full documentation, and the repository layout explains why the two are different: `pnpm-workspace.yaml` and `packages/` describe a monorepo where each primitive is its own package, so the install command is per primitive rather than one command for the library. `docs/` is not in the top level list at all, which is consistent with the documentation being a separate site.

pnpm test is a watcher, and the accessibility gate is in test:ci

The two test scripts do different amounts of work:

json
    "test": "vitest",
    "e2e": "playwright test",

`pnpm test` is bare `vitest`, which is watch mode, so on its own it never exits. It also never reaches a browser. The browser tests are separate scripts that install Playwright's browser and run the Playwright suite.

The supporting scripts spell out how narrow that browser stage is. `e2e:install` runs `playwright install --with-deps chromium`, and `e2e` is `playwright test`, with `e2e:ui` for the interactive mode. One browser, not a matrix, and `@axe-core/playwright` is a dev dependency, so the accessibility assertions live in that Playwright layer next to `playwright.config.ts` and the `e2e/` directory.

The consequence for a contributor is concrete: running the default test command locally tells you nothing about keyboard navigation or focus behaviour, because the checks that catch those are in the Playwright suite. Run `pnpm e2e:install` and `pnpm e2e` before you believe a change is safe.

Two publish paths, and the changelog is on a website

Releases are handled with changesets. The root carries a `.changeset/` directory, `@changesets/cli` as a dev dependency, and scripts that cut both a stable and a prerelease line:

json
    "bump:stable": "changeset version && pnpm format",
    "bump:next": "changeset version --snapshot",
    "bump:check": "changeset status --since=main",
    "release:stable": "pnpm publish -r --access public",
    "release:next": "pnpm publish -r --access public --tag next",

So there are two npm tags in play, stable and `next`, and the prerelease line is produced by taking a snapshot of the versions. There is also a `release:stable:ci` variant that calls `changeset publish` instead, which is the path that publishes the changesets themselves.

What is absent is as informative as what is present. There is no `CHANGELOG.md` at the top level, alongside `.changeset/`, `release-process.md` and the other files, and the README sends you to a URL for the changelog: `radix-ui.com/primitives/docs/overview/releases`. A consumer tracking versions therefore reads a website rather than a file, and the choice between the stable tag and the `next` tag is theirs to make, which is worth doing deliberately rather than by taking whatever resolves.

The root package is private and named primitives at 0.1.0

The manifest at the repository root is not the package you install. It is `"private": true`, named `primitives`, at version `0.1.0`, and the build scripts filter on the workspace:

json
    "build": "pnpm -r --parallel --filter \"./packages/**/*\" run build",
    "build:ci": "pnpm -r --workspace-concurrency=4 --filter \"./packages/**/*\" run build",

Nothing under `packages/` is published from this manifest; each package there carries its own version, bumped by changesets. The root version is workspace bookkeeping and tells a consumer nothing.

That layout is also what makes the README's adoption claim workable. It says the components can be used either as the base layer of a design system or adopted incrementally, and a monorepo of per-primitive packages is the structure that makes incremental adoption possible, because you depend on the one dialog or one select you need rather than on a library surface. Note the two build variants while you are here: `build` is unbounded parallel, `build:ci` caps workspace concurrency at four, so a local build can spawn a process per package and a CI build cannot.

Storybook runs on port 9009 and SSR lives in a second workspace app

The library has no application, so its development surface is Storybook, and the port is not the default one:

json
    "storybook": "pnpm --filter=@repo/storybook run dev -p 9009 --no-open",
    "dev": "pnpm run storybook",
    "dev:ssr": "pnpm --filter=@repo/ssr-testing dev",
    "storybook:build": "BROWSER=none pnpm --filter=@repo/storybook run build",

Port 9009, launched with `--no-open` so a CI or container run does not try to start a browser, and `BROWSER=none` on the build for the same reason. The `dev` script is an alias for Storybook, which means running `pnpm dev` gives you the component gallery and nothing else.

Server side rendering is tested separately, through a second workspace package called `@repo/ssr-testing`, reachable only via `dev:ssr`. That is worth knowing if you are working on anything that behaves differently on the server, since a Storybook page will not surface it and the SSR app is a different entry point. The top level `apps/` and `internal/` directories sit alongside `packages/`, which is where those workspace entries are defined.

Lint and typecheck both run with --no-bail across the workspace

Two scripts carry the flag that changes how a failure reads:

json
    "lint": "pnpm -r --no-bail run lint",
    "types:check": "pnpm -r --no-bail --parallel run typecheck",

`--no-bail` tells pnpm not to stop the recursive run at the first package that fails, so a single invocation reports every offending package, and the exit code still reflects the failures. For a workspace with a package per primitive that is the right trade, because a shared type change in a base package tends to produce the same error in several places and stopping at the first one wastes a cycle.

The typecheck script is additionally `--parallel`, so type checking fans out across packages at the same time. Between them, the two commands are how you find out whether a change to a primitive is safe for every other primitive in the repository, and running only the package you edited will not tell you that.

Formatting and linting run on oxfmt and oxlint, not prettier and eslint

The tooling at the top level is a departure from the usual defaults, and it is visible in the file names before you read a script. There is `.oxfmtrc.json` and `.oxlintrc.json`, and the two formatting scripts are:

json
    "format": "oxfmt --write",
    "format:check": "oxfmt --check",

There is no prettier configuration and no eslint configuration at the root, and the scripts make no reference to either tool. A contributor arriving with the standard habits will create a `.prettierrc` and an eslint invocation that fights `format:check` in continuous integration rather than agreeing with it.

The `patches/` directory is the other signal. Under pnpm, a patched dependency is a real file in the repository that `pnpm install` applies, which is a deliberate way to carry a local fix to a transitive package. It also means a fresh clone has more moving parts than a plain lockfile install, and `pnpm-lock.yaml` plus `pnpm-workspace.yaml` at the root are the two files that define the resolution for everything else.

Visual regressions are caught by a hosted service, not by the test suite

The README credits Chromatic with providing the visual testing platform that helps the maintainers review UI changes and catch visual regressions. That is a hosted service in the loop, and it is separate from both the vitest suite and the Playwright suite. The automated checks catch behaviour and markup; the appearance of a primitive is reviewed through a service outside the repository.

Three files at the top level point at how this project is worked on beyond the code. `philosophy.md` holds the design stance behind a library whose stated focus is accessibility, customization and developer experience, and reading it is the fastest way to argue with a maintainer on a pull request. `AGENTS.md` is present alongside it. And `context7.json` at the root, plus `.nvmrc` for the pinned Node version, describe how the repository is consumed by tooling rather than what it exposes.

For a user of the library, the practical summary is that accessibility is asserted with `@axe-core/playwright` in the end to end suite, appearance is reviewed through Chromatic, and neither of those runs when you type `pnpm test`.

Editorial conclusion

Radix Primitives fits a team that wants accessible behaviour it does not have to reimplement, and that will take primitives one at a time rather than adopting a whole design system, since the package layout is per primitive and the README states that incremental adoption is a supported route. It does not fit a team that wants styled components out of the box, since this is explicitly a low-level layer with no visual design attached. Before adopting, check three things: whether the primitive you need is on the stable release rather than the `next` tag, since the project publishes both, what the changelog says on `radix-ui.com/primitives/docs/overview/releases` rather than in a repository file, and whether your own CI can run the same checks the project defines, which are `vitest` plus a Playwright install for chromium and the Playwright suite with `@axe-core/playwright`. The last push to the default branch was on 2026-10-14.

Frequently asked questions

What are Radix Primitives?

Radix Primitives is an MIT licensed, low-level open source UI component library for building accessible design systems and web apps, maintained by WorkOS. Its stated focus is accessibility, customization and developer experience, and the components are meant to be used either as the base layer of a design system or adopted incrementally.

How do I install Radix Primitives into my project?

The installation section in the README is aimed at cloning the repository, not at adding a component: it installs pnpm globally with `npm install -g pnpm` and then runs `pnpm install`, with no `pnpm add` command and no package name. Per-primitive installation is documented at radix-ui.com/primitives/docs, and the repository publishes one package per primitive from a pnpm workspace.

How does Radix Primitives test accessibility?

The repository carries an `e2e/` directory and a `playwright.config.ts`, with `@axe-core/playwright` as a dev dependency. The `e2e:install` script runs `playwright install --with-deps chromium` and `e2e` runs `playwright test`, so the accessibility checks run in that Playwright suite and not in the default test command, which is a `vitest` watcher.

Is Radix Primitives free to use?

The licence is MIT, with the copyright line reading 2022-present WorkOS. There is also an optional prerelease channel: the publish scripts include a `next` variant that publishes under the next tag, alongside the stable channel.

Who maintains Radix Primitives, and is it still being worked on?

The repository description and the licence line both name WorkOS as the maintainer, and the licence reads Copyright 2022-present WorkOS. Community links are a Discord server and the @radix_ui account on Twitter, and the last push to the default branch was on 2026-10-14.

Official sources

  1. Issues
  2. License: MIT
  3. Project website
  4. radix-ui/primitives on GitHub
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/radix-ui-primitives.svg)](https://hysenlabs.com/projects/radix-ui-primitives)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/radix-ui-primitives.svg)](https://hysenlabs.com/projects/radix-ui-primitives)

Was this useful?