Skip to content
Switch branches/tags

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time

Monorepo solution for multiple VueJs Apps and a shared component library.

This is still a "work in progress" tutorial. It actually already works, but I still have to refine some steps. Feel free to help me / send me your suggestions.

Getting Started

Install Lerna

Let's start by installing Lerna globally with npm:

$ npm install --global lerna

Next we have to create a new git repository:

$ git init component-library-monorepo && cd component-library-monorepo 

And then, following Lerna's official documentation, will turn it into a Lerna repo:

lerna init

The repository should look lihe this:


If you'd like to learn something more about this process, you can check the official Lerna documentation.

Install Storybook

Let's start by installing Lerna globally with npm:

$ npm install @storybook/vue --save-dev

Add peer dependencies

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

Add a npm script

  "scripts": {
    "storybook": "start-storybook"

For a basic Storybook configuration, the only thing you need to do is tell Storybook where to find stories.

To do that, create a file at .storybook/config.js with the following content:

import { configure } from '@storybook/vue';

const req = require.context('../packages', true, /.stories.js$/);
function loadStories() {
  req.keys().forEach(filename => req(filename));
configure(loadStories, module);

Add the first component to the component library

We create in the root a packages/index.stories.js file and write our first story:

import Vue from 'vue';
import { storiesOf } from '@storybook/vue';
import MyButton from './Button/src/Button.vue';

storiesOf('Button', module)
  .add('as a component', () => ({
    components: { MyButton },
    template: '<my-button>with text</my-button>'
  .add('with emoji', () => ({
    components: { MyButton },
    template: '<my-button>😀 😎 👍 💯</my-button>'
  .add('with text', () => ({
    components: { MyButton },
    template: '<my-button :rounded="true">rounded</my-button>'

Now we create the real "Button" component:

  <button type="button"><slot /></button>

export default {
  name: 'MyButton',

The index.js

import MyButton from './Button.vue';
export default MyButton;

And the package.json:

  "name": "@mylibrary/my-button",
  "version": "0.2.0",
  "description": "Just a simple button component",
  "main": "dist/index.js",
  "module": "src/index.js",
  "scripts": {
    "transpile": "vue-cli-service build --target lib ./src/index.js"

Start Storybook

Now you are ready to start Storybook and play with your first component:

$ npm run storybook

And you should see it running here:


Create a VueJs App


To install the Vue CLI, use this command:

$ npm install -g @vue/cli
$ npm install --save-dev @vue/cli-service

Create a new project

To create a new project, run:

$ cd packages && vue create my-app

And please choose the easiest option:

> default (babel, eslint)

In this tutorial we don't want to build the best VueJs App possible, but just show how to share a component library between VueJs Apps.

Add eslint configuration

Create ./packages/my-app/.eslintrc.js

module.exports = {
    "env": {
        "browser": true,
        "es6": true
    "extends": [
    "globals": {
        "Atomics": "readonly",
        "SharedArrayBuffer": "readonly"
    "parserOptions": {
        "ecmaVersion": 2018,
        "sourceType": "module"
    "plugins": [
    "rules": {

Run the App

Let's run our new app:

$ cd my-app && npm run serve

And now you should see here your app, up&running:


Using Lerna to link dependencies

Add the following dependency to your packages/my-app/package.json:

  "dependencies": {
    "@mylibrary/my-button": "*"

Fix eslint

const path = require('path');
module.exports = {
  chainWebpack: config => {
      .tap(options => {
        options.configFile = path.resolve(__dirname, ".eslintrc.js");
        return options;
  css: {
    loaderOptions: {
      postcss: {

And now we can "bootstrap" the packages in the current Lerna repo, install all of their dependencies and links any cross-dependencies:

In the root:

$ lerna bootstrap

Update the Vue App

Change the content of ./packages/my-app/src/main.js:

import Vue from 'vue'
import App from './App.vue'
import MyButton from '@mylibrary/my-button';

Vue.config.productionTip = false
Vue.component('my-button', MyButton);
new Vue({
  render: h => h(App),

and change the content of our HelloWorld component (./packages/my-app/src/components/HelloWorld.vue):

  <div class="hello">
    <h1>{{ msg }}</h1>
    <my-button>It Works!</my-button>

export default {
  name: 'HelloWorld',
  props: {
    msg: String

We now transpile our components:

$ lerna run transpile

run again..

$ cd packages/my-app && npm run serve

Go to http://localhost:8080 and you should se the button in the middle of the HelloWorld page :)


No description, website, or topics provided.



No packages published