DEV Community

Cover image for Bundling and Distributing Next.js Sites via NPM
Evan Tahler for Grouparoo

Posted on • Originally published at


Bundling and Distributing Next.js Sites via NPM

Grouparoo uses Next.js to build our web frontend(s), and we distribute these frontend User Interfaces (UIs) via NPM as packages, e.g. @grouparoo/ui-community. This allows Grouparoo users to choose which UI they want to use (or none) by changing their package.json:

Example package.json for a Grouparoo project:

  "author": "Your Name <>",
  "name": "grouparoo-application",
  "description": "A Grouparoo Deployment",
  "version": "0.0.1",
  "dependencies": {
    "@grouparoo/core": "0.3.3",
    "@grouparoo/postgres": "0.3.3",
    "@grouparoo/mailchimp": "0.3.3",
    "@grouparoo/ui-community": "0.3.3" // <-- Choose UI Package to install
  "scripts": {
    "start": "cd node_modules/@grouparoo/core && ./bin/start"
  "grouparoo": {
    "plugins": [
      "@grouparoo/ui-community" // <-- Choose UI Package to load
Enter fullscreen mode Exit fullscreen mode

Here is how we bundle up our Next.js applications so that our customers can use them out of the box.

next build and npm run prepare

The first step in “compiling” your Next.js projects is to use the next build command. We alias this to the “prepare” npm lifecyle command so that this command will be run automatically before npm publish. In this way we can ensure that we always have a freshly built bundle to use when we publish our packages.

This is different from Next’s recommendation to alias next build to npm build because we are not “deploying” our sites - we are publishing them. Many hosting providers look for a build script in your pacakge.json to run when the deploy, hence Next.js’ recommendation.

.npmignore vs .gitignore

The next step in bundling up a Next.js application for deployment via NPM is to include the build files. In all Next.js projects, you want to ignore the .next folder in your .gitignore. The .next folder is where Next.js keeps all the build artifacts it creates — minified javascript, css chunks, etc. Assuming your “source code” is Typescript and SCSS, everything in the .next folder should be ignored, and rebuilt as needed from the source.

BUT… the content of .next is actually what the visitors to your site really load - that’s the HTML, CSS, and Javascript that ends up in the browser. Since we are trying to package up a usable site, we need to bundle the contents of .next into our NPM bundles. However, we still want to exclude these rapidly changing files from git’s history.

The solution is a .npmignore file! By default, NPM will use a .gitignore file to determine which files it packs up into your packages, and which files it ignores. But, you can override this behavior by placing a .npmignore in your project. For example:


Enter fullscreen mode Exit fullscreen mode


# .next is included
Enter fullscreen mode Exit fullscreen mode

Skip the .pack files

The final thing we learned is that while the contents of the .next directory are needed for your visitors, not everything is needed. We saw that we were shipping 300mb packages to NPM for our Next.js UIs. We dug into the .next folder and learned that if you opt-into Webpack v5 for your Next.js site, large .next/cache/*.pack files will be created to speed up how Webpack works. This is normal behavior, but we were inadvertently publishing these large files to NPM! We added the .next/cache/* directory to our .npmignore and our build sizes went down to a more reasonable 20mb.

Our final .npmignore looks like this:


Enter fullscreen mode Exit fullscreen mode

Top comments (0)


11 Tips That Make You a Better Typescript Programmer

1 Think in {Set}

Type is an everyday concept to programmers, but it’s surprisingly difficult to define it succinctly. I find it helpful to use Set as a conceptual model instead.

#2 Understand declared type and narrowed type

One extremely powerful typescript feature is automatic type narrowing based on control flow. This means a variable has two types associated with it at any specific point of code location: a declaration type and a narrowed type.

#3 Use discriminated union instead of optional fields


Read the whole post now!