How to Publish a React Component as an npm Package Using Vite
2025-05-29
ReactViteLibrarynpm
Introduction
Have you ever built a cool React component and wanted to share it with the world? In this guide, I'll show you how I published @janardanpethani/genmoji — a mood-based emoji generator — as an npm package, starting from a Vite React app.
Why Use a lib
Directory for Components?
Organizing your reusable code in a
lib/
directory keeps your published package clean and focused. It separates library code from demo or app code, makes exports easier to manage, and follows best practices used by many open source libraries.1. Create a Vite React App
npm create vite@latest <your library name> -- --template react-ts
cd <your library name>
npm install
2. Develop Your Component
Write your component as you would in any React app. For example, in lib/components/Chat.tsx:
import React from "react";
type ChatProps = {
// Add any props you want to support
};
export const Chat: React.FC<ChatProps> = (props) => {
return <button>Open Genmoji Chat</button>;
};
Create index.ts in lib/components and export everything from it:
export * from "./Chat";
Create index.ts in lib and export everything from it:
export * from "./components";
3. Configure Vite for Library Build
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import dts from "vite-plugin-dts";
// https://vite.dev/config/
export default defineConfig({
// Configure plugins for Vite
plugins: [
// React plugin for Vite
react(),
// Plugin to generate TypeScript declaration files (d.ts)
dts({
// Specify the path to the build-specific tsconfig file
tsconfigPath: "./tsconfig.build.json",
}),
],
// Build configuration options
build: {
// Library build options
lib: {
// Entry point for the library build
entry: "lib/index.ts",
// Function to determine the output file name based on the format
fileName: (format) => `genmoji.${format}.js`,
// Output formats for the library
formats: ["es", "cjs"],
},
// Rollup specific options (used under the hood by Vite)
rollupOptions: {
// Specify dependencies that should not be bundled into the library
external: ["react", "react-dom"],
// Output options for Rollup
output: {
// Configure global variables for external dependencies
globals: {
react: "React",
"react-dom": "ReactDOM",
},
},
},
// Empty the output directory before building
emptyOutDir: true,
// Do not copy the public directory to the output directory
copyPublicDir: false,
// Minify the output code
minify: true,
},
})
You need to install vite-plugin-dts to generate TypeScript declaration files.
npm install vite-plugin-dts
4. Update package.json
{
...
"type": "module",
"main": "dist/genmoji.cjs.js",
"module": "dist/genmoji.es.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/genmoji.es.js",
"require": "./dist/genmoji.cjs.js"
}
},
"files": [
"dist"
],
"publishConfig": {
"access": "public"
},
...
"dependencies": {
"framer-motion": "^12.15.0"
},
"peerDependencies": {
"react": "^19.1.0",
"react-dom": "^19.1.0"
},
...
}
Why These package.json
Fields?
Field | Description |
---|---|
main/module/types | Ensures compatibility with all bundlers and editors. |
exports | Modern way to define entry points for different environments. |
files | Only publish what's needed (keeps npm package small and clean). |
peerDependencies | Prevents multiple React versions in the consumers app. |
publishConfig | This setting is specifically relevant for scoped packages (packages whose names start with @scope/ , like yours: @janardanpethani/genmoji ). By default, scoped packages are published as private. Setting "access": "public" ensures your package is published publicly, making it available for anyone to install from the npm registry. |
scripts.build | Runs both TypeScript and Vite build for a complete output. |
5. Build Your Package
npm run build
This will output your library to the dist/ folder.
6. Publish to npm
npm login
npm publish --access public
7. Usage Example
import { Chat } from "@janardanpethani/genmoji";
function App() {
return <Chat />;
}
Conclusion
Vite makes it easy to go from a React app to a reusable npm package. You can see the full source and real-world example here: github.com/JanardanPethani/genmoji