Skip to content
No description, website, or topics provided.
HTML JavaScript Vue
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.
.vscode Adds WIP Tutorial - part 3 Mar 26, 2019
docs Fixes Mar 26, 2019
packages Fixes #2 Remove useless css Mar 27, 2019
.gitignore Adds WIP Tutorial Mar 26, 2019

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 :)

You can’t perform that action at this time.