Monorepos
Purpose and Scope
Monorepo support is for teams that want shadcn/ui components to live in a shared workspace while one or more applications consume those components through workspace imports. The official guide frames this as a fix for an older workflow where the CLI could add components, but developers still had to decide where generated files belonged and then repair imports by hand. The current workflow asks the CLI to understand the repository shape, place registry files in the correct workspace, install dependencies where they belong, and rewrite imports so applications can consume a shared UI package without duplicating component code.
Sources: apps/v4/content/docs/(root)/monorepo.mdx
The default mental model is a two-workspace project. The app workspace is where the framework application runs, and the UI workspace is where shared shadcn/ui components, hooks, utilities, and styles are installed. In the documented generated layout, the application is under an apps web workspace and the shared package is under a packages ui workspace. The app can still own app-specific composition, such as a login form, while primitive UI pieces such as a button are installed into the shared package. This split keeps reusable pieces centralized and leaves route-level or feature-level components near the application that owns them.
Sources: apps/v4/content/docs/(root)/monorepo.mdx
Relevant Source Files
- apps/v4/content/docs/(root)/monorepo.mdx — Main user-facing monorepo guide, including the init flow, add flow, import examples, generated file structure, and components.json requirements.
- templates/next-monorepo/README.md — Next.js monorepo template README showing the root command with a workspace config path and the shared UI import convention.
- templates/astro-monorepo/README.md — Astro monorepo template README showing the Astro app and shared UI package structure, add command, and Astro import example.
- packages/shadcn/src/utils/get-monorepo-info.ts — CLI utility code that detects monorepo roots, discovers candidate workspaces, and formats guidance when a command is run from the wrong level.
Core Primitives
There are three primitives to understand before adding components. First, every workspace that participates in shadcn/ui generation needs its own components.json file. The guide says package metadata tells the package manager how to install dependencies, while components.json tells the CLI how and where to install components. Second, aliases in each components.json describe import destinations for components, hooks, utilities, and UI primitives. Third, the shared UI package acts as the stable import boundary for reusable generated code, commonly imported with the workspace package name rather than a relative path.
Sources: apps/v4/content/docs/(root)/monorepo.mdx
The app workspace and the UI workspace intentionally have different responsibilities. The app workspace can point its UI alias at the shared package so generated app code imports primitives from the package instead of from a local components directory. In the visible app configuration, the UI alias resolves to the shared package components path, and the utility alias resolves to the shared package utility path. That is why a page or feature component in the app can import a button from the shared UI package while still keeping app-specific files in the app workspace.
Sources: apps/v4/content/docs/(root)/monorepo.mdx
Getting Started Workflow
To create a new first-party monorepo, run the init command with the monorepo flag and then choose a template. The guide lists framework choices including Next.js, Vite, TanStack Start, React Router, and Astro. The generated project uses Turborepo as the build system and creates the workspaces needed for the app and shared UI package. This path is the lowest-friction option because the workspace layout, configuration files, shared styles location, aliases, and package imports are created together instead of being assembled manually after the fact.
Sources: apps/v4/content/docs/(root)/monorepo.mdx
npx shadcn@latest init --monorepoAfter initialization, add components from the app workspace, not from an arbitrary package directory. The official guide tells readers to change into the application path before running the add command. That matters because the CLI uses the app workspace configuration to decide whether a registry item is a reusable UI component, an app-owned component, or a dependency of another registry item. For example, adding a button installs the button under the shared UI package and updates app imports. Adding a larger block such as a login form can install underlying primitives in the shared package while placing the form composition in the app components area.
Sources: apps/v4/content/docs/(root)/monorepo.mdx
cd apps/web
npx shadcn@latest add buttonThe templates also support running the add command from the repository root by passing the app workspace with the config flag. The Next.js monorepo template documents adding a button with a command that targets the web app and places UI components in the shared package source components directory. The Astro monorepo template shows the same idea with npx. Use the app workspace as the command target because its configuration describes how app imports should resolve to the shared UI package.
Sources: templates/next-monorepo/README.md, templates/astro-monorepo/README.md
pnpm dlx shadcn@latest add button -c apps/web
npx shadcn@latest add button -c apps/webImports and File Placement
The canonical import style is through the shared workspace package. A React or Next.js file imports a button from the UI package component path, while hooks and utilities can be imported from corresponding package paths. The Astro template demonstrates the same shared import inside an Astro frontmatter block, then renders the imported component in the page body. These examples are important because they show that monorepo support is not just file placement; it also produces an import contract that application code can rely on across frameworks.
Sources: apps/v4/content/docs/(root)/monorepo.mdx, templates/next-monorepo/README.md, templates/astro-monorepo/README.md
import { Button } from '@workspace/ui/components/button'
import { useTheme } from '@workspace/ui/hooks/use-theme'
import { cn } from '@workspace/ui/lib/utils'The generated layout separates application files from reusable package files. The app area contains the application entry points, app-local components, its own components.json, and its package metadata. The shared UI package contains source folders for components, hooks, libraries, and styles, plus its own components.json and package metadata. This structure is especially useful when a registry item contains both low-level primitives and higher-level compositions. The CLI can install the primitives once in the shared workspace while allowing a feature composition to remain in the app where it can reference app routes, data, and local layout decisions.
Sources: apps/v4/content/docs/(root)/monorepo.mdx
apps
└── web
├── app
├── components
├── components.json
└── package.json
packages
└── ui
├── src
│ ├── components
│ ├── hooks
│ ├── lib
│ └── styles
├── components.json
└── package.jsonCLI Detection and Workspace Targeting
The CLI utility for monorepo awareness starts by detecting whether the current directory looks like a monorepo root. It checks for pnpm workspace configuration, package manager workspaces in package metadata, Lerna configuration, and Nx configuration. This detection is deliberately based on common workspace signals rather than on one framework. Once a root is detected, the utility can guide users toward a specific workspace instead of letting a command run at the root where there may be no single correct components.json destination.
Sources: packages/shadcn/src/utils/get-monorepo-info.ts
Workspace discovery is based on workspace patterns. The utility reads pnpm workspace packages and package manager workspace definitions, filters out negated package patterns, resolves candidate directories with fast-glob, and ignores node_modules. A directory must have package metadata to be treated as an actual workspace. It becomes a candidate target when it either has a components.json file or a recognized framework configuration file such as a Next, Vite, Astro, Remix, Nuxt, Svelte, Gatsby, Angular, or similar config signal. The result is a concise list of plausible app workspaces, each marked by whether it already has shadcn/ui configuration.
Sources: packages/shadcn/src/utils/get-monorepo-info.ts
When a command is run from a monorepo root, the formatter prints a message explaining that the command appears to be running at the root and that the user should target a workspace with the cwd flag. It then prints suggested commands for each discovered target. This is the CLI counterpart to the documentation instruction to run add in the path of the app. In practice, the two interfaces reinforce the same rule: choose the app workspace as the command context so the CLI can read the correct aliases and install files to the intended package.
Sources: packages/shadcn/src/utils/get-monorepo-info.ts
Configuration Requirements
The most important requirement is that each participating workspace has a components.json file. The guide states this explicitly because monorepos cannot rely on a single repository-wide configuration to describe every package. The app configuration shown in the documentation points its Tailwind CSS file at the shared UI package styles file, uses a base color, enables CSS variables, and defines aliases that distinguish app-local components from shared UI components. Those aliases are what let generated files use stable package imports rather than fragile relative paths across workspace boundaries.
Sources: apps/v4/content/docs/(root)/monorepo.mdx
A healthy configuration should answer two questions for every generated file: where should this file be written, and how should other files import it? The UI package configuration answers that question for reusable primitives, utilities, hooks, and styles. The app configuration answers it for app-owned components and for references back to the shared UI package. If imports look wrong after generation, check the app workspace components.json first, especially the aliases for UI and utilities. If files are appearing in the wrong package, confirm that the command is targeting the app workspace and that both workspaces have valid configuration files.
Sources: apps/v4/content/docs/(root)/monorepo.mdx
Framework Template Notes
The Next.js template uses the same shared package import as the main guide. Its README specifically instructs users to add from the root of the web app by passing the app path with the config flag, and states that UI components will be placed in the shared package source components directory. This confirms that the template is not merely a sample folder layout; it is wired for CLI generation into the shared package while the app consumes generated components through the workspace package name.
Sources: templates/next-monorepo/README.md
The Astro template follows the same workspace contract with Astro-specific usage. It defines the app workspace as an Astro application and the package workspace as shared shadcn/ui components. Its example imports the button in an Astro file frontmatter section and renders it in the body. The key point for Astro users is that the import boundary stays the same even though the consuming file format changes. The shared package is still the source of reusable UI, and the app workspace remains the CLI target.
Sources: templates/astro-monorepo/README.md
Practical Next Steps
Start with the generated monorepo flow when possible, because it creates the expected app and UI workspaces, configuration files, aliases, and shared style path together. When adding components later, run the command in the app workspace or pass the app workspace with the config flag from the root. Keep reusable primitives in the shared UI package and keep app-specific compositions in the app workspace. Next, read the components.json reference to understand each configuration field, the package imports guide to align workspace import aliases, and the CLI page for the broader command surface around init and add.
Sources: apps/v4/content/docs/(root)/monorepo.mdx, templates/next-monorepo/README.md, templates/astro-monorepo/README.md