Last active
October 14, 2025 04:17
-
Star
(471)
You must be signed in to star a gist -
Fork
(47)
You must be signed in to fork a gist
-
-
Save gaearon/9d6b8eddc7f5e647a054d7b333434ef6 to your computer and use it in GitHub Desktop.
Revisions
-
gaearon revised this gist
Mar 22, 2023 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -6,6 +6,8 @@ You use Next.js router like normally, but don't define `getStaticProps` and such You can generate HTML fallback for the page if there's something meaningful to show before you "know" the params. (Remember, HTML is static, so it can't respond to dynamic query. But it can be different per route.) > Don't like Next? [Here's how to do the same in Gatsby.](https://www.gatsbyjs.com/docs/how-to/routing/client-only-routes-and-user-authentication/) ## Building ``` -
gaearon renamed this gist
Mar 18, 2023 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
gaearon revised this gist
Mar 18, 2023 . No changes.There are no files selected for viewing
-
gaearon revised this gist
Mar 18, 2023 . No changes.There are no files selected for viewing
-
gaearon revised this gist
Mar 18, 2023 . 5 changed files with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes.File renamed without changes.File renamed without changes.File renamed without changes.File renamed without changes. -
gaearon revised this gist
Mar 18, 2023 . 1 changed file with 10 additions and 12 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -6,7 +6,7 @@ You use Next.js router like normally, but don't define `getStaticProps` and such You can generate HTML fallback for the page if there's something meaningful to show before you "know" the params. (Remember, HTML is static, so it can't respond to dynamic query. But it can be different per route.) ## Building ``` npm run build @@ -16,7 +16,7 @@ This produces a **purely static app** in `out/` folder. It's purely static in the sense that it doesn't require Node.js — but **router transitions are all client-side.** It's an SPA. ## Deploying Host the `out/` folder anywhere on a static hosting. @@ -30,22 +30,20 @@ But if you look at what Next.js generated, it's an HTML file **per route**: - `out/404.html` - `out/stuff/[id].html` So you'd need to teach your static server to do this rewrite: - rewrite `/` to `out/index.html` - rewrite `/stuff/whatever` to `out/stuff/[id].html` The syntax for these rewrites is different for different static hosts, and I don't think there's an automated solution yet. I suspect this is why a lot of people don't realize Next.js can produce SPAs. It's just not obvious how to set this up. Ideally I think Next.js should either pregenerate such rewrite lists for common static hosts (e.g. Nginx, Apache) etc or there should be some common format that can be adopted across providers (e.g. Vercel, Netlify, etc). (Note that if we didn't do `next export`, Next.js would **still** create static output, but it would also generate a Node.js server that serves *that* static output. This is why by default SSG mode in Next.js emits a Node.js app. But it doesn't mean your app needs Node.js by default. **Next.js apps don't need Node.js at all by default, until you start using server-only features.** But you *do* need to rewrite your requests to serve the right HTML file for each route.) ## This is a better SPA It **makes sense** to have an HTML file per route even if you're doing an SPA. Yes, we need to figure out a good way to set up these rewrite maps, but it's strictly better than serving one `index.html` page with a giant bundle (or a smaller bundle + a bunch of code that's loaded in a waterfall), which is how SPAs are typically done today. -
gaearon created this gist
Mar 18, 2023 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,53 @@ # Next.js client-only SPA example Made this example to show how to use Next.js router for a 100% SPA (no JS server) app. You use Next.js router like normally, but don't define `getStaticProps` and such. Instead you do client-only fetching with `swr`, `react-query`, or similar methods. You can generate HTML fallback for the page if there's something meaningful to show before you "know" the params. (Remember, HTML is static, so it can't respond to dynamic query. But it can be different per route.) ## Building ``` npm run build ``` This produces a **purely static app** in `out/` folder. It's purely static in the sense that it doesn't require Node.js — but **router transitions are all client-side.** It's an SPA. ## Deploying Host the `out/` folder anywhere on a static hosting. There is however an unintuitive caveat — which is maybe why people don't use Next.js widely this way at the moment. In traditional SPA setups like CRA and Vite, you have one file like `index.html` that's served for every route. The downside of this is that (1) it's empty, (2) it contains **your entire bundle** by default (code splitting and `React.lazy` doesn't help here a lot because it creates waterfalls — you still have to load the main bundle first before it "decides" to load other scripts). But if you look at what Next.js generated, it's an HTML file **per route**: - `out/index.html` - `out/404.html` - `out/stuff/[id].html` What you need to do is to map requests when deploying. You want your static server to do this: - request `/` -> serve `out/index.html` - request `/stuff/123` -> serve `out/stuff/[id].html` - request `/stuff/456` -> serve `out/stuff/[id].html` - request `/stuff/789` -> serve `out/stuff/[id].html` - (you get the idea) So you'd need to teach your static server to do this rewrite: - rewrite `/` to `out/index.html` - rewrite `/stuff/whatever` to `out/stuff/[id].html` The syntax for these rewrites is different for different static hosts, and I don't think there's an automated solution yet. Ideally I think Next.js should either pregenerate such rewrite lists for common static hosts (e.g. Nginx, Apache) etc or there should be some common format that can be adopted across providers (e.g. Vercel, Netlify, etc). I think some kind of related work might already be ongoing. ## These are better SPAs It **makes sense** to have an HTML file per route even if you're doing an SPA. Yes, we need to figure out a good way to set up these rewrite maps, but it's strictly better than serving one `index.html` page with a giant bundle (or a smaller bundle + a bunch of code that's loaded in a waterfall), which is how SPAs are typically done today. It's also great to **have the ability** to do a bunch of stuff at the build time. E.g. I can still do `getStaticProps` + `getStaticPaths` in this app to pregenerate a bunch of HTML files **with actual content**. It's still an SPA and still can be hosted statically! I can also eventually decide to add a server, and I don't need to rewrite anything. This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,40 @@ // pages/stuff/[id].js import { useRouter } from 'next/router'; import Link from 'next/link'; import useSWR from 'swr' export default function Page() { const router = useRouter(); const id = router.query.id if (id == null) { // Static pre-generated HTML return <h1>Loading...</h1> } return ( <> <h1>Page for {id}</h1> <ul> <li> <Link href="/stuff/1">go to 1</Link> </li> <li> <Link href="/stuff/2">go to 2</Link> </li> </ul> <hr /> <Content id={id} /> </> ) } const fetcher = (...args) => fetch(...args).then((res) => res.json()) function Content({ id }) { const { data, error } = useSWR('https://jsonplaceholder.typicode.com/todos/' + id, fetcher) if (error) return <h1>Failed to load</h1> if (!data) return <h1>Loading...</h1> return ( <pre>{JSON.stringify(data, null, 2)}</pre> ); } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,20 @@ // pages/index.js import Link from 'next/link'; export default function Page() { return ( <> <h1>Index page</h1> <hr /> <ul> <li> <Link href="/stuff/1">go to 1</Link> </li> <li> <Link href="/stuff/2">go to 2</Link> </li> </ul> </> ); } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,5 @@ const nextConfig = { output: 'export', } module.exports = nextConfig This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,21 @@ { "name": "foobar", "version": "0.1.0", "private": true, "scripts": { "dev": "next dev", "build": "next build && next export", "start": "next start", "lint": "next lint" }, "dependencies": { "next": "13.2.4", "react": "18.2.0", "react-dom": "18.2.0", "swr": "^2.1.0" }, "devDependencies": { "eslint": "8.36.0", "eslint-config-next": "13.2.4" } }