Replacing Create React App with the Next.js CLI
How dare you make a jab at Create React App!?
Firstly, Create React App is good. But it's a very rigid CLI, primarily designed for projects that require very little to no configuration. This makes it great for beginners and simple projects but unfortunately, this means that it's pretty non-extensible. Despite the involvement from big names and a ton of great devs, it has left me wanting a much better developer experience with a lot more polish when it comes to hot reloading, babel configuration, webpack configuration, etc. It's definitely simple and good, but not amazing.
Now, compare that experience to Next.js which for starters has a much larger team behind it provided by a world-class company (Vercel) who are all financially dedicated to making it the best DX you could imagine to build any React application. Next.js is the 💣-diggity. It has amazing docs, great support, can grow with your requirements into SSR or static site generation, etc.
So why aren't more people using Next.js to build single-page-apps?
Next.js apps are normally tethered to its runtime framework. It's expected that you handle navigation with their next/router
and next/link
components and use their framework to build SSR-enabled apps out of the gate. Don't get me wrong, this is amazing, and if I'm building a website (not an app that probably sits behind a login), then I'll definitely go the normal route (pun intended) and stick with the Next.js runtime framework.
But if you're building a true single page app experience, you may not want to fudge with the Next.js runtime at all and just build as you would with CRA, but still get the amazing CLI and build experience that Next offers.
If that's you... then FINALLY I can tell yo how simple it is to achieve this:
How?
- Remove
react-scripts
and any related scripts from your build - Go through the Getting Started - Manual Setup guide for Next.js
- Add the
.next
directory to your.gitignore
file - Rewrite all routes to be handled by
pages/index.js
innext.config.js
.
module.exports = {
target: 'serverless',
async rewrites() {
return [
// Do not rewrite API routes
{
source: '/api/:any*',
destination: '/api/:any*',
},
// Rewrite everything else to use `pages/index`
{
source: '/:any*',
destination: '/',
},
]
},
}
- Export your default App component in
pages/index.js
export default function App() {
return <div>Hello Next!</div>
}
- Add React Router
npm install react-router-dom history
import React from 'react'
import { BrowserRouter, Routes, Route } from 'react-router-dom'
export default function App() {
if (typeof document === 'undefined') {
return null
}
return (
<BrowserRouter>
<Routes>
<Route
path="/"
element={
<>
<h1>Welcome!</h1>
</>
}
/>
</Routes>
</BrowserRouter>
)
}
- Suppress Hydration Warnings
Rendering null
on the server and not on the client will produce a hydration mismatch warning. Since, in this case, that's expected, we can silence it with a quick wrapper component that will supress the warning:
function SafeHydrate({ children }) {
return (
<div suppressHydrationWarning> // Must be a div, can't be a fragment 😑🤦♂️
{typeof document === 'undefined' ? null : children}
</div>
)
}
export default function App() {
return (
<SafeHydrate>
<BrowserRouter>
<Routes>
<Route
path="/"
element={
<>
<h1>Welcome!</h1>
</>
}
/>
</Routes>
</BrowserRouter>
</SafeHydrate>
)
}
And on and on...
Follow Next.js docs, examples and guides to setup/migrate any other functionality or libraries.
@dpyzo0o 👋
The
next/link
component implements an intersection observer. When a link is visible in the page or you mouseover the link, it'll prefetch the page. However, with react-routerLink
, it doesn't prefetch the page. For instance, by just moving the mouse over theLink
(ofnext/link
), you can see in devtools network panel that a request is made to prefetch the page.You can still export server-side rendered apps with
next export
using my approach. In the CodeSandbox I usedgetServerSideProps
just for demonstration but you can completely get away without itGood point 👍 but because the gist forces not to SSR on the
_app
, other pages won't be SSR-ed 😅My CodeSandbox was out of date with I was locally working on. I took a slightly different approach by creating a compatible
history
instance with react-router and I updated the[[...index]]
file to reflect how it'd look like if you were to do anext export
https://codesandbox.io/s/nextjs-react-router-md4ur?file=/pages/%5B%5B...index%5D%5D.tsx