Using yarn symlinks to share code between apps
If you're working on multiple apps and you want to share code between them - for example common utils, data types or UI components - there are several options available.
I was recently browsing a reddit thread about monorepos and sharing code between multiple apps and someone mentioned:
In my experience a monorepo with code symlinked at the src
level is the best. The other options are a pain in the ass
And I wondered - what does "monorepo with code symlinked at the src
level" even mean? And how would you go about setting up something like this?
A bit of browsing through the yarn
docs and I stumbled on this piece:
yarn add link:/path/to/local/folder
installs a symlink to a package that is on your local file system. This is useful to develop related packages in monorepo environments.
So then a monorepo setup using yarn symlinks would simply consist of several libraries and apps, stored as siblings in one main repository, which reference each other through yarn local dependencies.
This can be a great way to share code if:
- you're looking for a simple solution to share code
- you usually release your apps together (i.e. frontend + backend that share a library)
This might not be the best option if:
- you want to release and publish each package / app independently
- you want to be able to lock the shared package to a specific version, if needed
If you think this might be a good solution for you, read along for a step by step walkthrough of how to set this up.
Folder structure
For the sake of this example, we'll use two projects - project-a
and project-b
- and a library that I named very bluntly shared-code
😃
All the code is available on Gitlab, if you want to check it out yourself.
Our monorepo will simply have all of these as siblings at the root level:
- shared-code
- package.json
- yarn.lock
- project-a
- package.json
- yarn.lock
- project-b
- package.json
- yarn.lock
Notice how each app / library is completely independent and has its own package.json
and yarn.lock
file. This is good because it keeps each project simple and fully contained.
Adding dependencies
To make shared-code
available in both project-a
and project-b
you can simply run yarn add link:/path
for each:
cd project-a
yarn add link:../shared-code
cd ..
cd project-b
yarn add link:../shared-code
This will add shared-code
as a dependency in the package.json
file:
Local development flow
When something is changed in the shared-code
library, does it get automatically refreshed or do you need to always rebuild the package?
The short answer is - it depends.
If you use Create React App, this won't work. First, the app expects the shared-code
library to already be built, and second, the default CRA Webpack config does not detect changes in the shared-code
, as it's outside of its src
folder.
If you use your own custom setup, you can ensure hot reload by customizing your webpack config to detect changes in the symlinked packages. This is where having all the code under src
might come in handy.
Continuous integration
How would this work in CI?
Since all the code is in the same repository, the library will always be available to yarn
on yarn install
. However, yarn
assumes the shared-code
is already built, so will need to manually do this step when building any of the projects:
# Sample CI build script
# 1. First build the `shared-code`
cd shared-code
yarn install
yarn build
cd ..
# 2. Build the project
yarn install # Will symlink `shared-code` to the current `node_modules`
yarn build
That's it!
Caveats
This might make for a simple way to setup code sharing and a seamless developer experience, but there are several downsides to be aware of.
The most important one is related to the fact that both project-a
and project-b
always use just one version of the shared-code
- the latest one available at a given point in time.
Since the shared-code
is not published to an npm
repository it's not possible to lock in a certain version. This means that there's a high chance that a change needed for project-a
might break project-b
unexpectedly.
Where to go from here
While this setup is good to get started, with time you might "grow" out of it.
If you notice a lot of your dependencies are duplicated in each of the apps / packages, and you'd like to hoist them to the root level, you might want to look into using Yarn Workspaces.
If you decide you want to publish the packages to an npm
repository and want better versioning and publishing support, you might want to look at Lerna.
I hope you found this useful! Let me know in the comments below if you have any questions or thoughts!