It goes without saying that React is an excellent choice for building high-quality web applications. However, as things become more complex, you’ll need to learn about client-side routing, page layout, and so on. At some point you’ll want your pages to load faster. Often times, this is where things can become difficult.
Next.js is a universal JavaScript framework that runs in both the browser and the server. It offers developers an easy way to get started, and since it uses React for templating, it is also a straightforward way for developers with React experience to get productive fast.
One of its strong points is that it handles server-side rendering excellently, and it integrates with Express just well. Oh, how I love Express.
But we’re getting ahead of ourselves. Let’s talk about why you need server-side rendering in the first place. Then we’ll start building things.
What is server-side rendering, exactly?
Server-side rendering was the conventional method for getting your HTML up onto a screen. This refers to using a server environment to get your HTML up to the browser.
So why the fuss if it’s a conventional method that’s been around forever?
Remember the introduction of MVC (model, view, controller) concept that caused some concern? Basically, there was some disagreement that eventually brought about the rise of JavaScript frameworks for rendering views.
So what has this got to do with anything?
Soon enough, a problem emerged: the JavaScript frameworks only displayed a bunch of divs in the browser, using DOM manipulation to do its work around the browser. This meant that the user had to wait longer to see anything. It can also impact SEO if crawlers can’t see the content of the page quickly.
One solution was to render the JavaScript files from the server before returning to its output to the server.
And now you know.
Getting started with Next
Getting started is simple. All we need to do is make a new directory, start a new node project, and install Next, React, and ReactDOM.
#make a directory mkdir logrocket-next #change to the new directory cd logrocket-next #init a new node project npm init -y #install react, react-dom and next npm install --save react react-dom next
Next, open up your package.json
and replace your script section with this:
"scripts": { "dev": "next", "build": "next build", "start": "next start" }
Run the npm run dev
command, you should get an error like :
next > Couldn't find a `pages` directory. Please create one under the project root
This is because Next
uses the pages
directory and the files in them to map its routes. This means if we have a file called index.js
in our pages folder, Next
would try to use the component in it as our entry point. Let’s create the pages folder and the index.js
file.
#create pages directory mkdir pages #create file for base route touch pages/index.js
Next, let’s add some code to the pages/index.js
file:
const Index = () => ( <div> <p>Hello Next.js, this is your friend Brian from logrocket</p> </div> ) export default Index
Save the file above and run the command npm run dev
in your terminal. If you visit your browser, see the text “Hello Next.js, this is your friend Brian from logrocket” printed out on your browser.
Notice how easy this is? No need to mount React to a div, no need to import React, no need to set up routes. In our usual React app, we would need to do other configs to allow for code-splitting and server-side rendering. But hey, view your page source. You’ll be amazed. It’s all done out of the box.

Notice in the image above, there is a specific reference to [/_next/-/page/index.js](http://localhost:3000/_next/-/page/index.js)
? That’s code splitting done right. Also, notice that the div which has your text was fully rendered? That’s server-side rendering taking place.
Next and Express
I bet you thought that was all the magic Next
had in store. Next went a step further by allowing better server-side rendering using Express for the tougher cases.
First, add Express into your app:
npm install --save express
Then create a file called ssr-server.js
in your app and add following content:
const express = require('express') const next = require('next') const dev = process.env.NODE_ENV !== 'production' const app = next({ dev }) const handle = app.getRequestHandler() app.prepare() .then(() => { const server = express() server.get('*', (req, res) => { return handle(req, res) }) server.listen(3000, (err) => { if (err) throw err console.log('> Ready on http://localhost:3000') }) }) .catch((ex) => { console.error(ex.stack) process.exit(1) })
What happens in the code above? We require both Express and Next libraries. We create an instance of the Next
library passing in a Boolean based on the environment which detects whether to launch Next.js in dev mode or not.
We move ahead to call the getRequestHandler()
function, then finally we prepare the app. The prepare function returns a promise, so we can do a .then
pipe to it. In the .then
call, we initiate Express, and we use a wildcard route to catch all routes and return it to the handler function.
Now update your npm dev script to:
{ "scripts": { "dev": "node ssr-server.js" } }
If you run npm run dev
, your page would spin up looking the same as it was. So how is this helpful if I end up getting the same result as I got earlier? Let me show you.
While what we have done above does not seem to add much difference, it makes sense when we add more routes as it helps to achieve clean URLs. It’s worth noting that, if implemented in Next, this would return 404 pages (when not navigated by a Next link i.e if I manually put in the URL in a browser or I was directed from another site).
Look at this route below. This helps us to achieve clean URLs as discussed in the paragraph above:
server.get('/p/:id', (req, res) => { const actualPage = '/post' const queryParams = { id: req.params.id } app.render(req, res, actualPage, queryParams) })
By default, it’s easy to use query strings in Next, but as usual, you want to keep your URLs clean, so you opt for something like: /p/2
rather than /p?id=2
.
In the code above, we use the popular express routing to define such routes, then we pass the page that should be loaded and the id as a query param to the main Next app. Here the call /p?id=2
happens under the hood where no one can see what is going on.
The regular user sees the URL as /p/2/
.
Serving and exporting your app
When you’ve finished building the Next app, the question becomes: “How do I serve it in production?”
Easy.
First, we have to build the app. Then we can serve it. Luckily, Next provides an easy way out. Remember the script section we had in the package.json
? We had it all set up there.
All we need to do is:
#build the app npm run build #serve the app npm run serve
Wow, that’s cool, what if you want to export the app as a static HTML file? Great question. First, create a file called next.config.js
in the root of your app and add the following content:
module.exports = { exportPathMap: function () { return { '/': { page: '/' } } } }
Note: In the setup above, only the index page would be exported as it is the only route we have specified in the PathMap.
What if we want to add more pages? Amigos, that’s a good question.
You can add a new key and value in the return object of the PathMap
like '/about': { page: '/about' }
.
Then add the following to the scripts section of your package.json
:
"export": "next export"
Finally, build and export your app.
#build app npm run build #export app npm run export
Frontend monitoring For React apps
Debugging React applications can be difficult, especially when there is complex state. If you’re interested in monitoring and tracking Redux state for all of your users in production, try LogRocket. https://logrocket.com/signup/
LogRocket is like a DVR for web apps, recording literally everything that happens on your site. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.
The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.
Modernize how you debug your React apps – Start monitoring for free.
Conclusion
In this tutorial, we have been able to see how relatively easy it is to build a server-rendered app with Next.js. It is an excellent way of doing things using React. If you have gone through the procedure to achieve the same result in React, you will agree with me on this one.
Do you have any comments or observations? Let’s talk in the comments section.
Great article!
“serve” script is not described.
I was having problems with the ‘/p/:id’ request. I solved it adding a return to the app.render line:
return app.render(req, res, actualPage, queryParams);
This serve I believe: https://github.com/zeit/serve
server.js file executes client side rendering and sever side rendering??
i think when client side rendering, server.js doesn’t execute..
To use the programmatic API of Next.js, the node scripts need to be changed from “dev”: “next” to “dev”: “node server.js” and others similarly. See https://nextjs.org/docs#custom-server-and-routing
The prod section is incomplete.
Hello,
I tried it, the response in the route ‘posts/:id’ is returning all the posts but not only my post id…..(I tested the url “http://localhost:3000/posts?id=123” with Postman, with the id as a query params).
Am i missing something? Thanks!