Unfortunately, the app I ported from using the Pages Router to using App Router, is in a private repo. It's a Next.js static site SPA (Single Page App).
It's built with npm run build
and then exported so that the out/
directory is the only thing I need to ship to the CDN and it just works. There's a home page and a few dynamic routes whose slugs depend on an SQL query. So the SQL (PostgreSQL) connection, using knex
, has to be present when running npm run build
.
In no particular order, let's look at some differences
Build times
With caching
After running next build
a bunch of times, the rough averages are:
- Pages Router: 20.5 seconds
- App Router: 19.5 seconds
Without caching
After running rm -fr .next && next build
a bunch of times, the rough averages are:
- Pages Router: 28.5 seconds
- App Router: 31 seconds
Note
I have another SPA app that is built with vite
and wouter
and uses the heavy mantine
for the UI library. That SPA app does a LOT more in terms of components and pages etc. That one takes 9 seconds on average.
Static output
If you compare the generated out/_next/static/chunks
there's a strange difference.
Pages Router
360.0 KiB [##########################] /pages 268.0 KiB [################### ] 726-4194baf1eea221e4.js 160.0 KiB [########### ] ee8b1517-76391449d3636b6f.js 140.0 KiB [########## ] framework-5429a50ba5373c56.js 112.0 KiB [######## ] cdfd8999-a1782664caeaab31.js 108.0 KiB [######## ] main-930135e47dff83e9.js 92.0 KiB [###### ] polyfills-c67a75d1b6f99dc8.js 16.0 KiB [# ] 502-394e1f5415200700.js 8.0 KiB [ ] 0e226fb0-147f1e5268512885.js 4.0 KiB [ ] webpack-1b159842bd89504c.js
In total 1.2 MiB across 15 files.
App Router
428.0 KiB [##########################] 142-94b03af3aa9e6d6b.js 196.0 KiB [############ ] 975-62bfdeceb3fe8dd8.js 184.0 KiB [########### ] 25-aa44907f6a6c25aa.js 172.0 KiB [########## ] fd9d1056-e15083df91b81b75.js 164.0 KiB [########## ] ca377847-82e8fe2d92176afa.js 140.0 KiB [######## ] framework-aec844d2ccbe7592.js 116.0 KiB [####### ] a6eb9415-a86923c16860379a.js 112.0 KiB [####### ] 69-f28d58313be296c0.js 108.0 KiB [###### ] main-67e49f9e34a5900f.js 92.0 KiB [##### ] polyfills-c67a75d1b6f99dc8.js 44.0 KiB [## ] /app 24.0 KiB [# ] 1cc5f7f4-2f067a078d041167.js 24.0 KiB [# ] 250-47a2e67f72854c46.js 8.0 KiB [ ] /pages 4.0 KiB [ ] webpack-baa830a732d3dbbf.js 4.0 KiB [ ] main-app-f6b391c808310b44.js
In total 1.7 MiB across 27 files.
Notes
What makes the JS bundle large is most certainly due to using @primer/react
, @fullcalendar
, and react-chartjs-2
.
But why is the difference so large?
Dev start time
The way Next.js works, with npm run dev
, is that it starts a server at localhost:3000
and only when you request a URL does it compile something. It's essentially lazy and that's a good thing because in a bigger app, you might have too many different entries so it'd be silly to wait for all of them to compile if you might not use them all.
Pages Router
❯ npm run dev ... ✓ Ready in 1125ms ○ Compiling / ... ✓ Compiled / in 2.9s (495 modules)
App Router
❯ npm run dev ... ✓ Ready in 1201ms ○ Compiling / ... ✓ Compiled / in 3.7s (1023 modules)
Mind you, it almost always says "Ready in 1201ms" or but the other number, like "3.7s" in this example, that seems to fluctuate quite wildly. I don't know why.
Conclusion
Was it worth it? Yes and no.
I've never liked next/router
. With App Router you instead use next/navigation
which feels much more refined and simple. The old next/router
is still there which exposes a useRouter
hook which is still used for doing push
and replace
.
The getStaticPaths
and the getStaticProps
were not really that terrible in Pages Router.
I think the whole point of App Router is that you can get external data not only in getStaticProps
(or getServerSideProps
) but you can more freely go and get external data in places like layout.tsx
, which means less prop-drilling.
There are some nicer APIs with App Router. And it's the future of Next.js and how Vercel is pushing it forward.
Comments