Publish client side packages in minutes not hours with vite
Just wanted to write a quick guide (honestly mostly to myself) on how to publish client side packages with vite's library mode
The guide here will be focused on typescript and react but I am sure the high level ideas can be used elsewhere

Create 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 here

Change 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 use
The 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 normal
This 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 true
Add 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 dep
pnpm 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


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 users
Would highly highly recommend against spitting out a .css file cause that becomes problematic for next users
You can also just use the below repo