Frontend Development 17 min read

Step‑by‑Step Guide to Building a Vue 3 Headless UI Component Library with pnpm, Vite, and TypeScript

This article provides a comprehensive, hands‑on tutorial for creating a Vue 3 Headless UI component library, covering project initialization, workspace configuration, ESLint and Git‑hook setup, TypeScript and Vite integration, unit testing with Vitest, documentation with VitePress, playground creation, monorepo building, and publishing to npm.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Step‑by‑Step Guide to Building a Vue 3 Headless UI Component Library with pnpm, Vite, and TypeScript

Introduction

The article introduces the concept of a Headless UI component library and explains that the tutorial will guide readers through building a Vue 3‑based library that can be used in personal or company projects.

1. Project Initialization

Create a project directory and initialize it with pnpm init . Add a pnpm-workspace.yaml file to define the workspace packages, then create the packages/vue folder and install TypeScript and its types:

mkdir my-project
cd my-project
pnpm init
touch pnpm-workspace.yaml
mkdir -p packages/vue
cd packages/vue
pnpm init
pnpm i typescript @types/node -D
npx tsc --init
touch README.md LICENSE

2. ESLint and Basic Git‑Hook Configuration

Install ESLint and the @antfu/eslint-config preset, create eslint.config.js , and add lint scripts to package.json :

pnpm i eslint @antfu/eslint-config -D
# create eslint.config.js with custom rules
# add "lint" and "lint:fix" scripts to package.json

Install simple-git-hooks , lint-staged , and commitlint packages, then configure them in package.json to run on pre‑commit and commit‑msg hooks.

pnpm i simple-git-hooks lint-staged -D
pnpm i @commitlint/config-conventional @commitlint/cli -D
# add hooks configuration and scripts to package.json

3. Comparison of simple‑git‑hooks and husky

Both tools manage Git hooks, but husky offers more flexibility and a richer ecosystem, while simple‑git‑hooks focuses on simplicity and minimal configuration.

4. Core Library Setup (packages/vue)

Initialize the Vue package, install Vue, vue-tsc , and optional utilities like @vueuse/core :

pnpm install vue vue-tsc -D

Configure TypeScript with @tsconfig/node18 and @vue/tsconfig , creating tsconfig.json , tsconfig.app.json , tsconfig.build.json , and tsconfig.node.json to cover development, build, and Vite config compilation.

Vite Configuration

Install Vite, the Vue plugin, and vite-plugin-dts , then create vite.config.ts to define library build options (ESM, CJS, types) and resolve aliases.

import { resolve } from 'node:path'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import dts from 'vite-plugin-dts'

export default defineConfig({
  plugins: [vue(), dts({ tsconfigPath: 'tsconfig.build.json', cleanVueFileName: true })],
  build: { lib: { name: 'yi-ui', entry: resolve(__dirname, 'src/index.ts'), fileName: 'index' } }
})

Source and Build

Create a src folder with a simple TypeScript module, add a build script to package.json , and run pnpm build to generate the dist directory containing ESM, UMD, and type declaration files.

mkdir src
touch src/index.ts
# src/index.ts content
export const a = '1111'
export const b = '2222'
export const fn = () => { console.log('fn') }
export const add = (a: number, b: number) => a + b

# package.json script
"build": "vite build"
pnpm build

5. Unit Testing

Install Vitest, add vitest.config.ts , and create a test file src/index.test.ts that verifies the add function.

pnpm i vitest -D
# vitest.config.ts
import { resolve } from 'node:path'
import { defineConfig } from 'vitest/config'
import Vue from '@vitejs/plugin-vue'

export default defineConfig({ plugins: [Vue()], resolve: { alias: { '@': resolve(__dirname, './src') } } })

# src/index.test.ts
import { describe, expect, test } from 'vitest'
import { add } from './index'

describe('test', () => {
  test('function returns sum', () => {
    expect(add(1, 2)).toBe(3)
  })
})

pnpm test

6. Documentation (docs)

Set up a documentation site with VitePress and Tailwind CSS. Create a docs folder, install dependencies, and add scripts for development, build, and preview.

mkdir docs
cd docs
pnpm init
pnpm i vitepress tailwindcss -D
# package.json scripts
"docs:dev": "vitepress dev",
"docs:build": "vitepress build",
"docs:preview": "vitepress preview"

7. Playground

Create a playground folder, initialize a Vue 3 project (or a Nuxt project), and link the local @yi-ui/vue package via a workspace reference.

mkdir playground
cd playground
pnpm create vite   # choose Vue 3 or Nuxt
# package.json dependency
"@yi-ui/vue": "link:../../packages/vue"

8. Monorepo Build

Add a root‑level build script that clears previous builds with rimraf and runs the build command for all packages using pnpm workspaces.

pnpm i rimraf -D
# root package.json scripts
"clear": "rimraf packages/**/dist",
"build": "pnpm run clear && pnpm -r --filter=./packages/** run build"
pnpm build

9. Publishing & Release Process

Log in to npm, configure each package for public publishing, and use changesets to manage versioning and changelogs in a monorepo.

npm login
pnpm i @changesets/cli -D
pnpm changeset init
# .changeset/config.json defines public access, baseBranch, etc.
# release scripts in root package.json
"changeset": "changeset",
"vp": "changeset version",
"release": "pnpm build && pnpm release:only",
"release:only": "changeset publish"

# typical release flow
pnpm changeset   # create a changeset
pnpm vp          # bump versions and generate changelogs
pnpm release     # build and publish to npm

Conclusion

The guide walks through the essential steps to scaffold, configure, test, document, and publish a Vue 3 Headless UI component library, providing a solid foundation for further customization and feature development.

TypeScripttestingMonorepoVueComponent Librarydocumentationviteheadless UI
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.