Just wanted to write a quick guide (honestly mostly to myself) on how to publish client side packages with
vite's
library modeThe guide here will be focused on
typescript
and react
but I am sure the high level ideas can be used elsewhereCreate a basic vite app
npm create vite@latest
yarn create vite
pnpm create vite
This is the basic way of creating a vite app, would recommend choosing
typescript
and react
hereChange to library mode
We want to change
vite build
to library mode on src/index.ts
touch src/index.ts
Inside
src/index.ts
you should export any modules you want other to useThe really nice part about vite library mode is, you can use
main.tsx
to test your app locally without it being a part of your bundle. That is, vite dev
will work as normalThis will allow your application to work in library mode
//vite.config.ts
import { defineConfig } from "vite";
import path from "path";
import pkg from "./package.json";
// https://vitejs.dev/config/
export default defineConfig({
...,
build: {
lib: {
entry: path.resolve(__dirname, "src/index.ts"),
name: pkg.name,
fileName: (format) => `index.${format}.js`,
},
rollupOptions: {
// make sure to externalize deps that shouldn't be bundled
// into your library
external: ["react"],
output: {
// Provide global variables to use in the UMD build
// for externalized deps
globals: {
react: "react",
},
},
},
},
});
Configure typescript
Emit declaration
Remove
composite
from tsconfig.node.json
- You cannot emit with composite trueAdd the following to
tsconfig.json
"declaration": true,
"emitDeclarationOnly": true,
"outDir": "dist/src",
Bundle your typescript
Huh? Bundle my typescript? Yes, if the typescript is not properly bundle it will not work correctly when users import the package
Add
dts-bundle
as a dev deppnpm add -D dts-bundle
touch dtsBundle.js
import dts from "dts-bundle";
dts.bundle({
name: "PACKAGE_NAME",
main: "dist/src/index.d.ts",
out: "../index.d.ts",
});
This package will automatically bundle your typescript into one nice file
Configure package.json
Add exports
Here we are adding exports for both es modules and umd
Note: here we are ignored
dist/src
cause we are dumping the unbundled typescript there "module": "dist/index.es.js",
"exports": {
".": {
"import": "./dist/index.es.js",
"require": "./dist/index.umd.js"
}
},
"files": [
"dist",
"!dist/src"
],
"main": "dist/index.umd.js",
"typings": "dist/index.d.ts"
Change scripts
We want a custom tsc script here to bundle tsc for us, and we want to run it after
vite build
as vite build
will delete dist
"build": "vite build && yarn tsc",
"tsc": "tsc && node dtsBundle.js",
Your package json should look something like this
{
"name": "name",
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build && yarn tsc",
"tsc": "tsc && node dtsBundle.js",
"preview": "vite preview"
},
"peerDependencies": {
"react": "^18.2.0"
},
"devDependencies": {
"react-dom": "^18.2.0",
"react": "^18.2.0",
"@types/node": "^18.7.23",
"@types/react": "^18.0.17",
"@types/react-dom": "^18.0.6",
"@vitejs/plugin-react": "^2.1.0",
"dts-bundle": "^0.7.3",
"typescript": "^4.6.4",
"vite": "^3.1.0"
},
"module": "dist/index.es.js",
"exports": {
".": {
"import": "./dist/index.es.js",
"require": "./dist/index.umd.js"
}
},
"files": [
"dist",
"!dist/src"
],
"main": "dist/index.umd.js",
"typings": "dist/index.d.ts"
}
That’s it you are done, you can delete unnecessary files and publish your package
Caveats/Notes
How do I style my package?
Styling packages is a bit weird, cause you want the user to be able to customise the look for the most part. The main thing to do here is to use
css-in-js
strategies and expose a lot of theming and styling options. Would recommend twind
here for tailwind usersWould highly highly recommend against spitting out a
.css
file cause that becomes problematic for next
usersYou can also just use the below repo