How to Set Up Shared Shadcn UI Components in an Nx Monorepo


Monorepos help teams scale applications efficiently, and Shadcn UI brings flexible, customizable UI components. In this article, I’ll show you how to set up shared Shadcn UI components inside an Nx monorepo so you can reuse them across multiple apps.
Nx is a powerful open-source build system that provides tools and techniques for enhancing developer performance, optimizing CI performance, and maintaining code quality.
ShadCn is a collection of re-useable components that you can copy and paste. It provides beautifully designed components built with Radix UI and tailwindCSS. These components are both accessible and customizable, allowing you developers to easily integrate with projects. It’s open-source and free to use.
Prerequisite:
Before we begin, ensure you have the following installed:
Node.js and npm (Node Package Manager)
Next.js 13.4 or later
Nx
npm install --global nx@latest
Project setup
Let’s set up our monorepo and install the necessary dependencies.
- Create a new monorepo by running the following command:
npx create-nx-workspace@latest <project-name> packageManager=npm
Note: Create an empty monorepo that we will add other dependencies to.
Add nextJs to the repo
nx add @nx/next
Create a nextjs application
nx g @nx/next:app apps/app-name
Choose tailwind for the style. and add `app routing` with the `src` folder (recommended)
Test the code
export default function Index() { return <h2 className="text-red-500 text-3xl text-center mt-10">Hello</h2>; }
run the application using this command:
npx nx run @my-org/web:dev
If you see this
Then Tailwind is fully setup and we are ready to continue.
- Create a shared package for the shared UI components by using the following command:
nx g @nx/next:lib shared
Install the following shadCn dependencies
npm install tailwindcss-animate class-variance-authority clsx tailwind-merge
a. Configure path aliases by creating
tsconfig.json
a file to the root of the project://tsconfig.json { "compilerOptions": { "baseUrl": ".", "paths": { "@my-org/shared": ["shared/src/index.ts"], "@my-org/shared/*": ["shared/src/*"] } } }
b. Create and Configure
components.json
to the root of the project//components.josn { "$schema": "https://ui.shadcn.com/schema.json", "style": "new-york", "rsc": false, "tailwind": { "config": "apps/**/tailwind.config.js", "css": "apps/**/app/global.css", "baseColor": "stone", "cssVariables": true }, "aliases": { "components": "@my-org/shared/components", "utils": "@my-org/shared/shared/utils" } }
c. inside your nextjs app folder, update the tailwind config file
const { join } = require('path'); const { createGlobPatternsForDependencies } = require('@nx/react/tailwind'); const { fontFamily } = require('tailwindcss/defaultTheme'); module.exports = { darkMode: ['class'], content: [ join( __dirname, '{src,pages,components,app}/**/*!(*.stories|*.spec).{ts,tsx,html}' ), // Explicitly include your shared library components join(__dirname, '../../shared/**/*.{js,ts,jsx,tsx}'), // Keep this for other dependencies ...createGlobPatternsForDependencies(__dirname), ], theme: { extend: { colors: { border: 'hsl(var(--border))', input: 'hsl(var(--input))', ring: 'hsl(var(--ring))', background: 'hsl(var(--background))', foreground: 'hsl(var(--foreground))', primary: { DEFAULT: 'hsl(var(--primary))', foreground: 'hsl(var(--primary-foreground))', }, secondary: { DEFAULT: 'hsl(var(--secondary))', foreground: 'hsl(var(--secondary-foreground))', }, destructive: { DEFAULT: 'hsl(var(--destructive))', foreground: 'hsl(var(--destructive-foreground))', }, muted: { DEFAULT: 'hsl(var(--muted))', foreground: 'hsl(var(--muted-foreground))', }, accent: { DEFAULT: 'hsl(var(--accent))', foreground: 'hsl(var(--accent-foreground))', }, popover: { DEFAULT: 'hsl(var(--popover))', foreground: 'hsl(var(--popover-foreground))', }, card: { DEFAULT: 'hsl(var(--card))', foreground: 'hsl(var(--card-foreground))', }, }, borderRadius: { lg: `var(--radius)`, md: `calc(var(--radius) - 2px)`, sm: 'calc(var(--radius) - 4px)', }, fontFamily: { sans: ['var(--font-sans)', ...fontFamily.sans], }, keyframes: { 'accordion-down': { from: { height: '0' }, to: { height: 'var(--radix-accordion-content-height)' }, }, 'accordion-up': { from: { height: 'var(--radix-accordion-content-height)' }, to: { height: '0' }, }, }, animation: { 'accordion-down': 'accordion-down 0.2s ease-out', 'accordion-up': 'accordion-up 0.2s ease-out', }, }, }, plugins: [require('tailwindcss-animate')], };
d. Add the following CSS to each project
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 221.2 83.2% 53.3%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 221.2 83.2% 53.3%;
--radius: 0.5rem;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 217.2 91.2% 59.8%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 224.3 76.3% 48%;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
font-feature-settings: "rlig" 1, "calt" 1;
}
}
e. Add a cn helper to the
shared/src/lib/utils.ts
import { clsx, type ClassValue } from 'clsx'; import { twMerge } from 'tailwind-merge'; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); }
Create components using the shadCn command
npx shadcn@latest add button
A components folder will be generated inside our shared folder
Finally, let’s export our components from the shared package
//shared/src/index.ts
export * from './components/ui/button';
And Finally let’s use it
import { Button } from '@my-org/shared';
export default function Index() {
return (
<div className="flex justify-center flex-col w-[50%] m-20">
<Button variant={"destructive"}>Hello</Button>
</div>
);
}
And that’s it, Now we can create any shared UI components and use them as needed.
Subscribe to my newsletter
Read articles from Oussama Chahidi directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Oussama Chahidi
Oussama Chahidi
Hi, my name is oussama and i am a self-taught full stack javascript developer with interests in computers. I like the expend my knowledge and learn new things each day cause i always see the beauty in mystery.