Install
Set up a React app with nxds-cli, then add any component. For a Vue app,
follow Install by hand.
Set up your app
init sets up a Next.js App Router app or a Vite + React app that uses
TypeScript and React 18.3 or 19. It does every step in
Install by hand:
| Step | What init does |
|---|---|
| Tailwind 4 | Installs it, if your app doesn't have it yet |
| Token CSS | Copies it into components/nxds/tailwind/ and adds an nxds CSS entry beside your app |
| Appearance provider | Copies it with the cn helper, then adds it to app/layout.tsx or src/main.tsx |
| Packages | Installs @nxds/core, tw-animate-css, clsx and tailwind-merge |
| ESLint (Vite) | Turns off react-refresh/only-export-components for the nxds folder |
Your app's own styles stay as they are. init also writes nxds.json, which
records the nxds version and the items you installed.
If your app uses src/, nxds goes in src/components/nxds/. To choose
another folder, pass --dir.
(Optional) Set your theme
Each flag sets one part of your theme. To build the command, pick a theme in the Playground or the theme panel on any docs page, then open Use this theme.
| Flag | Values |
|---|---|
--brand-color | Any CSS color |
--surface-tone | stone, neutral, zinc (default), slate, gray |
--density | tight, compact, default (default), comfortable, relaxed, spacious |
--corners | square, subtle (default), smooth, round, extra-round |
--elevation | flat, quiet (default), soft, standard, strong |
--stroke | fine, normal (default), strong |
--mode | light (default), dark, system |
--light-contrast, --dark-contrast | 0 to 100 (default 50) |
--font | system (default), geist, inter, figtree, dm-sans, manrope, ibm-plex-sans, lora, playfair-display |
--heading-font | ui (the UI font, default), or any --font value but system |
--code-font | system (default), geist-mono, jetbrains-mono |
--font-size, --code-font-size | 8 to 32, in px (default 14 and 12) |
--icons | phosphor (default), lucide, tabler, hugeicons, remix |
init writes the theme to nxds-theme.ts and records it in nxds.json. It
installs the icon package and each font's Fontsource package, and imports the
fonts in the nxds CSS entry. Your appearance-state.ts spreads nxdsTheme, so
set your own overrides there. Don't edit nxds-theme.ts, since init
rewrites it.
To change the theme, run init again. A flag you leave out keeps its value.
An empty flag, such as --density=, resets that part to the default. A
new --icons rewrites lib/icons.ts in every installed component. A new font
replaces the old one's import, and doctor lists the old package to remove.
If an older init wrote your appearance-state.ts, init asks you to spread
nxdsTheme there once.
Add a component
add copies the component, the components it uses and the packages it needs,
from your app's nxds version. Each component page shows its add command.
Copied files are yours to edit. Import them from the nxds folder, such as
components/nxds/components/button/button.
Update components
diff lists the files that differ from the latest release. update moves
every installed item to it.
update replaces a file you haven't edited. If you edited a file, update
merges the release's changes into it. Line endings and trailing spaces don't
count as edits.
If you and the release changed the same lines, update marks them like a git
conflict. Keep the lines you want, delete the <<<<<<<, ======= and
>>>>>>> markers, then run update again. If a newer release comes out
before you resolve every conflict, update changes no files, keeps nxds.json
on your release and exits 3. To replace an edited file with the release's
copy, pass --overwrite.
Use React 18
nxds runs on React 18.3 and React 19. init reads the React your app uses and
records it in nxds.json, so add and update copy the files for it.
On React 18, each component that takes a ref is wrapped in forwardRef,
because React 18 drops ref from a function component's props. You pass ref
the same way on both.
After you upgrade to React 19, run update:
update switches the files to React 19 and merges the release's changes into
files you edited. Until you run it, add stops and doctor reports the
mismatch.
To install by hand on React 18, copy the files from the React 18 tab in a component page's Manual steps.
Find setup problems
doctor checks the provider, the CSS entry, the files, the packages and React,
and says how to fix each problem.
Options
| Option | What it does |
|---|---|
--dry-run | Shows what would change, without changing anything |
--json | Prints { ok, exitCode, data }, or { ok, exitCode, error } on failure |
--no-install | Skips installing npm packages |
--cwd <path> | Runs in another folder |
--registry <url|path> | Installs from another registry. NXDS_REGISTRY_URL sets it too |
Run npx nxds-cli@latest --help to list every command, or
npx nxds-cli@latest add --help for one command's options.
Behind a proxy, set HTTPS_PROXY. The CLI also reads HTTP_PROXY and
NO_PROXY.
| Exit code | Meaning |
|---|---|
0 | Done |
1 | Error, such as no network or an invalid release |
2 | Usage error, such as an unknown option or item |
3 | Done, with something left for you: conflicts, kept files, manual steps or failed checks |
Install by hand
If the CLI doesn't support your app, follow these steps. They're the steps
init and add run for you.
Copied components are your files: edit them freely, and paste a newer version to update.
Step 1: Install Tailwind 4
Tailwind 4 builds the nx: classes. These steps use a new Next.js 15 App
Router app. If your app already has a UI, follow
Add nxds to an existing app.
Remove the @import "tailwindcss" that create-next-app adds to
globals.css. The token CSS brings Tailwind's theme and utilities with the
nx: prefix. The prefix applies to the whole build, so a second import in the
same file doesn't give you unprefixed classes.
Step 2: Add the token CSS
The token CSS defines the colors, spacing and type scale. Copy the seven files
from packages/tailwind/ in the
nxds repo into
components/nxds/tailwind/:
Install the animation utilities that some components use to open and close:
Import Tailwind's Preflight, the token CSS and the animation utilities from your CSS entry point:
nxds ships no reset, so the page brings Preflight. nxds components need its
box-sizing: border-box.
Every Tailwind class in your app now takes the nx: prefix: nx:flex works,
flex does not. Only nxds tokens are available, so Tailwind's default colors
are gone.
Instead of nx:bg-red-500, use a token class like nx:bg-error-background.
To add your own tokens, put a @theme block in globals.css after the
imports. Don't edit nxds.css, because pasting a newer copy replaces it.
Scrollbars and form controls turn dark with the theme. Don't add a
color-scheme meta tag. A light dark meta lets the operating system darken a
light page.
Step 3: Add the appearance provider
The provider applies the theme and saves the user's choices, like dark mode.
Its script runs in <head>, so the page doesn't flash the wrong theme on load.
If you use Vite, follow Set up Vite for this step instead. Steps 1, 2 and 4 are the same on Vite.
Install the theme engine:
Copy the provider folder into components/nxds/components/appearance/provider/:
Set your app's starting theme. Copy the default appearance and change the fields you want:
brandColor sets the primary color. surfaceTone sets the gray used for
backgrounds. For every field, see Appearance.
Add the script and the provider to your root layout:
nx:typography-body-default gives page text the nxds font and size, which
follow the appearance settings.
Give the script and the provider the same storageKey. The provider saves the
user's choices under that key, and the script reads them on the next load.
To render the saved theme on the server, see Load the theme from a cookie.
Step 4: Add the cn helper
Copy packages/react/src/lib/utils.ts to components/nxds/lib/utils.ts.
cn merges class names and resolves conflicts between nx: classes. That's
what lets <Button className="nx:bg-error-background" /> replace the button's
own background.
Add your first component
Each component lists the components to install first, the packages to install, and the files to copy. For Button:
Install these components first: Spinner.
Paths are relative to packages/react/src/ in the repo. Copy each file to the
same path under components/nxds/, and the imports between files work without
edits. If a component has its own stylesheet, like Progress, it also lists the
@import lines to add to app/globals.css.
Render it:
To check dark mode, add a toggle that uses useNxdsAppearance:
resolvedMode is the mode on screen, so the toggle also works when mode is
'system'.
Check your setup
-
nxds.cssis imported from your CSS entry point - The provider wraps your app, and the script is in
<head> - Reloading in dark mode doesn't flash light first
- The mode you pick is still set after a full reload
If the button has no styles, nxds.css isn't imported (step 2). If it stays
light when you toggle, the provider isn't wrapping it (step 3).
If the page flashes on reload, check that the script and the provider use the
same storageKey. With different keys, they load different themes.
Set up Vite
On Vite, this section replaces step 3. Install @nxds/core, copy the
provider into src/components/nxds/components/appearance/provider/, and create
src/appearance-state.ts as in step 3.
Vite doesn't render on the server, so add the theme script to index.html at
build time:
Wrap your app in the provider, with the same storage key:
Don't apply the theme in a useEffect. Effects run after the first paint, so
the page would flash the default theme first.
(Optional) Load the theme from a cookie
On Next.js, read the user's saved theme from a cookie. The server then renders the right theme on the first request. Replace the layout from step 3 with this one:
If the cookie is missing or invalid, createNxdsAppearanceSnapshotFromCookie
falls back to defaultAppearance.
If you read the cookie in the root layout, Next.js renders every route on each request.
The provider saves the theme in two places:
| Prop | Saves to | Read by |
|---|---|---|
cookieWriteKey | A cookie | The server, on the next request |
storageKey | localStorage | The head script before first paint, then the provider |
When both are set, localStorage wins in the browser. To use only the cookie,
set storageKey={false} on both the script and the provider.
If your app uses a strict Content Security Policy, pass a nonce:
<NxdsAppearanceScript nonce={nonce} />.
Add nxds to an existing app
nxds CSS defines only nx: classes and --nx-* variables, so your app's
styles stay as they are. Follow steps 1 to 4 with these four changes:
-
Give nxds its own CSS entry and import it after your own stylesheet:
Your
globals.csskeeps its@import "tailwindcss". Two files make two Tailwind builds, one prefixed and one not. -
Keep your app's reset. nxds needs
box-sizing: border-box. Tailwind's Preflight, MUI'sCssBaselineand Bootstrap's Reboot all set it. If your app has no Tailwind, install it for the nxds entry.initthen adds Preflight toapp/nxds.css. If your app or library has its own reset, delete the Preflight lines, so your existing pages keep their look. -
Put element rules in a cascade layer, your library's and your own, if they aren't in one. nxds classes sit in Tailwind's
utilitieslayer. CSS outside any layer beats every layer, so a reset likebutton { border-radius: 0 }would overridenx:rounded-md. Name your library's layer first, at the top of your first stylesheet:Then load the library's CSS into that layer. Each Integrations page gives the line for its library.
If you use Vite, put the statement at the top of
<head>inindex.htmlinstead:The first statement on the page sets the order. On Vite, the nxds script's own statement comes before your CSS.
-
Add the provider and the script as in step 3. nxds owns light and dark mode, and writes the
.darkclass on<html>. Make your library follow nxds, as each Integrations page shows.
Three global effects remain:
| Source | What it adds |
|---|---|
.dark | color-scheme: dark, so your scrollbars and form controls turn dark in dark mode |
tw-animate-css | enter, exit, accordion-*, collapsible-* and caret-blink keyframes, as in any shadcn app |
| Tailwind's own output | spin, ping, pulse and bounce keyframes and --tw-* properties, as in any Tailwind 4 app |
Style an nxds component with nx: classes. An unprefixed Tailwind class
loses to the component's own nx: class, because both sit in the utilities
layer and nxds's stylesheet loads later.
Your content inside an nxds component inherits its text color, font and line height.
If your design system is a package, ship the nxds CSS entry from it and end
the entry with @source './';. Tailwind skips node_modules, so without that
line the package's classes are never generated.
nxds overlays use three layers:
| Variable | Default | Used by |
|---|---|---|
--nx-z-index-modal | 50 | Dialog, Sheet, Drawer |
--nx-z-index-popover | 70 | Popover, Select, menus, Tooltip |
--nx-z-index-toast | 100 | Toaster |
If your library's overlays sit higher, raise these on :root.
An open nxds dialog blocks pointer events outside itself. Render another library's popups inside it, with portals turned off. Each Integrations page gives its library's z-index values and portal prop.
Examples shows nxds added to a shadcn app, an MUI app and a company package.
(Optional) Add lint rules
The nxds ESLint rules keep copied components following nxds conventions:
nxdsComponentConfig turns on three rules for the files you list:
| Rule | Catches |
|---|---|
nx-class-conventions | Wrong prefix order, removed tokens, incomplete token names and raw palette colors |
no-render-prop-types | Props that inject what a component renders, instead of children |
no-multi-statement-jsx-handler | Long inline event handlers that should be named functions |
The rules need a TypeScript parser. create-next-app sets one up through
next/core-web-vitals. If your config has none, install typescript-eslint and
add languageOptions: { parser: tseslint.parser } to the entry.
Where the files are
Every file you copy comes from the nxds repo:
| File | Path in the repo | Path in your app |
|---|---|---|
| Token CSS | packages/tailwind/*.css | components/nxds/tailwind/ |
| Appearance provider | packages/react/src/components/appearance/provider/ | components/nxds/components/appearance/provider/ |
cn helper | packages/react/src/lib/utils.ts | components/nxds/lib/utils.ts |
| Components | packages/react/src/components/{name}/ | components/nxds/components/{name}/ |
Step 1: Install Tailwind 4
Tailwind 4 builds the nx: classes. These steps use a new Vite + Vue 3 app
from npm create vue@latest, with TypeScript.
Step 2: Add the token CSS
The token CSS defines the colors, spacing and type scale. Copy the seven files
from packages/tailwind/ in the
nxds repo into src/styles/nxds/.
Install the animation utilities that some components use to open and close:
Import Tailwind's Preflight, the token CSS and the animation utilities from your CSS entry point:
nxds ships no reset, so the page brings Preflight. nxds components need its
box-sizing: border-box.
Every Tailwind class in your app now takes the nx: prefix, and only nxds
tokens are available.
Step 3: Add the appearance plugin
The plugin applies the theme and saves the user's choices, like dark mode. A
script in index.html applies the saved theme before the first paint.
Install the theme engine and the Vue component dependencies:
Copy packages/vue/src/components/appearance/ into
src/components/appearance/.
Set your app's starting theme:
Add Tailwind, the @ import alias and the theme script to vite.config.ts:
Install the plugin with the same storage key:
Give the page the nxds background, text color and font in index.html:
Step 4: Add the cn helper
Copy packages/vue/src/lib/utils.ts to src/lib/utils.ts.
cn merges class names and resolves conflicts between nx: classes. That's
what lets <Button class="nx:bg-error-background" /> replace the button's own
background.
Add your first component
Each component lists the components to install first, the packages to install, and the files to copy. For Button:
Install these components first: ButtonGroup, Spinner.
Paths are relative to packages/vue/src/ in the repo. Keep the same folders
under src/, and the imports between files work without edits.
Render it:
To check dark mode, add a toggle that uses useNxdsAppearance:
Check your setup
-
nxds.cssis imported fromsrc/style.css -
main.tsinstallscreateNxdsAppearance - Reloading in dark mode doesn't flash light first
- The mode you pick is still set after a full reload
If the button has no styles, nxds.css isn't imported (step 2). If the page
flashes on reload, check that the script and the plugin use the same storage
key.
Where the files are
| File | Path in the repo |
|---|---|
| Token CSS | packages/tailwind/*.css |
| Appearance plugin | packages/vue/src/components/appearance/ |
cn helper | packages/vue/src/lib/utils.ts |
| Components | packages/vue/src/components/{name}/ |
Next steps
- Appearance: every theme field, and how to change it at runtime.
- Playground: try a theme on every component, then copy it into your app.
- Color: how the palette is built.
- Components: every component, with its source to copy.