What is Turborepo ?

Turborepo, developed by Vercel, is a powerful build system tool for JavaScript and TypeScript codebases that makes managing monorepos easier by optimizing workflows, speeding up build times, and ensuring consistency across projects.

Key features of Turborepo

  • Incremental Caching: Avoids rebuilding unchanged code, significantly improving build times.
  • Parallel Execution: Runs tasks across multiple projects simultaneously.
  • Remote Caching: Speeds up CI/CD pipelines by reusing cached results across different environments
  • Efficient Dependency Management: Uses a single package.json or workspace approach to manage dependencies.

Setting Up a Turborepo Project

Step 1. Initialize a New Monorepo

npx create-turbo@latest my-turborepo
cd my-turborepo

This starter repository includes

  • Two deployable NextJS applications in apps folder
  • Three packages libraries for use in the rest of the monorepo
my-turborepo/
├── apps/
│   ├── web/                # First Next.js application
│   │   ├── app/            # Core directory for App Router
│   │   │   ├── globals.css # Global styles
│   │   │   ├── layout.tsx  # Root layout component
│   │   │   └── page.tsx    # Home page component ("/")
│   │   ├── public/         # Static assets (images, fonts, etc.)
│   │   ├── next.config.js  # Next.js configuration file
│   │   ├── tsconfig.json   # TypeScript configuration
│   │   └── package.json    # Project dependencies and scripts
│   └── docs/               # Second Next.js application
│       ├── app/            # Core directory for App Router
│       │   ├── globals.css # Global styles
│       │   ├── layout.tsx  # Root layout component
│       │   └── page.tsx    # Home page component ("/")
│       ├── public/         # Static assets (images, fonts, etc.)
│       ├── next.config.js  # Next.js configuration file
│       ├── tsconfig.json   # TypeScript configuration
│       └── package.json    # Project dependencies and scripts
├── packages/               # Shared packages
│   ├── ui/                 # Shared UI components
│   │   ├── button.tsx      # Example shared button component
│   │   ├── input.tsx       # Example shared input component
│   │   └── package.json    # Package dependencies and scripts
│   └── eslint-config-custom/ # Shared ESLint configuration
│       ├── index.js        # ESLint configuration
│       └── package.json    # Package dependencies and scripts
├── turbo.json              # Turborepo configuration
├── package.json            # Root package dependencies and scripts
└── pnpm-workspace.yaml     # pnpm workspaces configuration

Step 2. Installing turbo globally

pnpm install turbo --global

Step 3. Installing and setting up Tailwindcss V4 in UI package

cd packages/ui
pnpm install tailwindcss @tailwindcss/postcss postcss

Once Tailwindcss and postcss plugin is installed, create a postcss.config.mjs file and add the following

const config = {
  plugins: ["@tailwindcss/postcss"],
};

export default config;

Create default.css inside src/styles folder of ui package and add the following

@import "tailwindcss";

Create an export alias for default.css and postcss.config.mjs in packages.json

{
  "name": "@repo/ui",
  "version": "0.0.0",
  "private": true,
  "exports": {
    "./postcss.config": "./postcss.config.mjs",
    "./styles/*": "./src/styles/*"
  },
  "scripts": {
    "lint": "eslint . --max-warnings 0",
    "generate:component": "turbo gen react-component",
    "check-types": "tsc --noEmit"
  },
  "devDependencies": {
    "@repo/eslint-config": "workspace:*",
    "@repo/typescript-config": "workspace:*",
    "@tailwindcss/postcss": "^4.0.15",
    "@turbo/gen": "^2.4.4",
    "@types/node": "^22.13.10",
    "@types/react": "19.0.10",
    "@types/react-dom": "19.0.4",
    "eslint": "^9.22.0",
    "tailwindcss": "^4.0.15",
    "typescript": "5.8.2"
  },
  "dependencies": {
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
  }
}

Step 4. Using Tailwindcss V4 in Nextjs apps
Install Tailwindcss v4 and postcss in your NextJs apps

pnpm install tailwindcss @tailwindcss/postcss postcss

then create a postcss.config.mjs file

//using the config exported from packages/ui
export { default } from "@repo/ui/postcss.config";

Find globals.css in your Nextjs app and add the following

@import "tailwindcss";
@import "@repo/ui/styles/default.css";

@source '../../../packages/ui';

Don’t forget to add @repo/ui as dependency in package.json

"dependencies": {
    "@repo/ui": "workspace:*",
  },

Note: Since i am using pnpm package manage i had to add "@repo/ui": "workspace:*" , if you are using yarn or npm add the package dependency "@repo/ui": "*"

Verify if its working
Image description

Step 5. Adding Shadcn ui

cd packages/ui
pnpm add class-variance-authority clsx tailwind-merge lucide-react tw-animate-css

Create components.json file in packages/ui add the following

{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "new-york",
  "rsc": true,
  "tsx": true,
  "tailwind": {
    "config": "",
    "css": "src/styles/default.css",
    "baseColor": "zinc",
    "cssVariables": true
  },
  "iconLibrary": "lucide",
  "aliases": {
    "components": "@repo/ui/components",
    "ui": "@repo/ui/components/shadcn"
    "utils": "@repo/ui/lib/utils",
    "lib": "@repo/ui/lib",
    "hooks": "@repo/ui/hooks"
  }
}

Configure the path aliases in your packages/ui/tsconfig.json file.

{
  "extends": "@repo/typescript-config/react-library.json",
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@repo/ui/*": ["./src/*"],
    }
  },
  "include": ["src"],
  "exclude": ["node_modules"]
}

Create lib/utils.ts file in packages/ui/src and add the following

import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

Now you can install Shadcn components using shadcn CLI, let try it out

npx shadcn@latest add button

This will install the button component in packages/ui/src/components/shadcn
Image description

To use the components you need add a path alias in exportsin package.json

{
  "name": "@repo/ui",
  "version": "0.0.0",
  "private": true,
  "exports": {
    "./*": "./src/*.tsx",
    "./lib/utils": "./src/lib/utils.ts",
    "./postcss.config": "./postcss.config.mjs",
    "./styles/*": "./src/styles/*",
  },
  "peerDependencies": {
    "react": "^19.0.0",
    "react-dom": "^19.0.0"
  },
  "scripts": {
    "lint": "eslint . --max-warnings 0",
    "generate:component": "turbo gen react-component",
    "check-types": "tsc --noEmit"
  },
  "devDependencies": {
    "@repo/eslint-config": "workspace:*",
    "@repo/typescript-config": "workspace:*",
    "@tailwindcss/postcss": "^4.0.15",
    "@turbo/gen": "^2.4.4",
    "@types/node": "^22.13.10",
    "@types/react": "19.0.10",
    "@types/react-dom": "19.0.4",
    "eslint": "^9.22.0",
    "tailwindcss": "^4.0.15",
    "typescript": "5.8.2"
  },
  "dependencies": {
    "@radix-ui/react-slot": "^1.1.2",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "lucide-react": "^0.483.0",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "tailwind-merge": "^3.0.2",
    "tw-animate-css": "^1.2.4"
  }
}

Now you can use the shadcn components in your nextjs applications

//page.tsx

import { Button } from "@repo/ui/components/shadcn/button";

const Home = () => {
  return (
    
      Hello world
    
  );
};

export default Home;

Conclusion

Monorepos offer a powerful way to manage multiple projects under a single repository, but they can introduce challenges in scalability and performance. Turborepo solves these issues by providing a high-performance build system that optimizes dependency management and execution.

By following best practices and leveraging Turborepo’s caching and parallel execution, you can efficiently manage your monorepo projects.