Wellocs Monorepo Setup: Turborepo, Pnpm, And Shadcn/ui

by Alex Johnson 55 views

As developers, setting up a new project with a robust and scalable architecture is crucial. This article guides you through initializing a Wellocs monorepo using Turborepo, pnpm, and shadcn/ui. This setup provides a clean, consistent foundation for building multiple applications like Admin, Agency, and Client apps while sharing UI components efficiently.

Why Monorepo with Turborepo, pnpm, and shadcn/ui?

A monorepo is a single repository containing multiple projects, which can be highly beneficial for organizations managing several related applications or libraries. Using Turborepo as a build tool, pnpm for package management, and shadcn/ui for UI components offers a powerful combination for modern web development. Let's delve deeper into why these technologies are advantageous.

Monorepo Benefits

Adopting a monorepo strategy can significantly streamline development workflows. Code sharing is simplified as components and utilities can be easily shared across different projects within the repository. Dependency management becomes more manageable, reducing the risk of version conflicts. Atomic changes across multiple projects are possible, ensuring that features and bug fixes are implemented consistently. This approach fosters a more collaborative environment, enhances code reuse, and simplifies the overall maintenance process.

Turborepo Advantages

Turborepo is a high-performance build system for JavaScript and TypeScript monorepos. It employs incremental builds, meaning it only rebuilds what has changed, saving considerable time during development. Parallel execution maximizes the utilization of available CPU cores, further speeding up the build process. Caching is a first-class citizen in Turborepo, ensuring that builds are fast and efficient. This powerful combination of features makes Turborepo an ideal choice for managing large monorepos and complex build pipelines.

pnpm Perks

pnpm (performant npm) is a package manager that addresses the shortcomings of npm and yarn. It uses a content-addressable file system to save disk space and boost installation speed. Symlinked dependencies create a non-flat node_modules structure, preventing common issues related to dependency hoisting. Speed and efficiency are paramount in pnpm, making it an excellent tool for managing dependencies in monorepo environments. Its unique approach to package management ensures consistency and reduces the risk of unexpected behavior.

shadcn/ui Advantages

shadcn/ui is a collection of accessible and reusable UI components built with Radix UI and Tailwind CSS. These components are designed to be unstyled, allowing developers to tailor them to their specific needs. The copy-and-paste approach enables developers to easily integrate components into their projects and customize them extensively. Accessibility is a core principle of shadcn/ui, ensuring that applications are usable by everyone. By using shadcn/ui, developers can accelerate UI development while maintaining flexibility and accessibility.

Setting Up the Wellocs Monorepo

To initialize the Wellocs monorepo, we will follow a structured approach, starting with setting up the basic monorepo structure, adding the necessary applications and packages, and configuring shadcn/ui. This step-by-step guide will help you create a solid foundation for your project.

1. Initializing the Monorepo

First, create the monorepo using Turborepo and pnpm. This involves setting up the basic directory structure and initializing the necessary configuration files. Run the following commands in your terminal:

mkdir wellocs
cd wellocs
pnpm init -y
pnpm install -g turbo
turbo init

This creates a new directory named wellocs, initializes a pnpm project, installs Turborepo globally, and sets up the basic Turborepo configuration. This initial setup lays the groundwork for the rest of the monorepo structure.

2. Defining Workspaces

Next, define the workspaces in the pnpm-workspace.yaml file. This file tells pnpm which directories are part of the monorepo. Create a pnpm-workspace.yaml file in the root directory with the following content:

packages:
  - 'apps/*'
  - 'packages/*'

This configuration specifies that directories under apps/ and packages/ are considered workspaces. This allows pnpm to manage dependencies across the monorepo efficiently.

3. Creating Apps and Packages

Now, create the base applications and packages. For this example, we will create two Next.js applications (apps/web and apps/agency) and a shared UI package (packages/ui). Use the following commands:

mkdir apps
mkdir apps/web
mkdir apps/agency
mkdir packages
mkdir packages/ui

cd apps/web
yarn create next-app -y --typescript
cd ../agency
yarn create next-app -y --typescript
cd ../../packages/ui
pnpm init -y

These commands create the necessary directories and initialize Next.js applications in apps/web and apps/agency. A basic package is also initialized in packages/ui, which will house the shared UI components. This structure ensures a clear separation of concerns within the monorepo.

4. Configuring Turborepo

Configure Turborepo by modifying the turbo.json file in the root directory. This file defines the build and development pipelines for the monorepo. Here’s a basic configuration:

{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "dist/**"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "lint": {},
    "test": {}
  }
}

This configuration defines the build, dev, lint, and test tasks. The build task depends on the build task of its dependencies (^build) and specifies the output directories. The dev task disables caching and persists the process. This setup ensures that Turborepo can efficiently manage the build process across the monorepo.

5. Setting Up shadcn/ui

To set up shadcn/ui, we'll first add Tailwind CSS to the packages/ui package and then install the necessary shadcn/ui components. First, install Tailwind CSS and its peer dependencies in the packages/ui directory:

cd packages/ui
pnpm install -D tailwindcss postcss autoprefixer
pnpm install -D tailwindcss-animate
tailwindcss init -p

Next, configure Tailwind CSS by modifying the tailwind.config.js file in packages/ui:

/** @type {import('tailwindcss').Config} */
module.exports = {
  darkMode: ["class"],
  content: ["./components/**/*.{ts,tsx}", "./app/**/*.{ts,tsx}","./src/**/*.{ts,tsx}"],
  prefix: "",
  theme: {
    container: {
      center: true,
      padding: "2rem",
      screens: {
        "2xl": "1400px",
      },
    },
    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)",
      },
      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")],
}

This configuration sets up the basic Tailwind CSS settings, including dark mode, content sources, theme extensions, and plugins. The tailwindcss-animate plugin adds pre-built animations for common UI elements. Next, install the required dependencies for shadcn/ui in packages/ui:

pnpm install -D @radix-ui/react-slot class-variance-authority clsx tailwind-merge

Finally, install the necessary shadcn/ui components. For example, to add a button component, run:

pnpm dlx shadcn-ui@latest add button

This command installs the button component and its dependencies, making it available for use in your shared UI package. Repeat this process for other components as needed. This streamlined process ensures that the UI components are consistently styled and easily customizable.

6. Configuring Tailwind CSS in Apps

To use Tailwind CSS and shadcn/ui components in the apps/web and apps/agency applications, you need to configure Tailwind CSS in each app. Install Tailwind CSS and its peer dependencies in each app directory:

cd ../../apps/web
pnpm install -D tailwindcss postcss autoprefixer
tailwindcss init -p
cd ../agency
pnpm install -D tailwindcss postcss autoprefixer
tailwindcss init -p

Copy the tailwind.config.js file from packages/ui to each app directory. This ensures that all applications use the same Tailwind CSS configuration, maintaining visual consistency across the monorepo. Next, update the postcss.config.js files in each app directory to include Tailwind CSS:

module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};

This configuration ensures that PostCSS processes Tailwind CSS directives correctly. Import the global styles from packages/ui in your application's entry point (e.g., _app.tsx in Next.js):

import "@/packages/ui/src/index.css";

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />;
}

export default MyApp;

This step ensures that the Tailwind CSS styles are applied globally in your application. By configuring Tailwind CSS in both the shared UI package and the individual applications, you maintain a consistent design system throughout the monorepo.

7. Using Shared UI Components

Now that shadcn/ui and Tailwind CSS are configured, you can start using the shared UI components in your applications. Import the components from the packages/ui package:

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

function HomePage() {
  return (
    <div>
      <h1>Welcome to Wellocs!</h1>
      <Button>Click me</Button>
    </div>
  );
}

export default HomePage;

This example demonstrates how to import and use the Button component from the shared UI package. By using shared UI components, you can ensure consistency across your applications and reduce code duplication. This approach streamlines development and maintenance, making it easier to manage the monorepo over time.

Conclusion

Initializing a Wellocs monorepo with Turborepo, pnpm, and shadcn/ui provides a solid foundation for building scalable and maintainable applications. By leveraging the strengths of these technologies, developers can create a consistent and efficient development environment. This setup not only streamlines the development process but also promotes code reuse and collaboration across teams. Embracing this modern approach to web development can significantly enhance the quality and efficiency of your projects.

For further exploration and advanced configurations, you might find the official Turborepo documentation helpful. Check it out here: Turborepo Documentation.