React has become one of the most popular front-end libraries used to build dynamic and responsive user interfaces. With React, developers can create complex single-page applications (SPAs) that offer a great user experience. However, there are some limitations to SPAs, especially when it comes to search engine optimization (SEO) and initial page loading times. That's where server-side rendering (SSR) comes in.
SSR allows us to render our React components on the server and send the pre-rendered HTML to the client. This approach improves the initial loading time of our app and allows search engines to crawl our pages easily. In this article, we will learn how to build a server-side-rendered React app with Node.js.
Prerequisites
Before we start, make sure you have Node.js and npm installed on your machine. You should also have a basic understanding of React and Node.js.
Setting up the project
We'll start by creating a new Node.js project and installing the required dependencies. Open your terminal and enter the following commands:
mkdir my-ssr-app
cd my-ssr-app
npm init -y
npm install express react react-dom react-router-dom
In the code above, we're creating a new directory called "my-ssr-app" and navigating into it. Then, we're initializing a new Node.js project and installing the required dependencies: Express, React, ReactDOM, and React Router DOM.
Creating the server
Next, let's create the server that will render our React app on the server. Create a new file called "server.js" and add the following code:
const express = require('express');
const React = require('react');
const ReactDOMServer = require('react-dom/server');
const { StaticRouter } = require('react-router-dom');
const App = require('./App');
const app = express();
app.use(express.static('public'));
app.get('*', (req, res) => {
const context = {};
const html = ReactDOMServer.renderToString(
<StaticRouter location={req.url} context={context}>
<App />
</StaticRouter>
);
if (context.url) {
res.redirect(context.url);
} else {
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>My SSR App</title>
</head>
<body>
<div id="root">${html}</div>
<script src="/bundle.js"></script>
</body>
</html>
`);
}
});
app.listen(3000, () => {
console.log('Server is listening on port 3000');
});
In the code above, we're importing the required modules and creating an Express app. We're also serving the static files in the "public" directory.
The main logic of our server is in the app.get route. We're using the StaticRouter component from react-router-dom to render our React app and passing the req.url to it. We're also initializing an empty context object that we can use to handle redirects or other server-side logic.
We're then using ReactDOMServer.renderToString() to render our app to a string of HTML. If the context object has a url property, it means that we need to redirect the user to a different page. Otherwise, we're sending the HTML string to the client with the required script tags.
Finally, we're starting the server and listening on port 3000.
Creating the client
Now that we have our server set up, let's create the client-side React app. Create a new file called "App.js" in the root directory of your project and add the following code:
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import Home from './components/Home';
import About from './components/About';
import Contact from './components/Contact';
import NotFound from './components/NotFound';
const App = () => {
return (
<div>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
<Route component={NotFound} />
</Switch>
</div>
);
};
export default App;
In the code above, we're importing the required modules and creating the App component. We're using the Switch and Route components from react-router-dom to define our app's routes. We're also importing the Home, About, Contact, and NotFound components that will be rendered for each route.
Creating the components
Let's create the components that we'll use to render our app's pages. Create a new directory called "components" in the root directory of your project and add the following files:
Home.js
import React from 'react';
const Home = () => {
return (
<div>
<h1>Welcome to my SSR app</h1>
<p>This is the home page</p>
</div>
);
};
export default Home;
About.js
import React from 'react';
const About = () => {
return (
<div>
<h1>About us</h1>
<p>We are a team of developers who love building web apps with React and Node.js</p>
</div>
);
};
export default About;
Contact.js
import React from 'react';
const Contact = () => {
return (
<div>
<h1>Contact us</h1>
<p>You can reach us at contact@example.com</p>
</div>
);
};
export default Contact;
NotFound.js
import React from 'react';
const NotFound = () => {
return (
<div>
<h1>404 Not Found</h1>
<p>The page you're looking for doesn't exist</p>
</div>
);
};
export default NotFound;
In the code above, we're creating simple components for our app's pages. Each component contains some HTML markup that will be rendered by React.
Adding some styles
Let's add some styles to our app. Create a new directory called "public" in the root directory of your project and add a new file called "styles.css". Add the following code:
body {
font-family: Arial, sans-serif;
margin: 0;
}
ul {
list-style: none;
margin: 0;
padding: 0;
}
nav {
background-color: #333;
color: #fff;
}
nav ul {
display: flex;
justify-content: space-between;
}
nav li {
padding: 1rem;
}
nav a {
color: #fff;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
In the code above, we're defining some basic styles for our app. We're setting the font family, margin, and padding for the body element. We're also defining some styles for the navigation bar.
Running the app
To run the app, open your terminal, navigate to the root directory of your project, and run the following command:
npm start
This will start the development server. Open your browser and go to http://localhost:3000 to see the app in action.
You should see the home page of your app, which contains a welcome message. Click on the links in the navigation bar to see the other pages.
Building the app
Now that we've built our app, let's create a production-ready build. Run the following command in your terminal:
npm run build
This will create a new directory called "build" in the root directory of your project. This directory contains all the files needed to deploy your app.
Deploying the app
To deploy your app, you can use any hosting service that supports Node.js and React. One popular option is Heroku.
To deploy your app to Heroku, follow these steps:
Sign up for a Heroku account if you haven't already.
Install the Heroku CLI on your computer.
Open your terminal and navigate to the root directory of your project.
Run the following commands:
The first command logs you in to your Heroku account. The second command creates a new Heroku app. The third command deploys your app to Heroku.
heroku login
heroku create
git push heroku master
Once the deployment is complete, run the following command to open your app in your browser:
heroku open
Congratulations! You've successfully built and deployed a server-side-rendered React app with Node.js.
Conclusion
In this tutorial, we've learned how to build a server-side-rendered React app with Node.js. We've covered the basics of server-side rendering, and we've seen how to use the react-dom/server module to render our app on the server.
We've also seen how to use the express module to create a server that can serve our app's HTML and assets. We've used the react-router-dom module to define our app's routes, and we've created simple components for our app's pages.
Finally, we've learned how to deploy our app to Heroku and make it available to the world.
I hope this tutorial has been helpful for you. Server-side rendering can be a powerful tool for improving the performance and SEO of your React apps. If you have any questions or feedback, feel free to leave a comment below.