Next.js single page application?
Asked Answered
R

4

21

I am trying to make a single-page application in Next.js, but I am starting to think this is not possible. Can anyone show me how?

My first attempt was to use a HashRouter:

<HashRouter>
  <div>
    <NavLink to="/">Home</NavLink>
    &nbsp;&nbsp;
    <NavLink to="/purchaseComplete">Purchase Complete</NavLink>
  </div>
  <div>
    <Route exact path="/" Component={Default} />
    <Route path="/purchaseComplete" Component={PurchaseComplete} />
  </div>
</HashRouter>

But in the browser I see "Invariant failed: Browser history needs a DOM"

I then tried to use a StaticRouter:

<StaticRouter location="/" context={staticContext}>
  <div>
    <NavLink to="/">Home</NavLink>
    &nbsp;&nbsp;
    <NavLink to="/purchaseComplete">Purchase Complete</NavLink>
  </div>
  <div>
    <Switch>
      <Route exact path="/" Component={Default} />
      <Route path="/purchaseComplete" Component={PurchaseComplete} />
    </Switch>
  </div>
</StaticRouter>

This renders the two links, but nothing happens when you click them.

In the Next.js tutorials, they talk about SPA's in the beginning, but then they only show you how to make apps with multiple pages.

I am starting to think it's not possible to build an SPA in Next.js. Can anyone confirm this? Or can someone show me how to build an SPA in Next.js?

Renshaw answered 25/12, 2019 at 6:39 Comment(3)
why are you using router in nextjs? Nextjs has its own router system! create a pages directory and make a file it will be your page! single page application is a kind of concept which modern front end development made it possible. – Luanneluanni
in next when you have multiple pages you still are working with a SPA! – Luanneluanni
@Renshaw could you mark an answer as the accepted solution? – Mcauley
M
22

If you're using Next.js 9.5 or later the correct way to do this is with Rewrites. Do not use a custom server, it makes maintaining and deploying your site much harder and just isn't necessary here.

There is a detailed tutorial on how to do this here. The basic idea:

  1. Create a custom App (/pages/_app.tsx)

  2. Return null if typeof window === "undefined". This is required to prevent react-router from throwing errors during the SSR step!

// pages/_app.tsx

import { AppProps } from 'next/app';

function App({ Component, pageProps }: AppProps) {
  return (
    <div suppressHydrationWarning> // <- ADD THIS
      {typeof window === 'undefined' ? null : <Component {...pageProps} />}
    </div>
  );
}

export default App;
  1. Rewrite all routes to the homepage
// next.config.js

module.exports = {
  async rewrites() {
    return [
      // Rewrite everything else to use `pages/index`
      {
        source: '/:path*',
        destination: '/',
      },
    ];
  },
};

There is a lot more context/explanation in the linked tutorial but this will get you started.

Mcauley answered 29/10, 2020 at 23:52 Comment(6)
Doesn't the typeof window === 'undefined' ? null : <Component {...pageProps} /> check prevent SSR and SSG? I'm trying to find a way to use this method only on specific pages that act like SPAs and use SSG elsewhere. Could the typeof window check be applied conditionally only on those pages? If we could get the current route in the custom app component we could perhaps do that πŸ€” – Brigadier
@Brigadier if you move that code from _app.tsx to pages/index.tsx, SSG will still work on all your other pages. The existence of a page in the pages directory takes precedence over the rewrite rule. From the docs: > Rewrites are not able to override public files or routes in the pages directory as these have higher priority than rewrites. For example, if you have pages/index.js you are not able to rewrite / to another location unless you rename the pages/index.js file. – Mcauley
@ColinMcDonnell but is there a way to compile as a SPA? The goal would be to deploy the SPA as an Ionic (mobile) app to be downloaded in the App Store. And have a separate deploy for just the web app and have all the fun of SSR/SSG..? – Tonneson
Nope. Arguably the "pure" SPA paradigm is mutually exclusive with SSR and SSG. There's no way to both use Next's SSR/SSG capabilities and use React Router for routing. For a complex "dashboard-style" SaaS app there's really no need for it, and that's the use case where I'd recommend this approach. – Mcauley
typeof window === 'undefined' can cause cls issues – Giesecke
Does bundle splitting still work as expected when next's routing is replaced with React Router? – Broom
R
15

I found a blog post that answers this question: https://dev.to/toomuchdesign/next-js-react-router-2kl8

It turns out that Next.Js is intended to be used for multi-page apps.

The author explains how it is possible to use the React Router to make a single-page app with Next.JS. However, he also says the Next.JS authors responded and said they don't intend for Next.JS to be used this way!

I totally agree with the author, and am using his approach in my app. I want server-side rendering from NextJS, but also want it to be a single-page application.

Renshaw answered 27/12, 2019 at 11:1 Comment(1)
My whole issue with NextJS is that if I want a SPA, I have to find hacky ways to intercept stuff like manually-pasted URLs in the browser. Also, I can't use their pages structure, neither for my API nor my pages itself. I basically have one index.ts file and made it work as a SPA that way with my own Express server.. until I got to the deployment part and realized that I can't deploy to Vercel or Netflify with a custom Backend. Anyway, it's a hassle as it is, I'm considering switching back to CSR. – Einkorn
G
3

I am confused by the notion that Next.js is a SPA. How can it be a SPA when you get redirected to a different page and it gets re-rendered in a browser. Instead of SPA which only refreshes part of the page.

Also when we redirect to different page, we lose all the data in the store(Redux store) in memory, as the page is refreshed and new page is rendered. So I think calling Next.js a SPA is not the right way to go. Its similar to traditional SSR apps like EJS, Jade in Express.js, but instead we do SSR separately on client side by having a next.js server for it to server side render.

George answered 6/1, 2021 at 5:53 Comment(1)
Generally and if used correctly, Next.js does not reload the entire page when you go from one page to another and you would not lose the state of your Redux store. – Theocrasy
C
3

Currently, you need to hack away NextJS to make it work as SPA , there's already a RFC that would solve all these issues layouts-rfc

Edit: layouts are now a thing on NextJS 13

Cairns answered 12/10, 2022 at 5:1 Comment(0)

© 2022 - 2024 β€” McMap. All rights reserved.