Creating a Vue.js Component Library
Although Vue CLI has built-in support for building component libraries, there's still some work in creating one, especially if you want it to be TypeScript and SSR compatible.
To specify what gets included in the library, an entry file should be created which exports each component like that:
export { default as Button } from "./components/Button.vue";
The file is then referenced from the build script:
{
"scripts": {
"build-lib": "vue-cli-service build --target lib src/index.ts"
}
}
The command puts the generated files in the dist
folder. To include them in the NPM package, they must be declared in the package.json
file:
{
"files": ["dist/*"],
"main": "./dist/vue-component.umd.js"
}
For testing purposes, you can create a local NPM package with an extended build script:
{
"scripts": {
"build-lib": "vue-cli-service build --target lib src/index.ts && npm pack && shx mv ./vue-component-0.1.0.tgz .."
}
}
I'm using shx for moving the generated package file.
The package can then be installed in another Vue.js project:
npm i ../vue-component-0.1.0.tgz
That's enough to import the components in a .vue
file:
import { Button } from "vue-component";
In a JavaScript file, this will just work. But the TypeScript compiler will complain:
Could not find a declaration file for module 'vue-component'.
This can be fixed by creating a placeholder type declaration file vue-component/index.d.ts
somewhere in the project, e.g. in the types
folder:
declare module "vue-component";
The file must be referenced in tsconfig.json
, either by adding the root folder to the typeRoots
setting:
{
"typeRoots": ["./node_modules/@types", "types"]
}
Or by adding the folder itself to the types
setting:
{
"types": ["@types/node", "@nuxt/types", "types/vue-component"]
}
However, this will only prevent the error. It won't provide the types.
A better solution is to include the type definitions in the component library. This can be done in a types/index.d.ts
file:
import { VueConstructor } from "vue";
export const Button: VueConstructor;
It's enough to just declare them as components, as Vue.js can't take advantage of more type information. Other Vue.js component libraries do that as well.
The file must also be declared in package.json
:
{
"files": ["dist/*", "types/*"],
"types": "./types/index.d.ts"
}
With this change in the component library, the TypeScript error will go away even without the placeholder types in the application code.
The component library includes the styles in a separate CSS file. This means that it must be included globally in the consuming project. In NuxtJS, this can be done in nuxt.config.js
:
export default {
// ...
css: ["node_modules/vue-component/dist/vue-component.css"],
// ...
};
Component libraries typically solve this with a module.
Vue CLI allows you to inline the styles in the generated JavaScript file by including a vue.config.js
file in the component project:
module.exports = {
css: {
extract: false,
},
};
However, this approach doesn't work with server-side rendering (SSR). The project will still build, but will fail at runtime:
document is not defined
You can find a working sample in my repository on GitHub. It consists of a component library and a NuxtJS application using it.
Even when using Vue CLI to create a component library, you still need to manually edit the package.json
file to configure and generate the NPM package. To make the components work with TypeScript, you should also include the type definitions in the package.