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.
1. Bootstrap the React UI Library
Permalink to “1. Bootstrap the React UI Library”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.
Add Nx Library
Permalink to “Add Nx Library”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 livestyles
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.
Update TypeScript Config Paths
Permalink to “Update TypeScript Config Paths”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';
2. Set Up Tailwind CSS
Permalink to “2. Set Up Tailwind CSS”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';
3. Create utils lib with cn
Permalink to “3. Create utils lib with cn” 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.
4. Initialize Shadcn UI
Permalink to “4. Initialize Shadcn UI”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.
5. Add Components
Permalink to “5. Add Components”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.
Conclusion
Permalink to “Conclusion”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.