The Next.js App Bundles
By Flavio Copes
Learn how Next.js splits your app code into separate JavaScript bundles like index.js, _app.js, webpack.js, and main.js, and what each preloaded file contains.
When you view the page source of a Next.js app, you can see a bunch of JavaScript files being loaded. Those are the app bundles: Next.js splits your application code into separate files, so the browser only downloads what each page needs.

Let’s start by putting the code in an HTML formatter to get it formatted better, so we humans can get a better chance at understanding it:
<!DOCTYPE html>
<html>
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1" />
<meta name="next-head-count" content="2" />
<link rel="preload" href="/_next/static/development/pages/index.js?ts=1572863116051" as="script" />
<link rel="preload" href="/_next/static/development/pages/_app.js?ts=1572863116051" as="script" />
<link rel="preload" href="/_next/static/runtime/webpack.js?ts=1572863116051" as="script" />
<link rel="preload" href="/_next/static/runtime/main.js?ts=1572863116051" as="script" />
</head>
<body>
<div id="__next">
<div>
<h1>Home page</h1></div>
</div>
<script src="/_next/static/development/dll/dll_01ec57fc9b90d43b98a8.js?ts=1572863116051"></script>
<script id="__NEXT_DATA__" type="application/json">{"dataManager":"[]","props":{"pageProps":{}},"page":"/","query":{},"buildId":"development","nextExport":true,"autoExport":true}</script>
<script async="" data-next-page="/" src="/_next/static/development/pages/index.js?ts=1572863116051"></script>
<script async="" data-next-page="/_app" src="/_next/static/development/pages/_app.js?ts=1572863116051"></script>
<script src="/_next/static/runtime/webpack.js?ts=1572863116051" async=""></script>
<script src="/_next/static/runtime/main.js?ts=1572863116051" async=""></script>
</body>
</html>
What are those 4 files?
We have 4 JavaScript files being declared to be preloaded in the head, using rel="preload" as="script":
/_next/static/development/pages/index.js(96 LOC)/_next/static/development/pages/_app.js(5900 LOC)/_next/static/runtime/webpack.js(939 LOC)/_next/static/runtime/main.js(12k LOC)
Each one has a specific job.
index.js contains the code of the page component serving the / route. It’s tiny, because it only holds the code you wrote for that page.
_app.js contains the App component, the wrapper that Next.js renders around every page. Anything you put there loads on every route.
webpack.js is the webpack runtime, the glue code that lets the bundles find and load each other.
main.js is the Next.js client-side code. It bootstraps the app in the browser, hydrates the server-rendered HTML, and handles client-side routing.
The preload hint tells the browser to start downloading those files as soon as possible, before the normal rendering flow reaches the script tags at the end of the body. This improves the page loading performance.
The page data
Then those 4 files are loaded at the end of the body, along with /_next/static/development/dll/dll_01ec57fc9b90d43b98a8.js (31k LOC), and a JSON snippet that sets some defaults for the page data:
<script id="__NEXT_DATA__" type="application/json">
{
"dataManager": "[]",
"props": {
"pageProps": {}
},
"page": "/",
"query": {},
"buildId": "development",
"nextExport": true,
"autoExport": true
}
</script>
Next.js reads this JSON during hydration. It’s how the client-side code knows which page it’s on and which props the server used, so React can take over the static HTML without re-fetching anything.
Code splitting
The bundle files loaded are already implementing one feature called code splitting. The index.js file provides the code needed for the index component, which serves the / route.
If we had more pages, we’d have more bundles, one for each page. A visitor landing on / never downloads the code for /contact. Each page bundle is only loaded when needed, which keeps the initial load fast even as the app grows.
These are development bundles, which is why they are so big. In production, Next.js minifies them and adds a content hash to each filename, so browsers can cache them for a long time.
One thing to watch out for: since _app.js wraps every page, a heavy library imported there gets shipped to every single route. If only one page needs a chart library, import it in that page instead, so the other pages don’t pay for it.
Related posts about next: