16 visitors

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?

FieldDescription
main/module/typesEnsures compatibility with all bundlers and editors.
exportsModern way to define entry points for different environments.
filesOnly publish what's needed (keeps npm package small and clean).
peerDependenciesPrevents multiple React versions in the consumers app.
publishConfigThis 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.buildRuns both TypeScript and Vite build for a complete output.
For more, see npm package.json docs.

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

References