Special header.hbs partial to generate the
tag

Creating your own Vue Component Library

Vue Aug 11, 2020

Previously, I wrote an article outlining the steps required to get a React component library up and running. You can read it here: https://blog.harveydelaney.com/creating-your-own-react-component-library

It's had some pretty good feedback so I decided to branch out and create another component library template for Vue! I've not had much experience with Vue before this article and was a great learning experience.

Vue Component Library Code

If think you'll find it helpful to have the code up while you read through the article, have a look at the following GitHub repository I created:

HarveyD/vue-component-library
A project skeleton to get your very own Vue component library up and running using Rollup, Typescript + Vue - HarveyD/vue-component-library

Component Library Overview

We'll be using the following to create the library:

  • Vue (obviously)
  • TypeScript
  • Rollup

I'll also be showing you how to publish your component library to NPM and how to consume it.

Creating the Component Library

Project Setup

Initialise NPM - npm init to generate a basic package.json file.

Initialise Git - git init.

We're going to have the following structure for our component library:

.gitignore
package.json
rollup.config.js
tsconfig.json
src/
  SampleComponent/
    SampleComponent.vue
  index.ts
  shims.tsx.d.ts
  shims.vue.d.ts

We will be outputting our bundled + compiled files into the lib directory.

Vue

Since we're building a Vue component library, we need to install Vue and it's required dependencies to get it working. Run:

npm i -D vue vue-class-component vue-property-decorator @vue/compiler-sfc vue-template-compiler

You'll notice we're installing these as devDependencies which won't be included when we install the component library in another project. We should expect the project that installs the component library already has Vue installed.

To make sure this is the case, we can specify Vue dependencies as peerDependencies. To do this, open package.json and add:

...
"peerDependencies": {
    "vue": "^2.6.11",
    "vue-class-component": "^7.2.3",
    "vue-property-decorator": "^8.4.2"
  },
  ...

When the component library is installed as a dependency, but the consuming project does not have the peerDependencies, a warning will be output.

Vue Component

What use is a component library without any components? Let's add our first Vue component. Within src/SampleComponent/SampleComponent.vue add:

<template>
  <div class="sample-component-container">
    <h2>{{ headingText }}</h2>
    <h3>{{ bodyText }}</h3>
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";

@Component
export default class SampleComponent extends Vue {
  @Prop() private headingText: string = "";
  @Prop() private bodyText: string = "";
}
</script>

<style>
.sample-component-container {
  padding: 40px;
  background-color: black;
  color: white;
}
</style>
src/SampleComponent/SampleComponent.vue

Next, let's expose this component in the root file of our library src/index.ts:

import SampleComponent from "./SampleComponent/SampleComponent.vue";

export { SampleComponent };

TypeScript

Install TypeScript by running:

npm i -D typescript

After installing TypeScript, create a tsconfig.json file. Within tsconfig.json and add:

{
  "compilerOptions": {
    "declarationDir": "lib",
    "target": "es5",
    "module": "es2015",
    "sourceMap": true,
    "declaration": true,
    "importHelpers": true,
    "strict": true,
    "experimentalDecorators": true
  },
  "include": ["src/**/*.ts", "src/**/*.vue"]
}

To get Vue types working with TypeScript, we need to introduce some shims. In src/shims-tsx.d.ts add:

import Vue, { VNode } from 'vue'

declare global {
  namespace JSX {
    // tslint:disable no-empty-interface
    interface Element extends VNode {}
    // tslint:disable no-empty-interface
    interface ElementClass extends Vue {}
    interface IntrinsicElements {
      [elem: string]: any
    }
  }
}

And in src/shims-vue.d.ts add:

declare module '*.vue' {
  import Vue from 'vue'
  export default Vue
}

Rollup

First, add Rollup and some additional Rollup plugins that'll help us get our component library transpiled and bundled:

npm i -D rollup rollup-plugin-peer-deps-external @rollup/plugin-node-resolve @rollup/plugin-commonjs rollup-plugin-typescript2 rollup-plugin-vue

In rollup.config.js add:

import peerDepsExternal from "rollup-plugin-peer-deps-external";
import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import typescript from "rollup-plugin-typescript2";
import vue from "rollup-plugin-vue";

import packageJson from "./package.json";

export default {
  input: "src/index.ts",
  output: [
    {
      format: "cjs",
      file: packageJson.main,
      sourcemap: true
    },
    {
      format: "esm",
      file: packageJson.module,
      sourcemap: true
    }
  ],
  plugins: [peerDepsExternal(), resolve(), commonjs(), typescript(), vue()]
};

Let's go through this config file and understand what it's doing.

Input

This points to src/index.ts. Rollup will build up a dependency graph from this entry point and then bundle all the components that are imported/exported.

Output

This is an array with two config objects. These two configs tell Rollup to output two bundles in two different JavaScript module formats:

  • CommonJS - CJS
  • ES Modules - ESM

IMPORTANT: You'll notice in output, values from package.json are used. In package.json, add:

...
  "main": "lib/index.js",
  "module": "lib/index.esm.js",
...

This tells packages that install our component library where the entry point of our component library is. We re-use these values in rollup.config.js to instruct Rollup where to output our bundled file.

Plugins

  • peerDepsExternal - if you remember earlier, we specified some peerDependencies. This plugin will take these dependencies and assign them to the external field in Rollup. Any module in external is not added to the Rollup bundle. This will reduce our bundle size as we already know these dependencies will be present in any project that consumes our library.
  • resolve (@rollup/plugin-node-resolve) - efficiently bundles third party dependencies we've installed and use in node_modules
  • commonjs (@rollup/plugin-commonjs - enables transpilation into CommonJS (CJS) format
  • typescript (rollup-plugin-typescript2) - transpiles our TypeScript code into JavaScript. This plugin will use all the settings we have set in tsconfig.json
  • vue (rollup-plugin-vue) - allows us to create our Vue components as Single-File Components (SFCs) and still have Rollup bundle them as expected

Running Rollup

Now we need a way to be able to instruct Rollup to do its thing. In package.json, add:

...
  "scripts": {
    "build": "rollup -c"
  },
  ...

The -c flag tells Rollup to use the rollup.config.js file we've created.

Try running it by running npm run build, you should see:

Then in /lib you should something like:

If this is true, your component library has successfully been configured. It's now time to use it!

Note: it's probably worth having the following .gitignore:

node_modules
lib

Final NPM Config

As a reference, here's what your final package.json should look like:

{
  "name": "vue-component-library",
  "main": "lib/index.js",
  "module": "lib/index.esm.js",
  "scripts": {
    "build": "rollup -c"
  },
  "peerDependencies": {
    "vue": "^2.6.11",
    "vue-class-component": "^7.2.3",
    "vue-property-decorator": "^8.4.2"
  },
  "devDependencies": {
    "vue": "^2.6.11",
    "vue-class-component": "^7.2.3",
    "vue-property-decorator": "^8.4.2",
    "@rollup/plugin-commonjs": "^14.0.0",
    "@rollup/plugin-node-resolve": "^8.4.0",
    "@vue/compiler-sfc": "^3.0.0-rc.5",
    "rollup": "^2.23.1",
    "rollup-plugin-peer-deps-external": "^2.2.3",
    "rollup-plugin-typescript2": "^0.27.2",
    "rollup-plugin-vue": "^5.1.6",
    "typescript": "^3.9.7",
    "vue-template-compiler": "^2.6.11"
  }
}

Publishing the Component Library

Now, it's time to publish our component library. We can either publish our library on public NPM registry, or we can find a self-hosted private NPM registry alternative like Verdaccio. After you've made your choice and configured NPM to point to the registry, run:

npm publish

NPM will then read files to see what files it should include when publishing. In our case it's lib, so it'll publish the bundled/transpiled code output by Rollup.

You should see something like:

For example, I've published a version of this Vue component library at: https://www.npmjs.com/package/harvey-vue-component-library

Note: You have to make sure that the files are available in lib before publishing.

Using the Component Library

Locally

We don't have to publish the component library to an NPM registry before installing our component library and test out our components.

Let's say you had a React project on your local machine called harvey-test-app. In harvey-test-app run (making sure the path is correct to your component library):

npm i -D ../vue-component-library

This creates vue-component-library as a symlinked dependency. Read more at: https://docs.npmjs.com/cli/link

From NPM Registry

Let's say your component library is available at: https://www.npmjs.com/package/harvey-vue-component-library, you would install it in another Vue project by running:

npm i -D harvey-vue-component-library

Using Components

Make sure that you register your Vue component. Then you can simple import and use components in another project like:

<template>
  <div id="app">
    <h1> Hello I'm consuming the component library </h1>
    <SampleComponent headingText="This is a test component" bodyText="Made with love by Harvey"/>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import { SampleComponent } from "vue-component-library";

@Component({
  components: {
    SampleComponent
  }
})
export default class App extends Vue {}
</script>

Here's a Code Sandpit with an example:

GitHub Repository

Again, here's the GitHub repo I created with everything in this article:

HarveyD/vue-component-library
A project skeleton to get your very own Vue component library up and running using Rollup, Typescript + Vue - HarveyD/vue-component-library

Harvey Delaney

Front End Engineer II at Amazon Web Services

Exclusive Usenet provider deals

Harvey's essential software engineering books

1

The Pragmatic Programmer: From Journeyman to Master

2

Clean Code: A Handbook of Agile Software Craftsmanship

3

Code Complete: A Practical Handbook of Software Construction

4

Design Patterns: Elements of Reusable Object-Oriented Software

Harvey is a participant in the Amazon Services LLC Associates Program, an affiliate advertising program designed to provide a means for sites to earn advertising fees by advertising and linking to amazon.com
Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.