DEV Community

Cover image for How to create and publish your own Vue component libraries (actually works I swear)
khaleel gibran
khaleel gibran

Posted on

How to create and publish your own Vue component libraries (actually works I swear)

This is a lazy (but up to date) tutorial, made while standing on the shoulders of giants, serving as a spiritual successor to these wonderful resources:

With that out of the way, let's get started on this amazing journey that I'm sure you'll enjoy. For context, this tutorial was run on:

  • Node v22.14.0
  • Vue v3.5.17
  • Vite v7.0.4

This is to avoid any discrepancies future versions might cause.

Vite (pronounced "wheat")

We start our day with this magical Vite command:

npm create vite@latest 
Enter fullscreen mode Exit fullscreen mode

Running this command will give you a few options. Make sure to choose Vue and TypeScript. Although TypeScript is optional for a Vue package, it's kinda uncool in 2025 not to have typings for your components. People might get irritated and fork your component library just to add types, and then someone will write another DEV tutorial.

Vite will work its magic and create a bunch of files that should look like this:

Project files


If your project files don't like this, you should be worried.

Install packages

Very simple, but even the pros forget sometimes.

npm install 
Enter fullscreen mode Exit fullscreen mode

Also, install Node typings before your code editor starts screaming:

npm install @types/node --save-dev 
Enter fullscreen mode Exit fullscreen mode

Delete some files

Because this is a lazy tutorial, we're going to get rid of some unnecessary files. If you ever need assets or stylesheets, you can always add them back.

Delete these files


Delete these files.

The component

Now, in our src/components/ folder, we're going to add the component that, well, we want to share with the whole world. For this tutorial, I'll create a fancy-looking button that links to this DEV article.

<!-- src/components/CoolButton.vue --> <script setup lang="ts"> defineProps<{ text?: string; }>(); </script> <template> <button class="cool-button"> <a href="https://dev.to/khalby786/how-to-create-and-publish-your-own-vue-component-libraries-actually-works-i-swear-1o5k" > {{ text }} </a> </button> </template> <style scoped> /* looks absolutely terrible btw */ .cool-button { background-color: #42b983; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; font-size: 16px; } .cool-button a { color: white; text-decoration: none; } .cool-button:hover { background-color: #367c6b; } </style> 
Enter fullscreen mode Exit fullscreen mode

Exporting the component

Rename the src/main.ts to src/index.ts, and delete all the content in the file. Then expose your component like so:

// src/index.ts import CoolButton from './components/CoolButton.vue'; export { CoolButton }; 
Enter fullscreen mode Exit fullscreen mode

Configuring Vite (pronounced "wheat")

To give you a quick summary, Vite lets you build in library mode, which makes it easier to share your amazing component with the whole world on NPM. All that configuration goes in the vite.config.ts file!

// vite.config.ts import { resolve } from "path"; import { defineConfig } from "vite"; import vue from "@vitejs/plugin-vue"; // https://vite.dev/config/ export default defineConfig({ plugins: [vue()], build: { lib: { entry: resolve(__dirname, "src/index.ts"), name: "CoolButton", fileName: "vue-cool-button", }, rollupOptions: { external: ["vue"], output: { globals: { vue: "Vue", }, }, }, }, }); 
Enter fullscreen mode Exit fullscreen mode

⚠️ If you encounter cannot find path or cannot find __dirname errors, make sure to go back to the Install packages step and install @types/node.

Three's a crowd (tsconfig)

You'll notice we have THREE tsconfig.* files. Because I'm lazy, we're going to merge it all into a single file and call it a day.

Delete tsconfig.app.json and tsconfig.node.json, and paste the following code into tsconfig.json.

tsconfig.json

{ "compilerOptions": { /* Types */ "declaration": true, "declarationDir": "dist/types", "emitDeclarationOnly": true, "outDir": "dist/types", /* General */ "target": "ES2020", "useDefineForClassFields": true, "module": "ESNext", "lib": ["ES2020", "DOM", "DOM.Iterable"], "skipLibCheck": true, /* Bundler mode */ "moduleResolution": "node", "allowImportingTsExtensions": true, "isolatedModules": true, "moduleDetection": "force", "jsx": "preserve", "sourceMap": true, "resolveJsonModule": true, "esModuleInterop": true, "baseUrl": ".", "paths": { "@/*": ["src/*"] }, /* Linting */ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true }, "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"], "exclude": ["node_modules", "dist"] } 
Enter fullscreen mode Exit fullscreen mode

Update package.json

We'll need to make changes in our package.json file so that it points to the right built files. Make sure to add in the following lines:

{ ... "files": [ "dist" ], "main": "dist/vue-cool-button.umd.cjs", "module": "dist/vue-cool-button.js", "types": "dist/types/index.d.ts", ... } 
Enter fullscreen mode Exit fullscreen mode

Make sure to also change the build script to the following, so that TypeScript emits type declaration files too.

{ ... "scripts": { ... "build": "vite build && vue-tsc --emitDeclarationOnly", ... } ... } 
Enter fullscreen mode Exit fullscreen mode

Build

npm run build 
Enter fullscreen mode Exit fullscreen mode

This will generate your built files in dist/. It'll include JS files for both UMD and ESM, and any CSS files used.

Testing

A lazy way to taste is to start a fresh Vue project in a different folder. Run npm link in your library's folder, and run npm link vue-cool-button in the test app folder so that the library will be usable in your test app.

Somewhere in your test app, import your component:

<script setup> import { CoolButton } from 'vue-cool-button'; import 'vue-cool-button/vue-cool-button.css'; </script> <template> <CoolButton text="hello world" /> </template> 
Enter fullscreen mode Exit fullscreen mode

Publishing to NPM

# sign into npm npm adduser # publish npm publish 
Enter fullscreen mode Exit fullscreen mode

That's it.

Conclusion

Some core Vue contributor is probably reading this tutorial, writhing in pain at the war crimes we’ve committed along the way. This might not be the most proper way to create and publish a Vue component library, but it’s fast and, most importantly, it works. It’s a lazy tutorial, after all.

I wrote this after making vue-utterances, documenting the problems I faced along the way. I hope this tutorial helps out anyone who's on a similar journey!

Top comments (0)