Ogundipe Samuel software engineer and technical writer

Building a server-rendered React app with Next.js and Express.js

5 min read 1541

How To Build A Server-Rendered React App With Next.js And Express.js

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 – .

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.

Ogundipe Samuel software engineer and technical writer

4 Replies to “Building a server-rendered React app with Next.js and Express.js”

  1. 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);

  2. server.js file executes client side rendering and sever side rendering??
    i think when client side rendering, server.js doesn’t execute..

Leave a Reply