I have several projects currently in NextJS 13 and I have created my own library for some common classes, components utility functions that I can use for my different projects.
I am now looking at using NextJS 15 with App Router and using the more modern functionality with client only components, server components and server actions etc. Instead of trying to upgrade my current library, which I assume would be pretty complex I am working on a new library specifically for NextJS 15 but am running into problems.
Normally within my library project I have an index.ts file which does all of the exporting of the different functions and components but that doesn't seem to work with NextJS 15.
Everything was going fine when I creating a normal form that was client based using the onSubmit function, however it all started going haywire when I was creating a form component that used server actions.
I either run in to a problem where the UI wouldn't load at all and it complained about how I was using useEffect within a server component, and if I managed to get that working so the UI would load, when the server action was triggered, I'd then get an error stating that the server action couldn't be called from the client.
I assume its because I have a single index.ts that gets built into the library, and although I am importing specific things in the right place, the index file contains server and client side stuff so it throws a wobbly when anything in the library gets used.
I am assume I need to somehow segregate the client, server and shared components so I can control how each part gets imported in to my main app but I have no idea how that would work or even if that's possible.
My library project package.json file looks like below:
{
"name": "devso-nextjs-library",
"version": "1.0.0",
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/types/index.d.ts",
"exports": {
".": "./index.js",
"./package.json": "./package.json"
},
},
"scripts": {
"build": "tsup src/index.ts --format esm,cjs --dts"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"@types/node": "^22.9.0",
"typescript": "^5.6.3"
},
"devDependencies": {
"tsup": "^8.3.5",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5"
},
"peerDependencies": {
"react": "19.0.0-rc-66855b96-20241106",
"react-dom": "19.0.0-rc-66855b96-20241106"
}
}
And the library tsconfig.json looks like below
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Node",
"outDir": "dist",
"declaration": true,
"declarationDir": "dist/types",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"jsx": "preserve"
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
Then in the main app I import things using the below format
import {
FormButtonContainer, FormServer, FormServerState,
InputType,
PrimaryButton,
TextField, ZodValidationError
} from "devso-nextjs-library";
How would I go about doing this and import each thing as and where it is needed without anything conflicting such as server actions thinking they are being imported on the client etc.
I figured it out. I updated my package.json to be the below
{
"name": "devso-nextjs-library",
"version": "1.0.0",
"exports": {
"./client": {
"import": "./dist/client/index.mjs",
"require": "./dist/client/index.js"
},
"./server": {
"import": "./dist/server/index.mjs",
"require": "./dist/server/index.js"
},
"./shared": {
"import": "./dist/shared/index.mjs",
"require": "./dist/shared/index.js"
},
"./package.json": "./package.json"
},
"types": "./dist/shared/index.d.ts",
"scripts": {
"build": "tsup src/client/index.ts src/server/index.ts src/shared/index.ts --format esm,cjs --dts --out-dir dist --clean"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"typescript": "^5.6.3"
},
"devDependencies": {
"tsup": "^8.3.5",
"@types/react": "^18",
"@types/react-dom": "^18",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5"
},
"peerDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
}
And I can import it as below
import {
FormButtonContainer, FormServer, FormServerState,
InputType,
PrimaryButton,
TextField, ZodValidationError
} from "devso-nextjs-library/client";