Finally, you completed the task of creating a fantastic input field for the Input in your app. You are happy with the outcome and will continue to work on your assignments.
Next week, a team leader from another team asks you for help: "I need this done as soon as possible. Would you be able to lend a hand?" You happily accept.
Your organization tries to maintain a consistent brand, so you're not surprised to find that they want you to build a similar input element as you built a week ago.
"I'll just copy and paste from my team's repository", you say to yourself. Copying and pasting is not difficult, but what if we want to add styles to the input? You'll have to change all occurrences of it. If there are only two components, it's fine, but what if there are hundreds?
In this blog post, you will learn how to reuse every component that you have created using Bit. This is not a mistake. The components you create can easily be reused across all of your organization's apps. Ready. Set. Let's go!
Clearly, we want to reuse that input component, but we don't want to copy-paste since it is not scalable, so what can we do?
Using Bit, we can extract the input component. Bit will handle everything for us, from versioning, building, testing, and even managing our component dependencies.
Create a React project and place this folder in the src folder. This folder contains the implementation details for the input component.
npx create-react-app reuse-comps
We'll have to modify package.json
:
"@testing-library/react": "12.1.5",
"react": "17.0.2",
"react-dom": "17.0.2",
Installing Bit is as simple as running a single command:
npx @teambit/bvm install
Now at the root of our project, we can initialize a Bit Workspace:
Time to start tracking the component:
We also have to modify the env of this component to use the React env:
Remember to replace [your-bit.cloud-account]
with your bit.cloud
account and open a remote scope called reuse-components
😉 .
You will see the following output:
tracking component ui/input:
added input.tsx
added index.ts
added input.module.scss
The component will be added to the .bitmap
file:
"ui/input": {
"scope": "",
"version": "",
"defaultScope": "nitsan770.reuse-components",
"mainFile": "index.ts",
"rootDir": "src/Input",
},
Congratulations, you have freed your component! With Bit tracking your component, you'll be able to use it in any React project you have. We'll talk about it soon.
Let's install all dependencies:
Compile all components to create the dist folder for our component:
And run the dev server so we can view our beautiful independent component in the Workspace UI:
The development server is up and running! However, we are unable to see anything 😔 . This is because we have not created any compositions for our newly created component.
Compositions allow components to be simulated in different variations. These can then be used for testing, visualization, and discoverability.
Let's create a composition for our component:
input.composition.tsximport React from 'react';
import { Input } from './Input';
export const BasicInput = () => <Input buttonText="Submit" />;
Now we can see the component in the Workspace UI:
Yet it is difficult to understand how it works. Let's write some documentation.
Bit components' documentation is written in MDX. No more boring sentences; now you can embed anything you want to make your component more visually appealing.
input.docs.mdx---
description: An input element with a built-in button.
labels: ["input", "submit form"]
---
import { Input } from "./input";
### Modify the text on the button
'''js live
<Input buttonText="Go" />
'''
It looks much better now, and consumers can try out the component before installing:
Last but not least, a good component is well-tested:
input.spec.tsximport React from 'react';
import { screen, render } from '@testing-library/react';
import { BasicInput } from './input.composition';
it('Renders the input', () => {
render(<BasicInput />);
const input = screen.getByRole('textbox');
expect(input).toBeInTheDocument();
});
it('Renders the button', () => {
render(<BasicInput />);
const button = screen.getByRole('button');
expect(button).toBeInTheDocument();
});
it('Renders the button text', () => {
render(<BasicInput />);
const button = screen.getByRole('button');
expect(button).toHaveTextContent('Submit');
});
Let's run the tests:
All tests have passed. 🎉
PASS src/input/input.spec.tsx
✓ Renders the input (67 ms)
✓ Renders the button (19 ms)
✓ Renders the button text (12 ms)
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 5.549 s
Ran all test suites.
test has been completed in 13.718 seconds.
Our last step is to tag the component with a (first) version number.
bit tag -m "first version"
When a component is tagged, it will also go through a build process in addition to locking the current state of the source files.
In this process, the component will be compiled to a distributable format and packed into a tar file.
Once the component has been tagged, it's time to publish it.
First you must login to your bit.cloud account through the CLI:
Then you can publish the component:
The component will be exported to the remote scope on bit.cloud.
Now that it has been published, we can use it in any React project.
Let's try it out!
Open another React project:
npx create-react-app another-project
You can install the component using npm, yarn, or pnpm. Let's use pnpm
:
pnpm i @nitsan770/reuse-components.ui.input
## if you haven't yet, remember to add your bit.cloud account as a scoped registry:
# npm config set '@nitsan770:registry' https://node.bit.cloud
We can now use the component in the app.js
file:
import logo from './logo.svg';
import { Input } from '@nitsan770/reuse-components.ui.input';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<Input buttonText="Submit" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
Let's run the dev server:
npm start
It's a perfect match!
The moment you started the dev server, a product manager from your company entered the room.
"What a nice button! I need one like that for my project!"
"Do you mean the input component?"
"No, I mean the button!"
Are you thinking about what I am thinking? Let's decompose the component into two smaller components!
Let's stay on the same project. Initialize a new Workspace:
And import the component into it:
The command above will import the input component into our Workspace. Once imported, we can then edit and export a new version. It's like installing it, but you are in control.
We will begin the refactor by creating a component called ui/button
Let's separate the button component from the input component:
button.tsximport React, { ReactNode } from 'react';
import classNames from 'classnames';
import styles from './button.module.scss';
export type ButtonProps = {
/**
* a node to be rendered in the button
*/
children?: ReactNode,
} & React.ButtonHTMLAttributes<HTMLButtonElement>;
export function Button({ children, className }: ButtonProps) {
return (
<button className={classNames(styles.button, className)} type="submit">
{children}
</button>
);
}
Of course, it also has styles. Since some of the styles are specific to the input component implementation, we'll keep them in the input component:
button.module.scss.button {
font-size: 16px;
line-height: 24px;
align-items: center;
background: #6c5ce7;
border: none;
border-radius: 8px;
color: #fff;
cursor: pointer;
text-decoration: none;
transition: background-color 0.3s ease-in-out;
min-width: fit-content;
}
Finally, we can refactor the input. We are not changing the API because we don't want to break the component for those who already use it.
input.tsximport React from 'react';
import { Button } from '@nitsan770/reuse-components.ui.button';
import styles from './input.module.scss';
export type InputProps = {
buttonText: string,
} & React.InputHTMLAttributes<HTMLInputElement>;
export function Input({ buttonText, ...rest }: InputProps) {
return (
<div className={styles.inputContainer}>
<input {...rest} className={styles.inputText}></input>
<Button className={styles.button} children={buttonText} />
</div>
);
}
Here are the styles we are leaving in the input component:
input.module.scss.button {
position: absolute;
width: 103px;
height: 40px;
right: 8px;
top: 8px;
width: 30%;
}
The last step is to tag the button component with its first version and the input component with its second version.
bit tag -m "decompose the button component from the input"
2 component(s) tagged
(use "bit export [collection]" to push these components to a remote")
(use "bit untag" to unstage versions)
changed components
(components that got a version bump)
> nitsan770.reuse-components/ui/input@0.0.2
> nitsan770.reuse-components/ui/button@0.0.1
Now we have two independent components that can be installed and used in any project.
We have learned how to separate the components from our projects into independent components that can be used anywhere.
Moreover, we learned how to decompose components into smaller pieces and use them in any project we like.
I hope you enjoyed this tutorial.
Please read our documentation if you want to learn more.
If you have any questions, please ask them in our slack channel.
Best of luck!