How to publish a React component package, using NextJS as the compiler
This is a short guide on publishing a React component library package to NPM, using NextJS as the compiler.
Motivation
Your component may be a straight client component with client hooks, and as such doesn't fall in to the React Server Components behaviour.
However, there are two reasons we might still want to use NextJS as our base:
- Server side rendering will still occur for your component, essentially the first render of the component will be rendered server side.
- In the future you might want to include RSCs in your component library, and so it's easier to make it work now.
Example code
All code for this tutorial can be found in this repo.
Step 1: Scaffold your project using create-next-app
Run npx create-next-app
and follow the prompts
Step 2: Create a src/library
folder and create your components
This is where you will create your component(s) for publishing.
Everything else will still be a regular NextJS project. You can use this as a sandbox for development, it won't be packaged for export.
Step 3: Create a src/exports.ts
to export your files
1export * from "./library/MyComponent"
Step 4: Add a tsconfig.build.json
You now have two tsconfigs, one for the regular NextJS application, and one specifically for building the package for publishing.
1{
2 "compilerOptions": {
3 "target": "es5",
4 "lib": [
5 "dom",
6 "dom.iterable",
7 "esnext"
8 ],
9 "allowJs": true,
10 "skipLibCheck": true,
11 "esModuleInterop": true,
12 "allowSyntheticDefaultImports": true,
13 "strict": true,
14 "forceConsistentCasingInFileNames": true,
15 "noFallthroughCasesInSwitch": true,
16 "module": "CommonJS",
17 "moduleResolution": "node",
18 "resolveJsonModule": true,
19 "isolatedModules": true,
20 "jsx": "react-jsx",
21 "outDir": "dist",
22 "declaration": true
23 },
24 "include": [
25 "src/exports.ts",
26 ],
27
28}
29
Step 5: Add build and publish scripts
At this step I also like to prefix the existing start
, build
scripts with next:
- because we need to distinguish between 'build the NextJS application' and 'build the package'.
Also, add the main
and files
properties to your package.json
1{
2 "name": "an-example-react-package-built-with-nextjs-tooling",
3 "version": "0.1.0",
4 "main": "dist/export.js",
5 "files": [
6 "dist"
7 ],
8 "scripts": {
9 "next:dev": "next dev",
10 "next:build": "next build",
11 "next:start": "next start",
12 "next:lint": "next lint",
13 "build": "rm -rf dist && tsc -p tsconfig.build.json",
14 "prepublishOnly": "npm run build"
15 },
Step 6: Mark React a dev + peer dependency, Next a dev dependency
The only items in your dependencies object should be things that are required for your package to run in the context it's installed.
By marking them as dev dependencies they're available to you while developing the package but won't be needlessly installed by the application that uses it.
16 "peerDependencies": {
17 "react": "^18",
18 "react-dom": "^18"
19 },
20 "devDependencies": {
21 "react": "^18",
22 "react-dom": "^18",
23 "next": "14.2.5",
24 "typescript": "^5",
25 "@types/node": "^20",
26 "@types/react": "^18",
27 "@types/react-dom": "^18",
28 "eslint": "^8",
29 "eslint-config-next": "14.2.5"
30 }
Step 7: Preview your package behaviour in another application using npm link
At this point it will be helpful to check if your package is building and configured correctly, and that another project is able to use it.
We can use npm link
to emulate using the package, without really publishing to NPM before we are ready.
npm run build
npm link
And in the other project
npm link an-example-react-package-built-with-nextjs-tooling
You should now be able to use the package as if it were any other package.
Step 8: Client components
If you have a client component like:
1"use client"
2import React, { useEffect } from "react";
3
4
5export function MyClientComponent() {
6
7 useEffect(() => {
8 console.log('hello!')
9 }, [])
10 return <div>This is the component</div>
11}
With an exports file like:
1export * from "./library/MyComponent"
2export * from "./library/MyClientComponent";
Then at this point you may be seeing this error:
Error: Unsupported Server Component type: undefined
This appears to be a pitfall with RSCs and NextJS see this Stack Overflow question.
The resolution is to export either your RSCs or your client components separately, like:
1"use client"
2export * from "./library/MyClientComponent";
and be sure to add this new exports file to your tsconfig.build.json
24 "include": [
25 "src/exports.ts",
26 "src/export-client.ts",
27 ],
and then have users import the component like:
import {MyComponent } from "an-example-react-package-built-with-nextjs-tooling";
import {MyClientComponent} from "an-example-react-package-built-with-nextjs-tooling/dist/export-client";
Step 9: npm publish
Run npm publish and see your published package!
This package is published here.
Addendum: Add other tooling you want to use
At this point you could add tools like Storybook for NextJS, or set up your unit tests etc.
Questions? Comments? Criticisms? Get in the comments! 👇
Spotted an error? Edit this page with Github