Skip to main content

How to add Shadcn UI into an existing React app in Nx Monorepo

When integrating Shadcn UI into an Nx monorepo you can stumble upon few issues.

At least I did when I decided to add Shadcn UI into Clipio. Due to Nx structure, running shadcn init will not work. So I have decided to write this article to help others have a smooth setup journey for Shadcn UI and Nx monorepo using pure React.

Let’s get into it.

Most of the time, you can follow the steps in the Shadcn UI documentation for manual installation. I will briefly describe the steps below for easy follow-up and highlight extra steps relevant for Nx monorepo.

In my setup, I want to have Nx library which will contain Shadcn UI components, I will call it ui. Later I can easily import it to other libs or apps in my monorepo.

You can easily add your new library for Shadcn UI using the Nx generator. It will do most of the heavy lifting for you; just double-check the correct imports after the process is complete.

Run this command in your terminal:

pnpm nx g @nx/react:library ui --directory libs/ui --compiler swc --bundler vite

Next, delete the content of the src folder in the UI lib we just generated, and create two new folders:

  • ui where the Shadcn UI components will live
  • styles where we will put the global styles for the UI lib

Add a global.css file to the styles folder. We will update it later.

Now, go to the tsconfig.base.json file in the root of your monorepo. You should see the new path for the UI lib:

"paths": {
"@shadcn-in-nx/ui": ["libs/ui/src/index.ts"],
"...": "other paths"
}

Change it to this format:

"paths": {
"@shadcn-in-nx/ui": [ "libs/ui/src/ui" ],
"...": "other paths"
}

Using an import path in this format will allow you to import individual components into your codebase like this:

import { Button } from '@shadcn-in-nx/ui/button';

Install the additional dependencies as described in the Shadcn UI documentation:

pnpm add tailwindcss-animate class-variance-authority clsx tailwind-merge

Add icons to your project based on your preferred style of Shadcn UI components. I chose the New York style, so I’m using Radix UI icons:

pnpm add @radix-ui/react-icons

Now, set up Tailwind CSS in your monorepo. Nx provides a handy generator for this task. Run this command for both the UI lib where Shadcn UI will live and the main app:

pnpm nx g @nx/react:setup-tailwind

When initializing Tailwind in the UI lib, the Nx generator might complain it can’t find a stylesheet to update. Don’t worry about it. Copy the Tailwind styles from Step 6 in the Shadcn UI documentation into the global.css file you created earlier.

Also, update the Tailwind config in your UI lib based on instructions from Step 5 in the docs.

Next, update the Tailwind config in your app. Import the Tailwind config from the UI lib and merge it with the app’s config. Here’s how you can do it:

const { createGlobPatternsForDependencies } = require('@nx/react/tailwind');
+ const TailwindConfig = require('../../libs/ui/tailwind.config');
const { join } = require('path');

/** @type {import('tailwindcss').Config} */
module.exports = {
+ ...TailwindConfig,
content: [
+ ...TailwindConfig.content,
join(__dirname, '{src,pages,components,app}/**/*!(*.stories|*.spec).{ts,tsx,html}'),
...createGlobPatternsForDependencies(__dirname),
],
- theme: {
- extend: {},
- },
- plugins: [],
};

You have to inject styles from global.css into your app, otherwise Shadcn UI components will have broken styles. I have decided to use path alias for this as well. I inserted this line into paths property in tsconfig.base.json:

"@shadcn-test/styles/*": [ "libs/ui/src/styles/*" ],

Then in your app you will inject it like this:

import '@shadcn-test/styles/global.css';

Next step is to create a cn utility to merge styles in components. For that I have created a new lib in my monorepo called utils and placed the function here (I have other general purpose utility functions here, so this makes sense for me). In this case I have left the TS path in the tsconfig.base.json as is: "@shadcn-in-nx/utils": ["libs/utils/src/index.ts"].

If you want to generate new lib, run this Nx command: pnpm nx g @nx/js:lib utils and follow setup instructions. Once generated copy cn from Shadcn UI docs inside.

Last step before we can add components is to create components.json file into the root of monorepo. Below is example components.json file for a reference:

{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "libs/ui/tailwind.config.js",
"css": "libs/ui/src/styles/global.css",
"baseColor": "slate",
"cssVariables": true
},
"aliases": {
"components": "@shadcn-in-nx/ui",
"ui": "@shadcn-in-nx/ui",
"utils": "@shadcn-in-nx/utils"
}
}

Notice the ui alias. This one is important to ensure generated components are placed in correct folder.

Now you’re all set to generate Shadcn UI components from the command line. Use this command:

TS_NODE_PROJECT=tsconfig.base.json pnpx shadcn@latest add <"optional component name">

Your components will be generated in the UI lib and you can easily import them to the main app as well. Import across the components will use path alias as well, which is not optimal in Nx, but I haven’t found any way how to change this. Nevertheless, even if Nx eslint might complain (if you install it) everything will work.

I hope this guide helps you smoothly integrate Shadcn UI into your Nx monorepo. While adding it isn’t overly difficult, there are some Nx-specific steps that can trip you up. By following these instructions, you should be able to get everything working as expected in no time.

Tomas Pustelnik

Front-end developer with focus on semantic HTML, CSS, performance and accessibility. Fan of great and clever design, tooling addict and neverending learner. Building Clipio in my free time and writing on this blog.