Anthony Gore I'm Anthony Gore, a frontend web developer from Sydney, Australia. I curate the weekly Vue.js Developers Newsletter and blog frequently on interesting web development topics.

Building a Vue 3 component library

6 min read 1741

Vue Logo Over a Bookshelf

Component libraries are a great way to collect common UI solutions into a package that you can reuse across your own projects or share with the Vue open-source community.

In this guide, you’ll learn the key steps to creating a Vue 3 component library, including:

  • Setting up a component library with Vue 3
  • Creating a plugin to install the component library in a project
  • Publishing the library on npm
  • Creating docs with VuePress
  • Publishing docs on GitHub

Let’s get started!

Basic setup

To create our component library, we will not use the normal scaffolding process of Vue.js projects, i.e., Vue CLI. We’ll instead create a project folder from scratch where we’ll initialize npm and Git.

$ mkdir vue3-component-library
$ cd vue3-component-library
$ npm init -y
$ git init
$ touch .gitignore
$ echo 'node_modules' >> .gitignore

By the end of this tutorial, we’ll have a folder structure that looks like this:

.gitignore
package.json
rollup.config.js
dist/
  library.mjs
  library.js
docs/
  .vuepress
  components
node_modules/
src/
  InputText.vue
  InputTextarea.vue

Creating components in Vue

A component library will, of course, need some components. Let’s create an src folder and add the following two simple Vue 3 components:

Note that I’ve intentionally kept these components trivial to ensure this article stays focused on publishing components. In reality, your components would be far more complex and interesting than these.

src/InputText.vue

<template>
    <input type="text" />
</template>
<script>
export default {
  name: 'InputText'
}
</script>

src/InputTextarea.vue

<template>
    <textarea />
</template>
<script>
export default {
  name: 'InputTextarea'
}
</script>

Creating a plugin

Next, we’re going to create a file that registers all the components we wish to share in our library. We’ll call this components.js and, in it, we simply import the components and then export them in a single object.

src/components.js

import InputText from './InputText.vue'
import InputTextarea from './InputTextarea.vue'

export default { InputTextarea, InputText }

We’re now going to create a Vue 3 plugin that will globally register the components from your library in a consuming project.

At the top of the file, we will import the registered components. Then, in the plugin install method, weโ€™ll iterate the components object and globally register each component on the Vue instance.

src/index.js

import components from'./components'

const plugin = {
  install (Vue) {
    for (const prop in components) {
      if (components.hasOwnProperty(prop)) {
        const component = components[prop]
        Vue.component(component.name, component)
      }
    }
  }
}

export default plugin

Any project using this library will need to have Vue 3 installed, so let’s add that as a peer dependency in package.json so that npm will warn the user if they don’t have it.



{
  "peerDependencies": {
    "vue": "^3.2.21"
  }
}

Building the plugin

We now need to create a build of our library that will be shared in an npm module. To do this, we’re going to use the Rollup bundler in conjunction with the Vue Rollup plugin and Rollup Plugin Peer Deps External. Using these, we’ll be able to easily create an efficient build for multiple environments:

$ npm i -D rollup rollup-plugin-vue rollup-plugin-peer-deps-external

Note the -D flag in the install command. It’s important to install these plugins as dev dependencies to ensure they don’t get installed in the consumer’s project!

Let’s now configure Rollup by creating a rollup.config.js file in the root of the project. To provide sufficient flexibility, you probably want to make a different build for these scenarios:

However, I’ve just shown how to configure the CommonJS and ES module builds below. You can find details for other build types in the Vue Rollup docs.

Note that we’re adding the vue plugin, which compiles our component templates, and the peerDepsExternal plugin, which automatically externalizes peer dependencies (i.e., Vue 3) to ensure they’re not included in your build.

rollup.config.js

import vue from 'rollup-plugin-vue'
import peerDepsExternal from 'rollup-plugin-peer-deps-external'

export default [
  {
    input: 'src/index.js',
    output: [
      {
        format: 'esm',
        file: 'dist/library.mjs'
      },
      {
        format: 'cjs',
        file: 'dist/library.js'
      }
    ],
    plugins: [
      vue(), peerDepsExternal()
    ]
  }
]

To run the build, we’ll create a build script in package.json.

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

Once we run npm run build, you’ll see two new files created, dist/library.mjs and dist/library.js, which are your ES module and CommonJS builds, respectively.

Publishing the plugin

To distribute our component library, we’ll publish it to the npm registry. The first step is to ensure your package.json file has the configuration required for publication. The essential values you need are:

  • name: this must be unique across npm, so perhaps prefix it with @yourname/
  • version: start at 0.0.1 (or wherever you like) and increment every time you update your library. Learn more about semantic versioning here
  • main: this is the “entry file” to access your package. This should point to the CommonJS build file
  • module: Same as main but should point to the ES module build file
  • files: this is a whitelist of files that npm will include in the published package. Because our build files are self-contained, we only need to include the dist
{
  "name": "@yourname/yourlibrary",
  "version": "0.0.1",
  "main": "dist/library.js",
  "module": "dist/library.mjs",
  "files": [
    "dist/*"
  ]
}

You may also want to add your component source files to the files whitelist in package.json if you want to allow your components to be imported individually without a plugin.

Now that our package is configured correctly, let’s publish it. Run npm login in the terminal to ensure you’re logged in to npm. Then run npm publish --access=public to publish your package.

$ npm login
$ npm publish --access=public

Once that completes, we can check the npm registry to see if we can find our published package. Run npm view @yourname/yourlibrary. If the publish was successful, you’ll see the package info printed in the console.

Using the published Vue 3 library

Now that our component library is publicly published on npm, we can use it in a project just like any other npm module.


More great articles from LogRocket:


Here’s how you’d include your library in a Vue 3 project using ES modules. Once the plugin is installed, you could now reference your components in any Vue template of this project.

import { createApp } from 'vue'
import App from './App.vue'

import plugin from '@yourname/yourlibrary'

createApp(App)
  .use(plugin)
  .mount('#app')

Setting up a docs site

Your library is now usable, but we’re not finished yet! If you’re going to publish a component library, you need to supply documentation so developers know how to use it.

Fortunately, the Vue ecosystem has its own documentation framework that is perfect for the job: VuePress. VuePress allows you to create a simple but good-looking static documentation site with markdown content.

Because our project is Vue 3, we’ll need VuePress v2. This version is still in beta, so we can install it by using the package version [email protected].

$ npm i -D [email protected]

To set up our docs, we’ll first create a directory, docs, and add the file README.md, which VuePress will use as the content for the home page.

Like most docs sites you’ve seen, the home page is a good place to provide a brief introduction to your library, quickstart instructions, etc.

# My Component library

Here's a brief introduction.

### Installation

$ npm install @yourname/yourlibrary

Running the VuePress dev server

We’ll now add two more scripts to package.json, one for running the VuePress dev server, and one for creating a production build.

"scripts": {
  "build": "rollup -c",
  "docs:dev": "vuepress dev docs",
  "docs:build": "vuepress build docs"
},

Let’s run the dev server with the command npm run docs:dev. The first time you run this, VuePress will create a subfolder named docs/.vuepress, where we’ll be adding more config shortly.

Documenting components in Vue 3

To document the two components in the library, let’s create two markdown files in another new subfolder, docs/components. These files are where you will explain the component API, provide usage examples, and whatever else will help the user.

# input-text

`InputText` is a cool component. Here's how to use it...

<template>
  <input-text />
</template>

In addition to regular markdown text and code, VuePress allows for interactive component demos by mounting your component to a Vue instance within the docs page. You can learn more about that here.

When VuePress runs, these markdown files will be published as pages. To make these pages accessible, we’ll add them to the docs sidebar by adding the following theming info to the VuePress config file:

module.exports = {
  themeConfig: {
    sidebar: [
      {
        title: 'Components',
        collapsable: false,
        children: [
          '/components/input-text.md',
          '/components/input-textarea.md'
        ]
      }
    ]
  }
}

Check the browser. Your VuePress site will now look something like this:

VuePress Example

Publishing docs to GitHub Pages

To publish our docs publicly, we can use GitHub Pages, which allows for free static hosting.

Assuming you don’t want a custom domain, your Pages site URL will probably be this:

https://<yourname>.github.io/<yourlibrary>/

The important thing to note is that it will be in a subfolder, not the root directory. For this reason, you need to provide a base config option in the VuePress config so that relative paths work correctly.

docs/.vuepress/config.js

module.exports = {
  ...
  base: '/yourlibrary/'
}

With that done, we’ll now create a deploy script. This script will build the docs, commit the build to the branch gh-pages, and then push the commit to GitHub where it will be published.

If you haven’t already, ensure your component library is published to GitHub (yourname/yourlibrary). Run the script, and your static build will be pushed to the repo.

deploy.sh

#!/usr/bin/env sh

set -e

npm run docs:build
cd docs/.vuepress/dist

git init
git add -A
git commit -m 'deploy'

git push -f [email protected]:yourname/yourlibrary.git master:gh-pages

cd -

To tell GitHub to publish the site, you can turn on the Pages feature in the repo settings. Make sure you select the branch gh-pages and source files from the root directory.

GitHub Pages

Conclusion

In this article, we’ve seen how to create a Vue 3 component library and publish it to npm, as well as publish the docs on GitHub Pages.

With this recipe, you can create libraries that will allow consistency across your own projects or provide some great open-source components to the Vue community.

Experience your Vue apps exactly how a user does

Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If youโ€™re interested in monitoring and tracking Vue mutations for all of your users in production, try LogRocket. https://logrocket.com/signup/

LogRocket is like a DVR for web and mobile apps, recording literally everything that happens in your Vue apps including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.

The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error, and what state the application was in when an issue occurred.

Modernize how you debug your Vue apps - .

Anthony Gore I'm Anthony Gore, a frontend web developer from Sydney, Australia. I curate the weekly Vue.js Developers Newsletter and blog frequently on interesting web development topics.

7 Replies to “Building a Vue 3 component library”

  1. Thanks for sharing, I’ve followed the instructions, but am hitting a “Error: Could not resolve ‘./components’ from src/index.js” when running the `npm run build` command. The ‘/components/’ folder is definitely there.

    If I change the import to “import components from ‘@src/components’ then the build works correctly, however the module then complains, saying “Cannot find module ‘@src/components'” when used as an npm module in a Vue project.

    Any help/advice would be appreciated.

  2. Hello, I am trying to follow your guide. I am encountering an error when trying to build. I have not gotten to publishing yet. It says, “SyntaxError: Cannot use import statement outside a module.” I’ve read some posts suggesting using “type”: “module” but your package.json does not use this. Any advice?

  3. Hi Anthony
    Thanks for sharing. This has helped me a lot on something that I currently working
    But I have a problem, I’m working with script setup and because of that I don’t have access to name, inheritAttrs, etc.
    How can I do the iteration through the list of components exported on index.js file?
    For now I added an extra script block where I set the same, but as you can image this is a hackish solution and would like to use on the final product.
    Thanks

  4. this is a really great source. Thanks for this!
    If I want to import the components individually in my project files (ie. import InputText from ‘my-package’) what do I need to do? I tried to add it to the files white-list but no luck

  5. I don’t suppose you could give a little more detail on the vuepress section? I followed the instructions but all I get running the docs server is the 404 page

Leave a Reply