This POC and repository started in a private repo, at github.com/MikelArnaiz/turborepo-basic.
Welcome to this monorepo Proof of Concept (POC), powered by Turborepo.
This repository was initially set up using the official Turborepo starter:
pnpm dlx create-turbo@latestHowever, numerous modifications have been made to evaluate whether a monorepo is the right fit for our needs. To understand the changes made to the original template, refer to How was this project set up.
For ongoing questions and TODOs related to this POC, please check TODOs.
This POC is exclusively focused on Turborepo. While there are alternative solutions like Nx, time constraints led us to concentrate solely on Turborepo. Here are a few reasons for choosing Turborepo:
- Alignment with our tech stack: Turborepo is developed by Vercel, the same company behind Next.js.
- Future-proof: It is expected to eventually merge into a unified tool with Turbopack, the successor to Webpack.
To develop all apps and packages, use the following command:
pnpm dev
To build all apps and packages, run:
pnpm build
This POC uses pnpm as a package manager.
Refer to pnpm.io/installation for installation instructions.
It is also possible to use yarn as the packege manager of the monorepo.
Note that from yarn 2 the yarn version itself was supposed to be commited to repository.
With yarn 4 this changes slightly as is not longer mandatory if you are using corepack.
With Node 18+ corepack is available by default.
When running yarn, it will try to find a yarnrc.yml, first in the current directory and the in the parent, and then in parent of the parent and so on.
To set the yarn version, we advice to do so in either your projects folder, or in your user ~
corepack enable
yarn set version 4.0.1
It will create a package.json that you can remove as the current directory is not meant to be a project.
rm ./package.json
With that set, you are good to create a project, e.g turborepo
yarn -v // 4.0.1
yarn dlx create-turbo@latest
? Where would you like to create your turborepo? my-turporepo
? Which package manager do you want to use? yarn workspaces
cd my-turporepo
yarn install // it will use the nearest `.yarnrc.yml` file
yarn run dev
This Turborepo includes the following packages/apps:
docs: a Next.js appweb: another Next.js appui: a stub React component library shared by bothwebanddocsapplications. Published to npm asmarnaiz-turborepo-uieslint-config-custom:eslintconfigurations (includeseslint-config-nextandeslint-config-prettier)tsconfig:tsconfig.jsons used throughout the monorepo. Published to npm as `@marnaiz/turborepo-tsconfig
Our research shows that the monorepo philosophy is to work always with the latest changes.
That's achived by pointing dependencies to workspace:*, e.g.:
"eslint-config-custom": "workspace:*",
This would mean a big change in the way we work.
Until now you were able to create a release with breaking changes and it was up to consumers to fix those breaking changes when adoption the new version.
With a monorepo where everything is always working on latest the responsibility of fixing those breaking changes relies on the person introducing the breaking changes.
To avoid such a radical change we can and will be using versioning, releasing packages to the registry. We will do that with a 3rd party tool; changeset.
Changeset is a CLI that after answering which packages we want to release, and how, whether major, minor or patch, it will create a file inside .changeset/ with the provided data.
Afterwards we have two options
-
Locally
- run command to update package versions;
pnpm changeset:version - publish;
pnpm publish
- run command to update package versions;
-
Let Github action do that.
- Once your PR get's merge, a new PR will be created automatically updating versions
- When this latter PR get's merge, it will publish the packages
We advice for the latter.
To illustrate, and to release a new version of the ui package, including changes to the <Button> component.
-
Create changeset file
When we have all the changes we need and want to prepate a release we create a changeset file.
pnpm changesetIt will ask you which packages to update, usually those with changes. Choose between major, minor and patch
Write a message for the release notes. It will generate a 3 word file inside
./.changeset/directory. File that you should commit. -
Commit and push changes
-
Create PR and merge it.
-
Second PR will be created
When this PR get's merged, the publishing of the new versions will happen

changeset supports both preseleases and snapshot releases.
To create a prerelease, e.g. 1.2.3-next.0, you should first enter in pre mode;
pnpm changeset:version:pre
Once you are ready to release a stable release, 1.2.3, you should exit pre mode:
pnpm changeset:version:pre:exit
We have configured a github action to react on the /publish command and create a snapshot release.
The release will be done in the alpha tag.
Turborepo can use a technique known as Remote Caching to share cache artifacts across machines, enabling you to share build caches with your team and CI/CD pipelines.
By default, Turborepo will cache locally. To enable Remote Caching you will need an account with Vercel.
cd my-turborepo
npx turbo login
This will authenticate the Turborepo CLI with your Vercel account.
Next, you can link your Turborepo to your Remote Cache by running the following command from the root of your Turborepo:
npx turbo link
It can also be set up to use a custom server for remore caching.
As a mere showcase I have deployed the apps to Vercel.
- Docs: https://turborepo-basic-docs-pearl.vercel.app/
- Web: https://turborepo-basic-web-ivory.vercel.app/
To do so you have to create a project per app you want to deploy, and make sure to change the config and point to each app directory:
While working inside the monorepo packages/ don't need to be bundled but if we want to consume them from a microservice (installing it from npm) we need to do so.
Until recencly it was common to generate commomJS (CJS) files, however we should aim to produce ES modules (ESM) so bundlers like webpack can do tree shaking and keep ES6 code if needed.
For marnaiz-turborepo-ui I needed to configure the package. It was a chanllenge to make it compliance with ESM and CJS. After lots of try and error it works now.
Key points.
package.jsonshould not have atypeproperty,mainshould point to thecjsversionmoduletomjs(aka ESM) versiontypestocjsexportsproperty with bothrequire(CJS) andimport(ESM) props.
Example from packages/ui/package.json
"name": "marnaiz-turborepo-ui",
"version": "1.0.10",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"files": [
"dist",
"src"
],
"exports": {
"./package.json": "./package.json",
".": {
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
}
},
"scripts": {
"build": "tsup src/index.ts --format esm,cjs --dts",
}The resources I used were:
- https://arethetypeswrong.github.io/
- https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseCJS.md
- https://tsup.egoist.dev/#bundle-formats
- https://blog.isquaredsoftware.com/2023/08/esm-modernization-lessons/#round-2-results
Learn more about the power of Turborepo:





