---
title: Turning Vue components into reusable npm packages
description: >-
  How can you reuse Vue.js components across your projects? 

  Here’s how we automated our process to bundle, test, document and publish Vue
  components.
language: English
url: 'https://voorhoe.de/en/blog/turning-vue-components-into-reusable-npm-packages/'
---

Blog

# Turning Vue components into reusable npm packages

By [Sjoerd](/en/team/sjoerd.md)

22 April 2020

* [Bundling a Vue component](#bundling-a-vue-component)
* [Adding documentation with VuePress](#adding-documentation-with-vuepress)
* [Publishing to npm](#publishing-to-npm)
* [Bonus: publish to GitHub](#bonus-publish-to-github)
* [Resources](#resources)
* [More about Vue](#more-about-vue)

A guide to publishing your Vue components

When working on multiple projects with Vue.js as your framework of choice, you often find yourself writing the same components over and over again. Is there a better way?

![Workflow using Vue, Vuepress, GitHub and npm](https://www.datocms-assets.com/6524/1587476645-blogpost-diagram.png)

Workflow using Vue, Vuepress, GitHub and npm

A way to solve this is to separate them from your projects and publish them to npm. This way you can make your components reusable, open source and available across teams. Here’s how we automated our process to bundle, test, document and publish Vue components.

> Do you want to become a Vue master? During our two day hands-on workshop we’ll teach you everything you need to know to build large performant web apps with Vue.
>
> [Join our Vue masterclass](https://www.voorhoede.nl/vuemaster)

## Bundling a Vue component

One way to share your component is by directly publishing the `.vue` file to npm. This will work in cases where you can import `.vue` files in your project, but what if someone wanted to use it directly in the browser? That will not work. To prepare for these types of situations you should bundle them into `.js` files.

As a base for the project we’ll need a `package.json` file (run `npm init` to create one) and a `src` folder.  The structure of it will roughly look like this:

```
src/
  index.js
  your-vue-component.vue
  your-vue-component.test.js
package.json
rollup.config.js
```

For unit testing we’re using [vue-test-utils](https://vue-test-utils.vuejs.org/), the official test library for Vue components, combined with [Jest](https://jestjs.io/). Tests are located in `.test.js` files and are used to make sure components work the way they are supposed to work. It improves maintainability, since it will indicate if you broke something when making changes to your code.

A commonly used tool for bundling Vue components is Rollup, a JavaScript module bundler. It compiles the Vue components into different formats to use in ES, common JS or directly in the browser. The base configuration for Rollup in this situation looks like this:

```
// import necessary dependencies
import vue from 'rollup-plugin-vue'
import buble from 'rollup-plugin-buble'
import commonjs from 'rollup-plugin-commonjs'

export default {
  input: 'src/index.js', // entry file for our components
  plugins: {
    preVue: [
      replace({
        'process.env.NODE_ENV': JSON.stringify('production'),
      }),
      commonjs(), // add support for CommonJS modules
    ],
    vue: {
      css: true, // include CSS in the output
      template: {
        isProduction: true,
      },
    },
    postVue: [
      buble(), // use buble to transpile ES2015
    ],
  },
}
```

This base configuration can now be extended for the module types we want to export. For a full example, see the `rollup.config.js` of our vue-lazy-load component.

In our `package.json` we can specify where we want our transpiled bundles to end up:

```
{
  "name": "your-component-name",
  "main": "dist/your-component-name.ssr.js",
  "module": "dist/your-component-name.esm.js",
  "unpkg": "dist/your-component-name.min.js",
  "style": "dist/your-component-name.css",
  // ...
}
```

All the different fields serve their purpose:

* Modern bundlers will use the `module` build
* Legacy bundlers and Node.js will use the `main` build
* The `unpkg` bundle can be used directly in the browser via [UNPKG](https://unpkg.com/)

You’ll also notice a `style` property. Typically, you’ll want to separate your styles from the JS bundle. This gives freedom for users to apply their styles or use their loaders/plugins of choice to process them.

This is an example of how you would import the component and styles in your application:

```
import YourComponentName from 'your-component-name'
import 'your-component-name/dist/your-component-name.css'
```

## Adding documentation with VuePress

Writing your documentation in your `readme.md` is a convenient way to document components. But what if you want to create an interactive demo or add a nice theme to it? Markdown processing tools like [VuePress](https://vuepress.vuejs.org/) or [Docz](https://www.docz.site/) are a perfect option for that. These tools convert markdown files into a static site to host as your documentation. In our case, we’re using VuePress, since it suits the Vue ecosystem.

![Component documented with VuePress](https://www.datocms-assets.com/6524/1587482520-docs-1.png)

Component documented with VuePress

To create our documentation we need a folder to build it from. The structure for this folder should look something like this:

```
docs/
  readme.md
  .vuepress/
    config.js
```

The `readme.md` file is used for generating the HTML. There are plugins available for VuePress to extend its functionality. In our case we’re using [plugin-register-components](https://vuepress.vuejs.org/plugin/official/plugin-register-components.html) combined with [vuepress-plugin-demo-code](https://github.com/BuptStEve/vuepress-plugin-demo-code) to be able to demonstrate our components:

```
const pkg = require('../../package.json')

module.exports = {
  title: `${pkg.name} (${pkg.version})`,
  description: pkg.description,
  dest: `www`,
  plugins: [
    ['@vuepress/register-components', {
        componentsDir: `${__dirname}/../../src/`
    }],
    'demo-code'
  ]
}
```

### **Documenting Vue props, events and slots**

To give the user information about our component, we should document props, events and slots. Documenting those is not fun to do by hand and seems like something we can automate. A tool named [@vuedoc/md](https://gitlab.com/vuedoc/md) can do just this. It scans through the Vue component looking for props, slots, and events. It converts these into a markdown structure and will append it to a specified markdown file.

![Output of generated docs](https://www.datocms-assets.com/6524/1587482495-docs-2.png)

Output of generated docs

Below are the scripts to generate our documentation. First, we extract the props, events, and slots and append to our markdown file. After that, we generate our static site using VuePress.

```
"scripts": {
  // ...
  "docs": "run-s docs:*",
  "docs:api": "vuedoc.md src/your-component-name.vue --section 'API' --output docs/v1/readme.md --ignore-data --ignore-methods --ignore-computed",
  "docs:vuepress": "vuepress build docs",
  // ...
},
```

The generated static site can now be hosted wherever you want, for example on [Netlify](https://www.netlify.com/) or [GitHub pages](https://pages.github.com/).

## Publishing to npm

The bundles created by Rollup are ready to be published to npm. After creating a build, use `npm publish`.

To make things easier, we can automate this by using a [GitHub action](https://github.com/features/actions). This will automatically create a new version of your npm package when a new tag is pushed. We do this by adding a `.github/workflows/npm.yml` file:

```
on:
  push:
    tags: v*.*.*
jobs:
  npm:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@master
    - uses: actions/setup-node@v1
      with:
        node-version: '10.x'
        registry-url: 'https://registry.npmjs.org'
    - name: Build package
      run: npm run build
    - name: Publish tag to npm
      if: contains(github.ref, 'tags')
      run: npm publish
      env:
        NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
```

Here’s the [complete workflow for one of our Vue components](https://github.com/voorhoede/vue-dato-image/blob/master/.github/workflows/npm.yml).

## Bonus: publish to GitHub

It’s also possible to publish your package to [GitHub packages](https://github.com/features/packages) registry

```
- uses: actions/setup-node@v1
  with:
    registry-url: 'https://npm.pkg.github.com'

- name: Publish tag to GPR
  run: npm publish
  env:
    NODE_AUTH_TOKEN: ${{ secrets.GITHUB_AUTH_TOKEN }}k
```

![Package on GitHub](https://www.datocms-assets.com/6524/1587482480-github.png)

Package on GitHub

You now have an npm package with (partly) automated documentation and publishing to npm on every new tag! To see an example of how this is implemented in real-life, take a look at the repo of [@voorhoede/vue-lazy-load](https://github.com/voorhoede/vue-lazy-load). We have created a couple of components this way, those can be found on our [open-source page](https://voorhoede.github.io/#front-end-components). If you publish a Vue component yourself, please do share it with us!

## Resources

* [Packaging Vue Components for npm](https://vuejs.org/v2/cookbook/packaging-sfc-for-npm.html)
* [Contributing packages to the registry](https://docs.npmjs.com/packages-and-modules/contributing-packages-to-the-registry)
* [Publishing Node.js packages](https://help.github.com/en/actions/language-and-framework-guides/publishing-nodejs-packages)

## More about Vue

* [Highlights from Vue.js Nation 2023](https://www.voorhoede.nl/nl/blog/highlights-vue-js-nation-2023/)
* Looking for experienced [Vue.js developers](https://www.voorhoede.nl/en/services/experienced-vuejs-developers/)? We can help!

[← All blog posts](/en/blog.md)

## Also in love with the web?

For us, that’s about technology and user experience. Fast, available for all, enjoyable to use. And fun to build. This is how our team bands together, adhering to the same values, to make sure we achieve a solid result for clients both large and small. Does that fit you?

[Join our team](/en/jobs.md)

[Return to top](#top)
