Sharing reusable Vue.js components with Lerna, Storybook, and npm or GitHub Package Registries

José Silva
Apr 24 · 9 min read

Creating reusable components is extremely important these days. We can get a lot of benefits from following the Component-Driven Development (CDD) methodology, it will result in code with more quality and fewer bugs, simply because while developing we can concentrate all our focus on a single component at a time, a single specific task.

It becomes easier to create new applications and even modify them, because we change the component once, and every place where it is being used will reflect that change. However, maintaining, sharing and reusing components can be tricky without the proper tools and that’s what I try to tackle in this article with Lerna, Storybook, npm and GitHub Package Registry.

Photo by José Alejandro Cuffia on Unsplash

What is the end goal of this article?

Create a GitHub repository where some Vue.js components that can be used across projects will be developed. It should be possible to visualize and publish these components to the npm or GitHub package registries. All these components must be scoped inside our username (eg. jsilva-pt).

What tools will we be using?

Lerna

Lerna is a tool that optimizes the workflow around managing multi-package repositories with git and npm.

Storybook

Storybook is a user interface development environment and playground for UI components. The tool enables developers to create components independently and showcase components interactively in an isolated development environment.

npm

npm is the world’s largest software registry.

GitHub Package Registry

GitHub Package Registry is a software package hosting service, similar to npmjs.org, rubygems.org, or hub.docker.com, that allows you to host your packages and code in one place. You can host software packages privately or publicly and use them as dependencies in your projects.

How these tools can work together?

Lerna will be used to keep all our components inside the same repository (monorepos) with independent versions, Storybook to create and visualize components in isolation and npm/GitHub package registry to distribute them to any user.

What will be our steps?

Setting up the project

Create the first component

Create the second component

Improve the JTable component

Moving from npm to GitHub package registry

So, let's start!

Setting up the project

Let’s start by installing Lerna globally and creating a new project:

npm install --global lernamkdir medium-reusable-vue-components && cd $_
lerna init --independent

Here we are creating a project called medium-reusable-vue-components and initializing a Lerna project in independent mode, which is perfect for us, as we can see in their documentation:

“Independent mode Lerna projects allows maintainers to increment package versions independently of each other. Each time you publish, you will get a prompt for each package that has changed to specify if it’s a patch, minor, major or custom change.

Independent mode allows you to more specifically update versions for each package and makes sense for a group of components.”

Then, we install Storybook for Vue

npx -p @storybook/cli sb init --type vue

… as well as the necessary dependencies

npm install vue --save
npm install vue-loader vue-template-compiler @babel/core babel-loader babel-preset-vue --save-dev

In the package.json file we can now find some commands that will allow us to.

{
...
"scripts": {
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"
}
}

By default, Lerna creates a packages folder where every package must be located. However, we are creating UI components so it would be preferable to have a folder called components. Lerna allows us to do it by replacing the configuration in the lerna.json file.

{
"packages": [
"components/*"
],
"version": "independent"
}

To finish the configuration let’s just create a .gitignore file to ignore some files and folders that we don’t want to commit to our repository.

node_modules
storybook-static
npm-debug.log*
lerna-debug.log*

And it's done! The project configuration is done! 💪

Too easy, right? :P

At this point, our project structure must look like this:

├── .storybook
│ ├── addons.js
│ └── config.js
├── components
├── stories
│ └── ...
├── .gitignore
├── lerna.json
├── package.json
└── package-lock.json

Let's start creating some components! 😃

Creating our first component: JTableRow

To start a new component we could just run lerna add <package>, however, it would generate some folders and files that we really don’t want, so let's do it manually.

This component is simply a row of a table that receives and prints the values sent to the prop values.

I like to keep my components, stories, and tests as close as possible to each other, so let's create the JTableRow.stories.js file next to the component:

When we install Storybook, by default, it will search for a stories folder in our root project. However, as we are going to keep the stories next to the components, let's remove the stories folder and change this configuration in .storybook/config.js file.

import { configure } from '@storybook/vue';// automatically import all files ending in *.stories.js
const req = require.context(
'../components',
true,
/\.stories\.js$/
);
function loadStories() {
req.keys().forEach(filename => req(filename));
}
configure(loadStories, module);

Right now, our folder structure must be:

├── .storybook
│ ├── addons.js
│ └── config.js
├── components
│ └── JTableRow
│ ├── JTableRow.stories.js
│ ├── JTableRow.vue
│ └── package.json
├── .gitignore
├── lerna.json
├── package.json
└── package-lock.json

And we should be able to see our first component by running the command npm run storybook, that will give us this page:

Storybook displaying JTableRow stories

We are not done yet! We need to make this component available to be used by other applications/projects/components. In order to do that, let’s create a package.json file inside the JTableRow folder, with the following content:

{
"name": "@jsilva-pt/j-table-row",
"version": "0.0.0",
"publishConfig": {
"access": "public"
}
}

As described in our goal, our components must be scoped. When we create an account on npm, our username can be used as scope name, so we only need to:

Regarding versioning, when publishing a new version of the component, it will be automatically incremented, so we set its version to 0.0.0.

Our final steps are:

git add .
git commit -m "JTableRow component"
git remote add origin git@github.com:jsilva-pt/medium-reusable-vue-components.git
git push -u origin master
Publishing JTableRow component with Lerna

And we are done! Our first reusable component is published and available to be used by everyone thought npm registry.

JTableRow component on npm registry

This component, by itself, is not very useful, so let's create another one that will use it as a dependency.

Creating our second component: JTable

{
"name": "@jsilva-pt/j-table",
"version": "0.0.0",
"publishConfig": {
"access": "public"
}
}
lerna add @jsilva-pt/j-table-row --scope=@jsilva-pt/j-table
Storybook displaying JTable and JTableRow stories
git add .
git commit -m "JTable component"
git push
JTable and JTableRow components on npm registry

Our final structure is:

├── .storybook
│ ├── addons.js
│ └── config.js
├── components
│ ├── JTable
│ │ ├── JTable.stories.js
│ │ ├── JTable.vue
│ │ └── package.json
│ └── JTableRow
│ ├── JTableRow.stories.js
│ ├── JTableRow.vue
│ └── package.json
├── .gitignore
├── lerna.json
├── package.json
└── package-lock.json

Improve JTable

Our JTable is already useful but it would be nice to have a header like every normal table has, so let's do it.

Storybook displaying JTable and JTableRow stories
git add .
git commit -m "Added header prop to JTable"
git push
Publishing a new version of JTable with Lerna

And we are done! A new version is available.

JTable and JTableRow components on npm registry

Moving from npm to GitHub Package Registry

Our project is ready to publish packages to npm but GitHub Package Registry just arrived and we decided we want to keep our packages close to the code. What do we need to do?

{
...
"repository" : {
"type" : "git",
"url": "ssh://git@github.com:jsilva-pt/medium-reusable-vue-components.git"
}
}
{
...
"command": {
"publish": {
"registry": "https://npm.pkg.github.com"
}
}
}
$ npm login --registry=https://npm.pkg.github.com
> Username: USERNAME
> Password: TOKEN
> Email: PUBLIC EMAIL ADDRESS
https://github.com/jsilva-pt/medium-reusable-vue-components/packages

How can we use these components in other projects?

Independently of using npm or GitHub as a package registry, just install them like any other dependency, through npm or yarn.

# install
npm install @jsilva-pt/j-table
# importing JTable automatically will bring JTableRow
import JTable from '@jsilva-pt/j-table/JTable'

Final Notes:

Conclusion

With Lerna, we can manage several components in the same repository instead of creating one for each, which would become really difficult to maintain.

Storybook, for me, is essential in every project, even when components are not being created in a separated repository.

npm don’t really need a presentation, it is the “world’s largest software registry” that allow us to easily share components between projects.

GitHub Package Registry is a new registry that will allow us to keep the packages close to the code, which in my opinion is a big advantage.

Currently, it is very easy to create reusable components. Tools like Lerna, Storybook, npm and GitHub really help to isolate our components from the application, resulting in more focus, quality and development speed.


Thanks for reading. You can learn more about me on Medium, Github and Linkedin.

Vue.js Developers

Helping web professionals up their skill and knowledge of Vue.js

Thanks to João Teixeira

José Silva

Written by

Lead Frontend Developer @ JumiaPay, Vue.js enthusiastic.

Vue.js Developers

Helping web professionals up their skill and knowledge of Vue.js

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade