DEV Community

Mariano Fernández Cocirio
Mariano Fernández Cocirio

Posted on

Unsolving the mysteries of yarn/npm link for libraries development

Introduction

Imagine you are developing a web app, that web app belongs to a family of web apps who shares the same style in their visual components, which means that you are probably going to build a UI library to centralize all your components. That will give you the possibility of changing some behaviour, even look and feel of specific components across all your applications by just modifying that library.

Now we have an issue: How do we test our library integration with the rest of the ecosystem locally without releasing a version every time we modify something? That’s a common bad practice that you’ll see at some companies, just releasing beta/minor versions to test stuff as they don’t have a local way to test the integration of the library with their apps.

Testing the individual components from your library can easily be done by using for example Storybook, but the integration with the rest of your applications is the tricky part, here is where you should use yarn link, the idea behind this is pretty simple: It just creates a symlink to whatever you point to.

Time to add some real work

Let assume we have the app myApp who uses myLibrary-UI, being myLibrary-UI the UI library that provides all the base component to myApp. You are gonna have something like this in your package.json:

Right now when we run yarn we can say that it generates the following structure: myAppis going to contain our library in its package, but if we want to use the local version instead of the published version, how do we override the version it uses?

Our app containing our library

Our app containing our library

It’s pretty simple, you should just build your library locally, then you run yarn link in the directory you build it, by doing it you’ll register your package locally. After this, you should just go to the root directory of your application and run yarn link "@yourCompany/myLibrary-ui", this will create a symlink to your local copy that will be resolved before the copy installed by yarn.

  • WARNING: if you run yarn again, this link might disappear, you’ll need to run the last command again.

Our app linked to our local version of the library

Our app linked to our local version of the library

By now we already have our app running using our local version of the library, this way we can easily test the integration of the new version with our app, and we can also use it to prepare our app to adopt any breaking changes from our library in case we need a simultaneous release. This might seems to be pretty straight forward but now is when the real issues begin.

Multiple definitions

I’ll assume we are using React in our library, but we are also using React in our application, this should be easily solved by our package manager while installing them in a regular way.

React is just installed at the top level, then used by children

React is just installed at the top level, then used by children

But as we are linking it locally we have the problem of multiple React definitions in our project, this gonna generate multiple errors that are not that easy to debug and really not very descriptives, like for example:

  • Unhandled Rejection (Invariant Violation): Invalid hook call. Hooks can only be called inside of the body of a function component.

React is defined two times causing errors

React is defined two times causing errors

The easy way to fix it is by using the link command, we just go to our library and we create a link from there the React definition in our application, something like this: npm link "../myApp/node_modules/React”. This will create a symlink in our library to the React definition in our app. The result is our project will only use the React definition in myApp but it will run our local version of myLibrary-UI.

Now we are using just the main definition

Now we are using just the main definition

Now you are able to develop your library and test its integration with your apps locally just by using link commands. There are other ways to avoid multiple definitions, for example, let’s say we are using styled-components in both packages, but our app is also using NextJS, we can solve this issue by adding the resolver for this specific package in Webpack configuration:

Conclusion

Sometimes it’s really important to test your library locally so you can test its integration with other apps that use it. That’s really tricky sometimes but it helps you a lot to develop a better library.

By using link command you can easily achieve a configuration that allows you to test your libraries in a local environment, saving you from making for example test releases to check the integration.

Hope you like this article and most important I wish you find it helpful, I’ve been fighting with issues to test integrations between multiple packages a couple of times, some of this came out after several fails by myself.

Top comments (6)

Collapse
 
andirkh profile image
Andi R. Hermawan

Thanks man, your post saving my day

Collapse
 
bgrand_ch profile image
Benjamin Grand

Thanks a lot!

Collapse
 
johnsoncherian profile image
Johnson

Thanks dude, I wish I had seen this post earlier. It would have saved a lot of mine and my teams' time.

Collapse
 
unnsteinngardars profile image
Unnsteinn Garðarsson

Saved my day!

Collapse
 
gadi_tz profile image
Gadi Tzkhori • Edited

What about HMR in the module? Do I need to run build on every change in my module?

Collapse
 
mfco profile image
Mariano Fernández Cocirio

Yes, because this way you are linking to the already build version