Updated
—
3 min read
In this post, I'll show you how to share components between NextJS projects with pnpm workspaces.
This approach can use alongside Turborepo for more complex projects, but it's not necessary.
Our goal is to keep things simple and easy to understand, with no build steps or type aliases.
Here's a typical project structure with two NextJS projects and a shared components package.
project/ ├─ apps/ │ ├─ web/ # 1st NextJS project | | └─ package.json | ├─ docs/ # 2nd NextJS project ├─ packages/ │ └─ ui/ # Shared components └─ package.json
In the root directory (project/) initialize via pnpm:
pnpm init
The root package.json should have this at the very least:
{ "name": "project", "version": "0.1.0", "private": true, "packageManager": "[email protected]", }
Create a pnpm-workspace.yaml file in the root directory and add the following:
packages: - "apps/*" - "packages/*"
This defines the packages that are part of the workspace.
In packages/ui, initialize a new pnpm package:
cd packages/ui pnpm init
The package.json should look something like:
{ "name": "@project/ui", "version": "0.1.0", "private": true, "exports": { "./Button": "./src/components/Button.tsx" // we'll soon create this file // ... more components as needed }, "peerDependencies": { "react": "^19.2.3", }, "packageManager": "[email protected]", }
Now let's make a simple Button component in packages/ui/src/components/Button.tsx:
import React from "react"; export const Button: React.FC = () => { return <button>Click me</button>; }
We can now re-use this component in the NextJS projects.
In apps/web and apps/docs, install the shared package by modifying their package.json files:
{ "dependencies": { // ... rest of dependencies "@project/ui": "workspace:*", } }
Then run pnpm install in both projects to install the shared package.
Now we can use the Button component in the NextJS projects.
import { Button } from "@project/ui/Button"; export default function Home() { return <Button />; }
And in apps/docs/pages/index.tsx:
import { Button } from "@project/ui/Button"; export default function Home() { return <Button />; }
We're now using the Button component in both the web and docs NextJS projects.
That's it! We have successfully shared a component between two NextJS projects.
Hope this short tutorial helps someone.
Join my newsletter for lessons, experiments, and failures in bootstrapping online businesses.
Sign up if you're curious. I’ll only email you if it's actually good.

Meet the Author
Ryan Chiang
Hello, I'm Ryan. I build things and write about them. This is my blog of my learnings, tutorials, and whatever else I feel like writing about.
What I'm currently building →.