Creating your own Vue Component Library
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:
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:
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 theexternal
field in Rollup. Any module inexternal
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 innode_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 intsconfig.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: