We recently rewrote Dub's codebase to use a monorepo setup via Turborepo.
In the process, we learned a lot about monorepo management – here are some of our learnings.
What is Turborepo?
Turborepo is a high-performance build system for JavaScript and TypeScript codebases.
It's built by the team at Vercel – who is also behind the Next.js framework.
Why rewrite to Turborepo?
As an open-source project, we want to make our local development setup as easy as possible – both for self-hosting and for contributing to the project.
However, our existing setup was a bit of a mess. We had both our marketing site and our main app in the same codebase, and we had to run both of them in order to develop locally.
This led to a lot of confusion for new contributors since they would had to set up dependencies and environment variables that were only relevant to the marketing site – just to contribute to the main app.
Rewriting to a monorepo setup allowed us to decouple the two codebases, and improve the local development experience.
The migration process
The migration process was relatively straightforward. We followed the official Turborepo + TailwindCSS example and made some tweaks to fit our needs.
There are plenty of amazing guides on this topic (e.g. this fantastic one from Lee Robinson) so we won't dive too deep into it, but rather focus on some of the gotchas that we ran into in the process.
Gotcha #1: Why are my Tailwind styles not working with Turborepo?
If you're using Tailwind with Turborepo, you might run into an issue where your Tailwind styles are not working properly.
To fix that, all you need to do is make sure you configure the content
option in your tailwind.config.js
file to include the paths to your UI package.
For example, if you are using your local UI package in one of the apps in your monorepo, you would need to add the following:
If you are using your UI package as a third-party import in a different codebase, you should do this instead:
Shoutout to this blog post by Will Liu for the pro-tip!
Gotcha #2: Differences between dependencies
and devDependencies
When you're working with a monorepo, you might run into issues with inter-package dependencies.
For example, in our Dub codebase, we had a @dub/ui
package that depended on a @dub/utils
package.
Naturally, we would add the @dub/utils
package under the dependencies
field in the package.json
file of the @dub/ui
package.
While this would work fine in the monorepo, this would cause issues when we publish the @dub/ui
package to npm. When you install the @dub/ui
package from npm, it would throw an error saying that it could not find the @dub/utils
package with the workspace:*
tag.
To fix this, we had to move the @dub/utils
package from the dependencies
field to the devDependencies
field.
This would ensure that @dub/uitls
won't block the installation of the @dub/ui
package from npm – and since you are also importing the @dub/utils
package, it would still be available in your local development environment.
Gotcha #3: Latest versions of tsup
doesn't export .d.ts
files
If you're using tsup
to compile your Typescript packages, you might run into an issue where your .d.ts
files are not being exported properly, causing your Typescript imports to fail.
To fix this, we had to downgrade to tsup@6.1.3
:
This issue might be fixed in the future, so keep an eye out on the tsup
changelog to see if you can upgrade to a newer version.
Pro-tip: Use prettier-plugin-organize-import
When migrating to a monorepo setup, you might need to bulk update your imports to use the new package names.
This is where the prettier-plugin-organize-import
plugin comes in handy.
It helps you organize your imports in a consistent manner, as well as auto-combining duplicate imports. Watch it in action below:
Build Faster with Turborepo
We're excited for what this migration to Turborepo unlocks for Dub.
Not only are we now able to build our code faster with Turborepo's high-performance build system, we were also able to decouple our codebases for a better local development experience.
This means that we will be able to improve our self-hosting experience, as well as make it easier for contributors to make changes to the codebase.
Our codebase is fully open-source, so feel free to check it out and learn more about our Turborepo setup.