Library / SDK
ng-primitives/ng-primitives avatar
ng-primitives/ng-primitives

Angular Primitives: headless UI behaviour for Angular design systems

Headless, composable primitives for building accessible UI components in Angular.

611 stars82 forksTypeScriptApache-2.0

At a glance

What is it?
ng-primitives is an Apache licensed headless library of composable, accessible primitives for Angular, and its pre-1.0 versioning means the API is still moving, which is the main thing to weigh before adopting it.
Who is it for?
Angular Primitives is the right choice for an Angular team that owns its visual layer and wants accessible behaviour such as focus handling, keyboard interaction and ARIA wiring without writing it for every component, and it suits a team building a design system from scratch. It is the wrong choice for a team that wants styled components out of the box, for a project that is not on Angular, and for anyone who cannot absorb breaking changes arriving in minor versions.
Can I use it commercially?
Yes. Apache-2.0 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 1 day 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 October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Behaviour without markup, for people who own the visual layer

Angular Primitives is described as a headless UI library with a focus on accessibility, customization and developer experience, and the repository description calls it headless, composable primitives for building accessible UI components in Angular.

Headless means the library handles behaviour and leaves appearance to you. There is no theme, no stylesheet, no button component with a rounded rectangle baked in. What you get is the part of a component that is easy to get wrong: which element has focus, what the keyboard does, what the ARIA attributes say, when a panel opens and where focus goes when it closes.

The stated audience is explicit. The project says its primitives are there to support you whether you are building a design system from scratch or looking to enhance an existing one. That is a narrower and more useful claim than a component library makes, because the hard part of a design system is not the visuals, which a team can draw, but the interaction model, which every team reinvents badly.

The topics reinforce the direction. Alongside headless and ui there are accessibility, aria and wcag, plus design-system and components, and the names cdk, headless-ui and radix-ui appear as references to the idea the project is working within rather than as dependencies.

The documentation lives at angularprimitives.com, and there is a Discord server for the community around it.

Accessibility is the product, not a checkbox

The positioning is worth being careful about, because accessibility libraries often promise more than they deliver.

What the readme actually says is that the library is headless with a focus on accessibility, customization and developer experience, and the topic list includes aria and wcag. That tells you the intent and the vocabulary. It does not tell you which parts of the accessibility guidelines are covered, how much of a given pattern is implemented, or how the behaviour behaves with a screen reader, and the readme does not enumerate that.

The honest read is that this is a library whose components are built with accessibility as the design constraint rather than as an add-on, so a team that builds its own buttons, menus, dialogs and listboxes gets correct patterns without reimplementing them. A team that wants a printed list of satisfied success criteria is not going to find it here, and the documentation site is where that level of detail would live.

There is also an ergonomic argument that is separate from the guidelines. In a headless component, the ARIA wiring and the keyboard handling are written once by the library, so every component in your system behaves the same way. The alternative is every team member writing their own focus trap, and every screen reader user finding a slightly different menu.

What a team should do before committing is pick the two or three patterns they care most about, build them with the primitives, and test them with the assistive technology the users actually use. A library that handles accessibility well is worth a lot, and a library that handles it inconsistently is worth less than the readme implies.

Installing the package and matching your Angular version

Installation is a single npm command.

bash
npm install ng-primitives

The published package name is ng-primitives, and the compatibility table in the readme tells you which Angular versions each release line supports. The early range, 0.1.0 to 0.18.0, targets Angular 18.x.x. The range 0.19.0 through 0.89.x covers Angular 19.x.x and 20.x.x. The range 0.90.0 through 0.120.x covers Angular 20.x.x and 21.x.x. And from 0.121.0 onwards, the library targets Angular 21.x.x and 22.x.x.

Two things follow from that table. The library moves with Angular, so an Angular upgrade is also a primitives upgrade, and the current release sits in the newest band, which covers the two most recent Angular majors. The second is that the version numbers of the two projects are not aligned at all, so the mapping is the only way to know whether your Angular version is supported, and you have to look it up rather than infer it.

For a first use, the sensible approach is to add the package to one feature area, implement a single pattern such as a menu or a dialog with it, and see how the API feels against your own components before spreading it across the application.

An Nx monorepo with pnpm, a local registry and a hotfix script

The repository is a monorepo, and the root package.json shows how the pieces are wired.

json
{
  "name": "@ng-primitives/source",
  "version": "0.0.0",
  "license": "Apache-2.0",
  "private": true,
  "scripts": {
    "prepare": "husky",
    "start": "nx serve documentation",
    "lint": "nx run-many -t lint",
    "test": "nx run-many -t test",
    "test:browser": "nx run ng-primitives:test-browser",
    "test:node": "nx run ng-primitives:test-node",
    "build": "NODE_OPTIONS='--max-old-space-size=8192' nx run-many -t build",
    "release:hotfix": "node scripts/hotfix.mjs"
  },

The root package is private and versioned at zero, which is what you expect in a workspace where the version lives on the published packages. Nx drives everything: a documentation site served with nx serve documentation, formatting, linting and tests across all projects, and separate browser and node test targets. The build script raises the Node heap to 8192 megabytes, which is a hint that the build is not small.

Two details stand out. There is a hotfix release script that runs a Node script, which pairs with the release history, where patch versions arrive days apart, and a .verdaccio directory, which is a local package registry, used so that packages can be consumed from the workspace before they are published.

The tooling around it is current: pnpm for the lockfile, Nx 22, Angular 21, Analog with Vite and Vitest, ESLint with the Angular rulesets, SWC, and Tailwind CSS 4 for the documentation site.

Pre-1.0 means the API is still moving

The readme says plainly that the library is in active development and that the team is working towards a 1.0 release, and asks for help in testing, giving feedback and contributing.

It then states the consequence: until 1.0 is reached, some APIs may change, and breaking changes may be introduced in minor versions, with an effort to keep them to a minimum. That is an unusual and honest thing to publish, and it is the single most important line in the readme for an adopter.

The version numbers make the pace visible. The recent releases are v0.130.5 on 2026-09-28, v0.131.1 on 2026-09-30 and v0.131.2 on 2026-10-03, and the last push to the default branch, which is named next, was on 2026-10-16. The repository is not archived. Patch releases two days apart indicate an active release line rather than a slow one, and a 0.x version with breaking changes allowed in minors is a different contract from a 1.x version.

The repository also carries a CHANGELOG.md and a document with a specific name, STATE_PATTERN_MIGRATION.md, which suggests a state management pattern in the library that has changed shape and needed a migration note. That is a hint about the scale of the API churn in flight, and it is worth reading before you commit to a pattern for holding open and closed state in your own components.

Where Angular Primitives is the wrong tool

Four cases are worth ruling out.

The first is a team that wants styled components. Headless means every pixel, every icon and every animation is your code. A team that expects a dialog to look finished on the day it installs will find it unfinished, and will have to decide whether to build those styles or to use a different library for the few components that matter most.

The second is a non Angular project. The primitives are Angular components and the compatibility table is a table of Angular versions. There is no React or Vue path, and there is no framework agnostic core described in the readme.

The third is a team that cannot take API churn. If your release process pins dependencies and reviews them, a library that says breaking changes can arrive in a minor version is a different kind of dependency from a stable one. You can mitigate that with a lockfile and a deliberate upgrade cadence, but the mitigation is your work.

The fourth is a project that has already solved this. A mature design system in an Angular codebase usually has its own focus management and keyboard handling in place. Migrating to a headless library to replace working code is a rewrite with no functional gain, and the readme does not describe a path for adopting primitives incrementally inside an existing component library, only the general claim that it supports enhancing an existing design system.

Licence, contribution model and what the project asks of you

The licence is Apache-2.0, stated in the root package.json and carried by a LICENSE file in the repository. That is a permissive grant with an express patent clause, and it lets you use the library in a commercial product without copyleft obligations on your own code. This is not legal advice, and a company with a licence review process should still run its own check.

The contribution model is visible in the repository layout. There is a CONTRIBUTING.md, a CODE_OF_CONDUCT.md, an AGENTS.md and a CLAUDE.md, a .claude directory, a .mcp.json file, a context7.json file and a .husky directory for git hooks. A codecov.yml file and a CI workflow badge indicate that coverage is tracked. Several people are credited in the contributors list, with a mix of code, documentation and planning roles, which suggests an active group rather than a single maintainer.

What the project asks of a user is fairly modest. Test it, report what breaks, and expect the API to move. The README explicitly says that until 1.0 some APIs may change, and it invites help in testing and providing feedback.

The practical way to adopt it is therefore to treat it as a moving dependency for the duration of the 0.x line. Pin a version, read the changelog on every upgrade, and keep your own components thin enough over the primitives that a future change to an API is a small edit rather than a rewrite.

Editorial conclusion

Angular Primitives is the right choice for an Angular team that owns its visual layer and wants accessible behaviour such as focus handling, keyboard interaction and ARIA wiring without writing it for every component, and it suits a team building a design system from scratch. It is the wrong choice for a team that wants styled components out of the box, for a project that is not on Angular, and for anyone who cannot absorb breaking changes arriving in minor versions. Before adopting, check the compatibility table against the Angular version you run, read CHANGELOG.md and STATE_PATTERN_MIGRATION.md for the shape of recent changes, and expect to revisit the pinned version when the 1.0 release lands.

Frequently asked questions

What is ng-primitives?

Angular Primitives is a headless, composable library of primitives for building accessible UI components in Angular, with a focus on accessibility, customization and developer experience. It is aimed at teams building a design system from scratch or enhancing an existing one, and it ships no styling of its own.

How do I install Angular Primitives?

The installation step is npm install ng-primitives. The compatibility table maps library versions to Angular versions, with 0.1.0 to 0.18.0 for Angular 18.x, 0.19.0 to 0.89.x for Angular 19 and 20, 0.90.0 to 0.120.x for Angular 20 and 21, and 0.121.0 and later for Angular 21 and 22.

Is Angular Primitives stable enough for production?

The project states that it is in active development and working towards a 1.0 release, and that until 1.0 some APIs may change and breaking changes may be introduced in minor versions, with the effort to keep them to a minimum. Recent releases include v0.130.5, v0.131.1 and v0.131.2.

What licence does ng-primitives use?

The licence is Apache-2.0, as declared in the root package.json and stored in the LICENSE file at the root of the repository. The repository also ships a CHANGELOG.md and a STATE_PATTERN_MIGRATION.md document for the state pattern changes.

How is the ng-primitives repository built and tested?

The repository is an Nx monorepo using pnpm, with lint, format, test, browser test, node test and build targets, a documentation site served with nx serve documentation, a hotfix release script and a Verdaccio directory for a local package registry. The published package itself is installed with npm install ng-primitives.

Official sources

  1. License: Apache-2.0
  2. ng-primitives/ng-primitives on GitHub
  3. Project website
  4. README
  5. Releases
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/ng-primitives-ng-primitives.svg)](https://hysenlabs.com/projects/ng-primitives-ng-primitives)

Was this useful?