up-to-date 2025
cd my-project-folder
# Scaffold directly into the folder
npm create vite@latest . -- --template vue-ts
# Install dependencies
npm install
# Include node types which are needed whenever you need to import from node
npm i --save-dev @types/node
## Update `package.json`
- Change name to `@username/projectname` to publish into your user scope.
- Remove `"private": true,` to allow publishing
- Specify `vue` as a peer dependency.
```json
"peerDependencies": {
"vue": "^3.0.0"
},- Add
distand optionallysrctofilesto be published.
"files": [
"dist",
"src"
],- Add
author,license,repository,keywordsfor package information.
"author": "your-user-name",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/your-user-name/your-repo-name.git"
},
"keywords": [
"vue"
],- Add
exportsfields to indicate how the package can be imported. Also mentions how types can be loaded.
"exports": {
".": {
"types": "./dist/types/index.d.ts",
"import": "./dist/your-component-name.es.js",
"require": "./dist/your-component-name.umd.js"
},
"./style.css": "./dist/your-component-name.css"
}Add the build config. An important setting includes emptyOutDir, which means do not clean the dist folder when building JS files by Vite. This is because we let vue-tsc emit the type declarations and let Vite emit the JS files.
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from "path";
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
build: {
lib: {
entry: path.resolve(__dirname, "src/index.ts"),
name: "YourComponentName",
fileName: (format) => `your-component-name.${format}.js`,
},
rollupOptions: {
external: ["vue"],
output: {
exports: "named",
globals: {
vue: "Vue",
},
},
},
emptyOutDir: false, // to retain the types folder generated by tsc
},
})Since Vite will not help to empty out the folder, we need to update package.json to help do that.
"build": "rm -rf dist && vue-tsc -b && vite build",Include an entry point, to allow it to be both used globally or individually.
import type { App } from "vue";
import HelloWorld from "./components/HelloWorld.vue";
export { HelloWorld };
export default {
install(app: App) {
app.component("HelloWorld", HelloWorld);
},
};The tsconfig.app.json is the one that we intend to use for building our component. The tsconfig.node.json is for any other development TypeScript files.
Set "noEmit": false so that type declarations can be emitted. This is set to true by default in the base config that we extend from. Other important settings include "emitDeclarationOnly": true to only emit type files.
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
/* Type Declaration */
"noEmit": false, // This is important to generate type declaration files
"emitDeclarationOnly": true, // This prevents the generation of JS files, which is left to vite
"declaration": true,
"declarationMap": true,
"outDir": "dist/types",
"rootDir": "src"
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
"exclude": ["node_modules", "dist"]
}Remove the default files that are not needed, e.g., App.vue, main.ts, style.css, src/assets/, index.html.
# Update version in package.json as needed
npm run build
npm publish --access publicMake sure to import the style.css if your component contains css styles.
import { YourComponentName } from '@user-name/your-component-name'
import '@user-name/your-component-name/style.css'