Sharing React components using Bit — a quick guide — Part One
Introduction
One of the long-standing problems developers face — in personal or work projects — is the effectiveness of code sharing and more specifically component sharing and collaborative work on these components.
Let’s examine the following scenario. We have a collection of base React components that have simple or little functionality, for example a Button
component, that has different types of styling that can be used using the appropriate props. Ideally, we would like to use this component on every Front-End project in the company, or at least where it’s actually useful.
What would be the ideal sharing concept at this point? We want to easily plug in the aforementioned Button
in a new React project, and use it as is. We also might want to use the component, but the requirements change in the meanwhile. In this case, we want to import the component, make some changes, tag those changes as an version update on the component, and update all occurrences of the component in various Front-End projects.
There are a few ways to create this kind of setup in our projects. We can create repositories for every component we create, and use them as npm packages. This, however, requires a lot of maintenance of the npm packages, and can be confusing and time-consuming for a large collection of components.
This is where Bit comes to solve these problems. Using bit we can create our own (private or public) scope to store our components. These components then can be imported using NPM/Yarn to use without changes, or via Bit to import locally and make changes.
Storing the components in our scope, we can also add examples, as above, and documentation using JSDoc. For this component, we added title, description, examples and properties in our main js file.
/**
* @render react
* @name Button
* @desc Button component with different styles
* @example
* <div><Button type="button"> Primary </Button>
* <Button styleName="secondary" type="button"> Secondary </Button>
* <Button styleName="tertiary" type="button"> Tertiary </Button>
* <Button styleName="outline" type="button"> Outline </Button>
* <Button styleName="link" type="button"> Link </Button></div>
* @property {string} classname - classname
* @property {boolean} fullWidth - makes button fullwidth on true
* @property {boolean} disabled - makes button disable on true
* @property {string} type - one of button, submit, reset
* @property {string} styleName - bione of primary, secondary, tertiary, outline, link
*/
But first of all, let’s take a step back and take a look at how we can install and use Bit in our project
Setting up Bit
- Install bit globally by
npm install bit-bin -g
or with package manager of your choice (Yarn / Brew) - Go into the project directory and execute:
bit init
. This creates a bit.json file that is the configuration for the local bit workspace, and a .bitmap file that is used to map the paths of your tracked components and their dependent files. - Make sure you are registered in bitsrc.io and you have created the scope that you wish to store your components in. It can be public or private. In our case it’s private.
- Ensure that you have access to this scope by authenticating through the Bit CLI:
npm login --registry=https://node.bitsrc.io --scope=@bit
. This will allow us to import components using the@bit
prefix.
Configuring Bit
Let’s look now at how we would like our Bit configuration to be. We are going to change two basic configuration options: componentsDefaultDirectory and compiler.
componentsDefaultDirectory: This defines the default directory our newly locally imported components will be stored. In our case, all components are stored in src/components
folder, so we set it as: src/components/{namespace}/{name}
Namespace and name are two variables we can use. For example the first component we created was tracked as global/button. The namespace is global and the name is Button. We use this default directory because of our components’ structure in our project, and we want to be consistent.
compiler: Bit uses compilers, which are environments — a special kind of extension, in order to build components. There are a few ready-to-use compilers in Bit’s envs scope. In our case we are going to use the webpack-css-modules compiler, since we are using webpack and css modules. To use this compiler and store it in our configuration as our default compiler we use the provided bit import command and we add the --compiler
option, so bit import bit.envs/bundlers/webpack-css-modules --compiler
This command will import the compiler and change the bit.json file to document this as our default compiler.
Finally, our bit.json looks like this:
{ "env": { "compiler": "bit.envs/bundlers/webpack-css-modules@0.0.8" }, "dependencies": { "bit.envs/bundlers/webpack-css-modules": "0.0.8" }, "componentsDefaultDirectory": "src/components/{namespace}/{name}", "packageManager": "npm"}
More configuration options can be found here.
Tracking components
In case we want to add a new component to our Bit repository, to enable it to be used across other projects, we can use the add
command.
We wish to track and export the Titles
component in namespace global
:
bit add src/components/Titles --id global/titles
We define the component’s directory, and the namespace/name of it with the --id
flag.
Now, we want to see the current status of our bit project and we can do that by using bit status
. Components that have been changed will show up here.
If we want to see all the components in our local scope, including the ones we didn’t change, we can use bit list
.
Another useful command is bit show <namespace>/<name>
which displays all the relevant information about a specific component.
Our component is now successfully tracked, let’s take a look at the changes in our local .bitmap file:
If we wanted to track all our components with a single command in the same namespace we could use this: bit add src/components/* --namespace global
This would track every folder in src/components directory as a separate component. Namespace would be global and name would be the name of the corresponding folder.
Tagging and exporting components
When we make changes in a tracked component, we can tag these changes, as a new version for our components. Let’s look at an example:
We will add documentation to our main file as described above with JSDoc formatting.
Note: to correctly render a component on the remote Bit workspace and display the example, we need to export our react class as default.
Now that we made the change, let’s tag the Titles component with a new version. Since we just created this component, this is going to be the initial version of it. We run again bit status
and we see that our component is listed under modified. We can tag it by using bit tag
Different options for tagging:
bit tag --all
tags all components by bumping the previous version (eg. a component with previous version 1.0.0, after this will have a version of 1.0.1.bit tag <namespace>/<name>
, same as above but for a specific component--major
: use this to bump major version--minor
: use this to bump minor version--patch
: use this to bump patch version (default)bit tag <namespace>/<name> <version>
. Tag a specific component to a specified version.
We are going to use the last option for our first tagging of this component:
Now, our component is tracked and tagged and is ready to be exported to our remote workspace to be used by other developers. The component will be exported in an isolated environment, with all its dependencies automatically tracked, so that it can be used in any React project. Let’s export it!
Our component is now exported successfully, and can be imported using npm/yarn and bit for making changes to it.
Note: using the --eject
flag, when exporting will eject this component from our local workspace and import it as an external dependency. Warning: this doesn’t change the import statements we already use, we have to change these by hand.
In our project, I created a custom script called tag-export
that tags all components and exports them to our scope, we can run it using yarn tag-export
.
Importing components
We covered a simple case of tracking, tagging and exporting a component to our remote workspace. Now, we want to import and use this component on another project. Going to the component’s specific page on bitsrc.io we see three options for importing a component, which leads us to two different cases:
Importing a component using npm or yarn
This is the case where we want to use the component as is, without making any changes to it, and adding it as an external dependency.
npm i @bit/<username>.<scope>.<namespace>.<name>
yarn add @bit/<username>.<scope>.<namespace>.<name>
The component will be added on our node_modules folder and can be used as an external dependency. We created an empty react project and imported it.
Then we import it in the app by: import Title from '@bit/<username>.<scope>.global.titles';
and then we use it.
Importing a component using Bit
Let’s try now to import a component using Bit, to make local changes, tag the new version and export it while ejecting it. This will examine if Bit can allow us to perform the ideal scenario we defined in the Introduction of this article.
We import it using: bit import <username>.<scope>/<namespace>/<name>
Notice that @bit
is not needed to import as opposed to npm/yarn
That’s it! Our component is successfully imported and we can start making changes! 🙏
Note: We can still use the previous import declaration import Title from '@bit/<username>.<scope>.global.titles';
because bit import created a symbolic link from the corresponding node_modules folder to the actual folder we are editing now :)
Important Note: in order to avoid linting errors when importing a component via bit (webpack compiler builds components in UMD format, to be able to be used anywhere), we need to eject our react app and add this to our package.json to avoid eslint complains:
"eslintIgnore": [ "src/components/*/*/dist", "src/components/*/*/node_modules"],
Titles component is now imported and we can make changes to it (to preview changes use bit build
)
Following the instructions above: we make the changes we want in the component and we build it to preview it. If changes satisfy us, we can tag a new version for the component and export it using the--eject
flag. After that our component will again be used as an external dependency.
Updating the component in other places it is used
After exporting the new version with the required changes, any project that uses the component and we need to update its version, we simply need to run
npm install
or
bit install
and the component will be updated in our project if there is a new version available
Summary
This article covers most basic functionalities of bit and provides use cases and examples of a Front-End React project that shares components with other React projects. Next up, global css modules, theming, testing :)
Go to Part Two, exporting a component containing all our Sass variables!