next.jsmonoreposhadcnuiturbopack

Shared package in nextjs monorepo + shadcn/ui


Using Next.js and Shadcn/UI in a monorepo environment.

As per the documentation https://ui.shadcn.com/docs/monorepo, I added form component as

cd apps/web
pnpm dlx shadcn@canary add form

This added form component in

packages/ui/src/components/form.tsx

and installed zod inside packages/ui/package.json

{
  "name": "@workspace/ui",
  "version": "0.0.0",
  "type": "module",
  "private": true,
  "scripts": {
    "lint": "eslint . --max-warnings 0"
  },
  "dependencies": {
    "@hookform/resolvers": "^5.2.1",
    ...
    "sonner": "^2.0.6",
    "zod": "^3.24.2"
  },
  "devDependencies": {
    "@tailwindcss/postcss": "^4.0.8",
    "@turbo/gen": "^2.4.2",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "@workspace/eslint-config": "workspace:*",
    "@workspace/typescript-config": "workspace:*",
    "tailwindcss": "^4.0.8",
    "typescript": "^5.7.3"
  },
  "exports": {
    "./globals.css": "./src/styles/globals.css",
    "./postcss.config": "./postcss.config.mjs",
    "./lib/*": "./src/lib/*.ts",
    "./components/*": "./src/components/*.tsx",
    "./hooks/*": "./src/hooks/*.ts"
  }
}

The form component is usable inside apps/web/ as

import {
  Form,
  FormControl,
  FormField,
  FormItem,
} from "@workspace/ui/components/form";

But unable to import zod inside apps/web.

The same is the case with sonner, which I then installed inside apps/web for working. Can't the packages required for a specific component be shared in apps?

package.json inside apps/web is

{
  "name": "web",
  "version": "0.0.1",
  "type": "module",
  "private": true,
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "lint:fix": "next lint --fix",
    "typecheck": "tsc --noEmit"
  },
  "dependencies": {
    "@tanstack/react-table": "^8.21.3",
    "@workspace/ui": "workspace:*",
    "lucide-react": "^0.475.0",
    "next": "^15.2.3",
    "next-themes": "^0.4.4",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "sonner": "^2.0.6"
  },
  "devDependencies": {
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "@workspace/eslint-config": "workspace:^",
    "@workspace/typescript-config": "workspace:*",
    "typescript": "^5.7.3"
  }
}

sonner is reflected in this file as well because it was intentionally installed in apps/web.


Solution

  • In your root pnpm-workspace.yaml, make sure it includes all packages:

    packages:
      - "apps/*"
      - "packages/*"
    

    Then hoist shared deps to the root by adding them at the top-level package.json:

    {
      "dependencies": {
        "zod": "^3.24.2",
        "sonner": "^2.0.6"
      }
    }
    

    Then run:
    pnpm install