Skip to content
Esc
navigateopen⌘Jpreview
On this page

Installation

Set up the StyleX build and install your first components — Next.js and Vite.

Components are copied into your project as source, so your app needs to be able to compile StyleX — that’s the only setup beyond a normal React app. The madeui init command does it for you; the manual steps are below it for reference.

npx @madeui/cli init
npx @madeui/cli add button dialog select

init detects your framework (Next.js or Vite), wires the StyleX build, installs @stylexjs/stylex + @base-ui/react, copies the design tokens to lib/, and appends a component-conventions section to AGENTS.md so coding agents follow the house rules. add copies components (and anything they depend on) into components/ui/.

Components are also installable with the shadcn CLI — the registry is shadcn-compatible:

npx shadcn@latest add @madeui/button

Next.js (manual)

StyleX compiles through Babel; on Next.js it runs inside PostCSS via the official @stylexjs/postcss-plugin.

npm install @stylexjs/stylex @base-ui/react
npm install -D @stylexjs/babel-plugin @stylexjs/postcss-plugin

babel.config.js:

const path = require('path');

module.exports = {
  presets: ['next/babel'],
  plugins: [
    [
      '@stylexjs/babel-plugin',
      {
        dev: process.env.NODE_ENV !== 'production',
        runtimeInjection: false,
        treeshakeCompensation: true,
        aliases: { '@/*': [path.join(__dirname, '*')] },
        unstable_moduleResolution: { type: 'commonJS' },
      },
    ],
  ],
};

postcss.config.js:

const babelConfig = require('./babel.config');

module.exports = {
  plugins: {
    '@stylexjs/postcss-plugin': {
      include: [
        'app/**/*.{js,jsx,ts,tsx}',
        'components/**/*.{js,jsx,ts,tsx}',
        'lib/**/*.{js,jsx,ts,tsx}',
      ],
      babelConfig: {
        babelrc: false,
        parserOpts: { plugins: ['typescript', 'jsx'] },
        plugins: babelConfig.plugins,
      },
      useCSSLayers: true,
    },
  },
};

app/globals.css — the @stylex marker is where the generated CSS lands. Declare the base layer before it: with useCSSLayers, any unlayered global CSS would outrank every StyleX rule and silently zero component paddings:

@layer base;

@stylex;

@layer base {
  * {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
  }
}

Vite (manual)

Vite uses the official @stylexjs/unplugin — it runs its own Babel pass (works with @vitejs/plugin-react v6, which dropped its Babel pipeline) and injects the extracted CSS into the app’s CSS asset. No PostCSS config or @stylex marker needed.

npm install @stylexjs/stylex @base-ui/react
npm install -D @stylexjs/unplugin

vite.config.ts — the StyleX plugin must come before the React plugin to preserve Fast Refresh:

import { fileURLToPath } from 'node:url';

import { unplugin as stylexPlugin } from '@stylexjs/unplugin';
import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    stylexPlugin.vite({
      aliases: {
        '@/*': [fileURLToPath(new URL('src/*', import.meta.url))],
      },
      unstable_moduleResolution: { type: 'commonJS' },
    }),
    react(),
  ],
  resolve: {
    alias: { '@': fileURLToPath(new URL('src', import.meta.url)) },
  },
});

Make sure the app imports at least one CSS file from its root (e.g. import './index.css' in main.tsx) — the plugin appends the generated CSS to the app’s CSS bundle.

Path aliases

Components import tokens and utilities via @/lib/... and each other via @/components/ui/.... Add the alias to tsconfig.json if you don’t have it:

{
  "compilerOptions": {
    "paths": { "@/*": ["./*"] }
  }
}

(For Vite projects the alias usually points at ./src/*.)

Next steps

Was this page helpful?