> For AI agents: the complete documentation index is available at https://mfeflow.github.io/mfeflow/llms.txt, the full documentation bundle is available at https://mfeflow.github.io/mfeflow/llms-full.txt.

# ui-shadcn

shadcn/ui gives you components as source files rather than a package. This feature sets that up in each remote on top of the Tailwind prefix, so the components carry the right class names and read the same theme as the rest of the page.

## What you get

Every remote gets its own copy of a `Button`, a class helper called `cn` and a shadcn config. Nothing is shared between remotes. A button in one remote and a button in another are two files that look the same because they read the same theme tokens.

## Add it

`ui-shadcn` needs [tailwind](https://mfeflow.github.io/mfeflow/features/tailwind.md). Add both in one command, or add `tailwind` first. Without it, `add` stops with [E206](https://mfeflow.github.io/mfeflow/errors/E206.md).

```sh
pnpm mfeflow add tailwind ui-shadcn
```

What changed, in every remote:

```text
components.json          # shadcn's config, with the remote's css path and prefix
src/
├── lib/
│   └── utils.ts         # the cn helper
└── components/ui/
    └── button.tsx       # the Button
```

Each of the last two files also gets a test if you use [testing](https://mfeflow.github.io/mfeflow/features/testing.md). Three packages are added as dependencies:

```text
class-variance-authority
clsx
tailwind-merge
```

## Use it

Import the button from the remote's own folder and use it like any component.

```tsx
import { Button } from './components/ui/button';

<Button variant="outline" size="sm" onClick={save}>
  Save
</Button>;
```

The files are yours to change or delete. This is the start of the generated button, where every class carries the remote's prefix (`cart` here).

```tsx title="apps/cart/src/components/ui/button.tsx"
variant: {
  default: 'cart:bg-primary cart:text-primary-foreground cart:hover:opacity-90',
  secondary: 'cart:bg-secondary cart:text-secondary-foreground cart:hover:opacity-80',
  outline: 'cart:border cart:border-border cart:bg-background cart:hover:bg-accent cart:hover:text-accent-foreground',
  destructive: 'cart:bg-destructive cart:text-white cart:hover:opacity-90',
  ghost: 'cart:hover:bg-accent cart:hover:text-accent-foreground',
},
```

## Configure it

There are no install options. These are the knobs you will actually reach for.

| Setting       | Where             | Values                                                                    |
| ------------- | ----------------- | ------------------------------------------------------------------------- |
| `variant`     | `Button` prop     | `default`, `secondary`, `outline`, `destructive`, `ghost`                 |
| `size`        | `Button` prop     | `default`, `sm`, `lg`                                                     |
| `type`        | `Button` prop     | `button` unless you pass `submit`, so it never submits a form by accident |
| `style`       | `components.json` | `new-york`                                                                |
| `baseColor`   | `components.json` | `neutral`                                                                 |
| `prefix`      | `components.json` | this remote's Tailwind prefix                                             |
| `iconLibrary` | `components.json` | `lucide`                                                                  |

## How it works

`cn` joins class names with `clsx`, then merges them with `tailwind-merge`, which is told about the remote's prefix. That is why this keeps only the last padding class:

```ts
cn('cart:p-2', 'cart:p-4'); // 'cart:p-4'
```

The colours come from the tokens the [tailwind](https://mfeflow.github.io/mfeflow/features/tailwind.md) feature defines. Those read the `--mf-*` variables, so with [theming](https://mfeflow.github.io/mfeflow/features/theming.md) a theme switch recolours every remote's buttons together.

The three helper libraries only reach a remote's bundle when something imports them. Nothing uses the `Button` yet, so a remote that does not use it pays nothing. Watch the default 10 KB remote budget when you start using it.

## Gotchas

- To add more components with the shadcn CLI, first give the remote the `@/` alias it expects. Add this to the remote's `tsconfig.json` under `compilerOptions`, and Rsbuild reads it from there.

  ```json
  "baseUrl": ".",
  "paths": { "@/*": ["./src/*"] }
  ```

- Then run the CLI from the remote's folder. It adds the prefix to the classes it writes, because of `"prefix"` in `components.json`.

  ```sh
  cd apps/cart
  pnpm dlx shadcn@latest add dialog
  ```

- Check one new component after adding it. This `Button` leaves out shadcn's `asChild` and Radix `Slot`, so the remote has no Radix dependency until you add a component that needs one.

## Reference

- [tailwind](https://mfeflow.github.io/mfeflow/features/tailwind.md), which defines the prefix and tokens
- [theming](https://mfeflow.github.io/mfeflow/features/theming.md)
- [Cost model](https://mfeflow.github.io/mfeflow/concepts/cost-model.md), where the remote budget comes from
- [feature.json reference](https://mfeflow.github.io/mfeflow/reference/feature-json.md)
