How to Deploy a React App with a Node Backend

Using React with a Node backend is a popular choice that lets you use one language for both your client and server: JavaScript. In this tutorial, we will look into how you can build out your frontend using React, and connect it to your backend using Node.

For simplicity, we will keep everything in one project folder, but you can easily store them in different repositories, as we will separate the two with two different folders. In the end, we will deploy both React and Node to Heroku, which is a cloud application platform. With that being said, letโ€™s set up the project.

Remove ads

Setting up React with Node

The first step is to create a new NPM project by running npm init -y. Using the -y flag will automatically answer yes to any prompts by npm. Make sure you also init the project as a git repository, as we will need it to deploy to Heroku. Once your project is ready, we will need to install some dependencies:

npm i -D [email protected]
npm i express react react-dom

We are going to use Parcel as the bundler, and Express as the backend. Donโ€™t forget to also add react and react-dom as dependencies.

Creating the Backend with Express

First, letโ€™s set up the API. To keep Express separate from React, we can create two directories in the root folder called client and server. Add an index.js file into your server folder that will act as the entry point for Express:

const express = require('express'),
      path    = require('path'),
      route   = require('./route.js'),
      app     = express(),
      port    = process.env.PORT || 8080;
app.use(express.static(path.resolve(__dirname, '../client/build')));



console.log(`API server is listening on port:${port}`);
Copied to clipboard!

We only going to need 13 lines of code. Note that we are going to need to use a built-in middleware called express.static to serve static files. We want to reference the build folder of our React app that will be generated by Parcel. And we also have a route function Iโ€™m using for wrapping the Express app. This is for handling the different endpoints.

Create a route.js file next to your index.js file, and export the following function to expose a mock endpoint with some data that we can fetch:

const path = require('path');

module.exports = app => {
    app.get("/api", (req, res) => {
        res.json({ message: '๐Ÿ‘‹ from Express!' });
    app.get('*', (req, res) => {
        res.sendFile(path.resolve(__dirname, '../client/build', 'index.html'));
Copied to clipboard!

This will define an /api endpoint for us that returns with a message. In case we hit any other route, we can use a wildcard to return the static index.html file that will hold the React app. So to actually test this out, letโ€™s create a new command for it inside package.json:

"scripts": {
    "start": "node server/index.js"
package.jsonHeroku will use npm run start by default.
Copied to clipboard!

And run npm run start to start the Express server. If you visit localhost:8080/api you should see the message returned we have defined inside route.js.

The response from Express

Creating the Frontend with React

Now letโ€™s turn our attention to the UI. Inside your empty client folder, add an index.html. This is what we will serve from Express if we hit anything else, other than the /api endpoint:

<!DOCTYPE html>
    <html lang="en">
            <meta charset="UTF-8" />
            <meta http-equiv="X-UA-Compatible" content="IE=edge" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <title>โš›๏ธ React app with Node backend</title>
        <div id="root"></div>
        <script type="module" src="./index.tsx"></script>
Copied to clipboard!

Donโ€™t forget to create an index.tsx file next to it that is referenced in the HTML. Parcel will take care of the extensions, so we can safely use tsx inside the HTML file.

import React from 'react'
import { render } from 'react-dom'
import App from './src/app'

render(<App />, document.getElementById('root'));
Copied to clipboard!

As you can see, Iโ€™ve outsourced the root of the app into a src folder, so lets create that and see what we have:

import React from 'react'
import useFetch from './useFetch'

const App = () => {
    const [data, loading] = useFetch('/api');

    if (loading) {
        return <div>loading...</div>

    return data.message;

export default App;
Copied to clipboard!

First things first, we are using a custom hook, called useFetch. Internally, this is using useState and useEffect. If you would like to learn more about how useFetch works, you can refer to the tutorial below. Alternatively, you can also get the source code from GitHub.

How to fetch data with react hooks?

Secondly, we are hitting the /api endpoint without specifying the host. However, Express is running on port:8080, so we need a different port to run React. But if we use a different port, then we would need to use absolute paths. The problem with absolute paths, however, is that if we were to use localhost:xxxx/api, it would break on production. So how we can get around this?

Luckily, Parcel is capable of proxying requests. Add a .proxyrc.json file at the root of your project directory, and add the following:

    "/api": {
        "target": "http://localhost:8080/"
Copied to clipboard!

This will tell Parcel to forward the requests sent to /api to localhost:8080/api. The good things is, itโ€™s only used for development. Once we deploy the app, both React and Express will live on the same server, so the relative path will work fine.

If you donโ€™t want to use Parcel, you can proxy requests in webpack or CRA, and most probably in your other choice of build tool too.

There is only one thing left to do, and that is to add some more scripts to package.json so we can start and build our React application:

"scripts": {
    "start:react": "parcel serve client/index.html --open --dist-dir client/build",
    "start": "node server/index.js",
    "build": "parcel build client/index.html --dist-dir client/build"
Copied to clipboard!

Since we already have a start script for Express, we can namespace the command using double-colon. Also, make sure you define the distribution directory as client/build, as that is what we were referencing inside Express. And be sure to name your build command as build, as that is what Heroku will look for by default. You can change this behavior with a heroku-postbuild command. Now you can also add this to your .gitignore.

# dev dependencies

# builds
Copied to clipboard!

Deploying to Heroku

Thereโ€™s one last thing to do, and that is to deploy the whole application to Heroku. In order to do that, you will need to create an account first. Once that is ready, you can access your Heroku dashboard. Inside your dashboard, you can create apps by clicking on the โ€œNewโ€ button and selecting โ€œCreate new appโ€. The app for your name has to be unique.

Creating a new app in Heroku

Once your app is ready, you can go to the โ€œDeployโ€ tab, where you will get clear instructions on how to deploy to Heroku.

The deploy tab on Heroku

First, you will need to install the Heroku CLI, so you can give Heroku-specific commands in your terminal. Once that is done, login to Heroku using heroku login inside your terminal. Lastly, you can push your changes by simply saying:

git push heroku master

Before you do that, however, make sure you commit all changes. Once the deployment is ready, you will get the URL for your app in your terminal that you can visit to verify if the app is working.

React app with Node backend deployed on Heroku
The Heroku app serves React, and if you visit /api you can see the response.

If there is any problem that you need to debug on Heroku directly, you can quickly access the log files which will log everything that happens with your app. Here you should be able to see errors, just like you would get in your terminal during local development.

The logs inside Heroku


And now you have your React application with a node backend up and ready. For new changes to deploy, you just have to trigger a push to Heroku, but this step can also be automated. You can easily integrate it with GitHub through your โ€œDeployโ€ tab, it also comes with its own CI, or you can simply do a push to the git endpoint from your CI script:

gitย pushย https://heroku:[email protected]/$HEROKU_APP_NAME.gitย HEAD

Lastly, if you would like to get the source code in one piece, you can clone it from GitHub. Thank you for reading through, happy coding!

Remove ads
Remove ads
Remove ads

๐Ÿ“š Get access to exclusive content

Want to get access to exclusive content? Support webtips with the price of a coffee to get access to tips, checklists, cheatsheets, and much more. โ˜•

Get access Support us
Remove ads
Remove ads
Remove ads
๐ŸŽ‰ Thank you for subscribing to our newsletter. x