You're managing your component library - creating new components, updating existing components. And you want to test the updates in an application that consumes those components, in a separate repo on your machine.
You could tag and export after every change, and then update the dependency version in your consuming application. But that's not a dev ex that can scale.
What you'd ideally like to do is somehow locally transfer the changes to your consuming project, and have those changes automatically update in the consuming application.
Thankfully Bit can help 😎
For those who prefer following video tutorials, here is this post's sister video:
Bit's link-target feature - in 2 quick steps:
(Step 0: Run bit watch
in your component workspace - this ensures you dont need to manually compile components after each change)
Step 1: Fetch the path of the target project root directory (e.g. by running pwd
in the consuming project)
Step 2: run the following command in the component workspace (replace path with your target project's path):
At this point you will likely need to clear the consuming application's bundler cache.
For webpack
that's in the node_modules/.cache
directory.
For vite
it's node_modules/.vite
.
Delete the relevant directory and restart the application's dev server and you should now start seeing your component updates.
It's really that simple - now any changes you make in your component workspace will be immediately reflected in your consuming application.
In fact you can use this feature to link a component workspace to multiple consuming projects simultaneously!
You can verify, even before running the application, that your consuming app is linked to the component workspace by hovering over the path in the import statement in your application code. The IDE should show you
the path to the imported module, which should now show the path to the component workspace, not the version in local node_modules
.
To restore your application to the regular installed versions of the components, simply re-run dependency installation in your application (e.g. npm install
) - the symlinks will be overwritten and you'll be
back to the project state before bit link --target
.
If you've set up a
postinstall
script as described below, make sure to remove that from your package.json too.
When you run bit link target
Bit creates symlinks from the component workspace's node_modules
directory to the consuming project's node_modules
, for all the components in the workspace.
Under the hood it uses npm link
, but in a targeted way in order to specifically link folders relevant to the components in the workspace and not other, unnecessary packages.
When you change any component's code (and have bit watch
running in the background, or run bit compile
per change) those updates are compiled by Bit to the component's node_modules
directory in the component workspace, which has now been symlinked
to your application project. So now your application project is 'looking' at the live, updated version of your component/s.
--peers
flagThis is where the link --target
feature really comes into its own.
Symlinking between projects isn't as simple as it sounds - for symlinking components to work you can't only link the components to the target project, you must also link any peerDependencies
of the linked components too (we won't go into the
intricacies of that here, but suffice to say that your application won't function as expected in numerous scenarios if peerDependencies
are not linked too).
Bit automatically calculates dependency graphs for all components as part of its feature set, so Bit knows which packages need to be symlinked as peerDependencies
to complete the link process to the consuming application.
The --peers
flag tells Bit to link those peer dependencies too, and ensures the feature will work in any scenario.
A previous version of this feature, from mid-2023, did not have the --peers
flag and had 'blind spots', which have now been filled - so if you've tried this before and it didn't work for you, it's worth trying again now.
Depending on your bundler and its config, your dev server may not be set up to monitor changes in node_modules
.
For webpack
you can add the following to your application's webpack.config.js
- replace @org
with your Bit org:
watchOptions: {
ignored: [
'node_modules/(?!@org/.+)',
'@org/.+/node_modules',
],
},
For vite
you can add the following to your application's vite.config.js
:
server: {
watch: {
ignored: ['!**/node_modules/@your-org/**']
}
},
// The watched package must be excluded from optimization,
// so that it can appear in the dependency graph and trigger hot reload.
optimizeDeps: {
exclude: ['@your-org']
}
As noted above it's easy to revert the project back to its non-linked state, but that also means this could happen inadvertantly by installing packages in the consuming application while you're still using the symlinks.
So if your component updates suddenly stop updating in the consuming application, this is probably what's happened. Just run bit link --target
again to re-establish the symlinks as before.
You can create a node
script to run in the consuming application/s package.json to re-sync the symlinks after running npm install
:
'use strict';
// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = 'development';
process.env.NODE_ENV = 'development';
const { exec } = require('child_process');
const path = require('path');
// Define the directory where you want to run the command
const componentsWorkspaceRoot = '/Users/benjamingilbert/path/to/components-workspace';
const thisDirectory = path.resolve('.');
console.log(thisDirectory);
// Define the command you want to run
const commandToRun = `bit link --target ${thisDirectory} --peers`;
// Run the command in the specified directory
exec(commandToRun, { cwd: componentsWorkspaceRoot }, (error, stdout, stderr) => {
if (error) {
console.error(`Error executing command: ${error}`);
return;
}
if (stderr) console.error(`Command error: ${stderr}`)
else console.log(`Command output: ${stdout}`);
});
You can either run this script manually from your consuming application, e.g. in this case via npm run sync-bit
:
// package.json
{
"scripts": {
"sync-bit": "node ./scripts/link-components.js"
}
}
Or if want this to be fully automated, add it to node's built-in postinstall
script, which will run it automatically after any npm install
::
{
"scripts": {
"postinstall": "node ./scripts/link-components.js"
}
}
Make sure to remove this postinstall
script from your package.json when you want to revert your project back to
its unlinked state