javascript - How to publish Next.js app as npm package? - Stack Overflow

I have a web app built on Next.js. I want to publish this app as npm package so it can be used in other

I have a web app built on Next.js. I want to publish this app as npm package so it can be used in other projects. I tried to find resources and help using google but did not find any useful information.

Is it possible? if yes how can I achieve this?

Thanks

I have a web app built on Next.js. I want to publish this app as npm package so it can be used in other projects. I tried to find resources and help using google but did not find any useful information.

Is it possible? if yes how can I achieve this?

Thanks

Share Improve this question asked Jun 1, 2022 at 8:41 Rizwan IjazRizwan Ijaz 1714 silver badges13 bronze badges 5
  • The NPM website does have decent documentation on it. – Quentin Commented Jun 1, 2022 at 8:43
  • I know how to publish a npm package developed using plain JavasSript and also with React Js ponents. But I can't figure out how can I achieve this for Next.js. @Quentin – Rizwan Ijaz Commented Jun 1, 2022 at 8:59
  • 1 Why would it be different for Next.js? – Quentin Commented Jun 1, 2022 at 9:00
  • 1 Most of the resources explains only how to generate npm package of React ponents but not the plete web application. Same goes for Next.js @Quentin – Rizwan Ijaz Commented Jun 1, 2022 at 9:11
  • Have you found the solution to this question? How to publish the Next.js app as an npm package? – Munir Shah Commented Jan 15, 2024 at 19:16
Add a ment  | 

3 Answers 3

Reset to default 6

I have almost the same need. I created a blog using next js pages and i want to share these pages for 2 next js projects.

Until now, i've been able to make it using vite / rollup builder this way (simplified):

NPM package:
// node_modules/@namespace/next-blog/pages/ssr/BlogArticle.tsx
export SSRBlogArticle = getServerSideProps(...) {...}
export BlogArticlePage: NextPage = (SSRProps) => <Blog {..props} />
Using Package in my main next js app
// ./src/pages/blog.tsx
import { SSRBlogArticle, BlogArticlePage } from '@namespace/next-blog'

export getServerSideProps = SSRBlogArticle

const BlogPage: NextPageWithLayout = BlogArticlePage

// some layout
BlogPage.getLayout = (page) => <Layout>{page}</Layout>

export default BlogPage

The problem is about the usage of process.ENV and useRouter. next/link seems not to work...

Here is my vite configuration file :
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import dts from 'vite-plugin-dts'
import gql from 'vite-plugin-simple-gql'
import tsconfigPaths from 'vite-tsconfig-paths'
import * as path from 'path'
import pkg from './package.json'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    react(),
    tsconfigPaths(),
    gql(),
    dts({
      insertTypesEntry: true,
    }),
  ],
  resolve: {
    alias: [{ find: '@', replacement: path.resolve(__dirname, 'src') }],
  },
  build: {
    lib: {
      entry: path.resolve(__dirname, 'src/index.ts'),
      name: 'NextBlog',
      formats: ['es', 'cjs'],
      fileName: (format) => `index.${format}.js`,
    },
    rollupOptions: {
      external: [
        ...Object.keys(pkg.dependencies),
        ...Object.keys(pkg.peerDependencies),
      ],
    },
  },
})

So i would like to add more questions to the original one :

  • Can we read env file from npm packages? process.ENV reading main app env file ?
  • Why useRouter is not working properly?
  • Am i doing it right? Is it good practice?

Thanks for your help :)

Edit

Process.env
  • I've managed to find out why all process.env were removed from build, vitejs removes them and replace with {}. Solution is :
define: {
  // keep process.env* vars in code bundle
  'process.env': 'process.env',
},
useRouter

Still impossible to understand the issue with it...

Here one example error on useRouter in main app when clicking on next/link:

Uncaught TypeError: Cannot read properties of null (reading 'push')
    at linkClicked (index.es.js?4bcb:3731:1)
    at onClick (index.es.js?4bcb:3830:1)
    at HTMLUnknownElement.callCallback (react-dom.development.js?ac89:4161:1)
    at Object.invokeGuardedCallbackDev (react-dom.development.js?ac89:4210:1)
    at invokeGuardedCallback (react-dom.development.js?ac89:4274:1)
    at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js?ac89:4288:1)
    at executeDispatch (react-dom.development.js?ac89:9038:1)
    at processDispatchQueueItemsInOrder (react-dom.development.js?ac89:9070:1)
    at processDispatchQueue (react-dom.development.js?ac89:9083:1)
    at dispatchEventsForPlugins (react-dom.development.js?ac89:9094:1)
    at eval (react-dom.development.js?ac89:9285:1)
    at batchedUpdates$1 (react-dom.development.js?ac89:26096:1)
    at batchedUpdates (react-dom.development.js?ac89:3988:1)
    at dispatchEventForPluginEventSystem (react-dom.development.js?ac89:9284:1)
    at dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay (react-dom.development.js?ac89:6462:1)
    at dispatchEvent (react-dom.development.js?ac89:6454:1)
    at dispatchDiscreteEvent (react-dom.development.js?ac89:6427:1)

For now, the only work around i'm thinking off is using native a tags...

Edit 2

I have finally found out the solution : to make router object not null inside of my package, i have to pass it from main nextjs app to page's package. Inside of the package, i had to wrap my ponents with RouterContext:

// package next page
import { RouterContext } from 'next/dist/shared/lib/router-context' // next 12

const MyPackagePage = (props) => {
  <RouterContext.Provider value={props.router}>
    <MyComponents ... />
  </RouterContext.Provider>
}

// main next app
const Page = (props) => {
  const router = useRouter()
  return MyPackagePage({ router, ...props })
}

Now works fine. Less easy to integrate and more boilerplate code, but at least it's possible de export next pages in npm packages.

The solution that I going to say is worked for me. It is related to my ponent library that make using Nextjs and Material UI ponents.

First you need to add the below dependency

//It takes your TypeScript code, piles it to JavaScript, bundles the files.
npm install --save-dev tsup typescript

Then follow the below steps,

  1. I just created a ponent. Below is example ponent.
    'use client';
    import * as React from 'react';
    import Box from '@mui/material/Box';
    import TextField from '@mui/material/TextField';
    
    type BasicTextFieldsProps = {
        defaultlabel: string;
        defaultValue?: string;
        variant: 'filled' | 'outlined' | 'standard';
        required?: boolean;
        disable?: boolean;
    }
    export default function BasicTextFields({
        defaultlabel = 'Enter Text',
        defaultValue = '',
        variant = 'filled',
        required = false,
        disable = false,
    }: BasicTextFieldsProps) {
        const [label,setLabel] = React.useState(defaultlabel);
    
      return (
        <Box
          ponent="form"
          sx={{ '& > :not(style)': { m: 1, width: '25ch' } }}
          noValidate
          autoComplete="off"
        >
          <TextField  label={label} defaultValue={defaultValue} required={required} disabled={disable} variant={variant} />
        </Box>
      );
    }
  1. Then export the ponents using index.ts file.

you need to create new index.ts file in the main directory

//Export the ponents
export { default as SearchBar } from './src/ponents/SearchBar';
export { default as BasicTextFields } from './src/ponents/BasicTextFields';
  1. Change the package.jsonand tsconfig.json.

Most of the things needed to be added manually

//package.json
{
  "name": "my-ponent-library",
  "version": "0.1.0",
  "private": false,
  "main": "dist/index.js",
  "module": "dist/index.mjs",
  "files": ["dist"],
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "tsup index.ts --dts --format esm,cjs --out-dir dist",
    "prepare": "npm run build",
    "start": "next start",
    "lint": "next lint"    
  },
  "dependencies": {
    "@emotion/cache": "^11.14.0",
    "@emotion/react": "^11.14.0",
    "@emotion/styled": "^11.14.0",
    "@mui/icons-material": "^6.4.7",
    "@mui/material": "^6.4.7",
    "@mui/material-nextjs": "^6.4.3",
    "@mui/styled-engine-sc": "^6.4.6",
    "next": "15.2.1",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "styled-ponents": "^6.1.15"
  },
  "devDependencies": {
    "@tailwindcss/postcss": "^4",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "@vitest/browser": "^3.0.8",
    "@vitest/coverage-v8": "^3.0.8",
    "playwright": "^1.51.0",
    "tailwindcss": "^4",
    "tsup": "^8.4.0",
    "typescript": "^5.8.2",
    "vitest": "^3.0.8"
  }
}

//tsconfig.json
{
  "pilerOptions": {
    "target": "ES2017",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "declaration": true,
    "declarationDir": "dist",
    "plugins": [
      {
        "name": "next"
      }
    ],
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts","ponents","index.ts"],
  "exclude": ["node_modules"]
}

  1. That's it then you can publish the package by run below mands
//Log to your npm account
npm login

//Publish the package
npm publish --access public 
or
npm publish

Hope this will work for you. Feel free to ask any other clarifications.

Additionally

  • sometimes after one publish if you try to publish again an error may be appear. simply delete the dist folder and try again. It will resolve the problem.

You can use semantic-release npm package.

In your package.json file you should add follow configuration

  "release": {
    "branches": [
      "master"
    ],
    "plugins": [
      "@semantic-release/mit-analyzer",
      "@semantic-release/release-notes-generator",
      [
        "@semantic-release/changelog",
        {
          "changelogFile": "CHANGELOG.md"
        }
      ],
      "@semantic-release/npm",
      "@semantic-release/github",
      [
        "@semantic-release/git",
        {
          "assets": [
            "CHANGELOG.md",
            "package.json",
            "package-lock.json"
          ]
        }
      ]
    ]
  }

And your project should be public and should have "private": false in package.json.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1742342382a4425886.html

相关推荐

  • javascript - How to publish Next.js app as npm package? - Stack Overflow

    I have a web app built on Next.js. I want to publish this app as npm package so it can be used in other

    8小时前
    10

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信