Deploying an Express / React app with Now

Use Parcel to build your React app, Express to serve it and Now to deploy

Profile photo

by Mark MurrayFebruary 28, 20194 min read

(View the full source code here)

Historical note (August 2026): Zeit is now Vercel, Now 2.0 and the @now/* builders are retired, and Parcel 1's parcel-bundler package is deprecated. The deployment configuration remains below for historical context; use the current Vercel documentation and Parcel 2 for a new application.

This post shows how an Express server can serve a React frontend built with Parcel and be deployed with the former Now platform.

Create the project structure

Get started by creating the project directory, initialising a new Node project and installing required dependencies.

# Create new directory
mkdir my-project
cd my-project

# Create new node project
yarn init

# Install dependencies
yarn add express react react-dom

# Install devDependencies
yarn add --dev parcel-bundler rimraf

# Install now-cli globally
yarn global add now

The intended project structure we're going to create looks like this:

my-project
├── server
│   ├── dev-server.js
│   └── index.js
├── src
│   ├── index.css
│   ├── index.html
│   └── index.js
├── now.json
├── package.json
├── README.md
└── yarn.lock

Use the following to create a standard .gitignore file for a node project and create the directory structure.

# Add a gitignore to your project
npx gitignore node

# Create server directory
mkdir server
touch server/index.js
touch server/dev-server.js

# Create client directory
mkdir src
touch src/index.js
touch src/index.html
touch src/index.css

Server-side

// server/index.js
const path = require('path');
const express = require('express');

const app = express();
const port = process.env.PORT || 8080;
const dist = path.resolve(__dirname, '..', 'dist');

app.get('/api/healthcheck', (req, res) => {
  return res.send('Healthy!');
});

if (process.env.NODE_ENV === 'production') {
  app.use('/static', express.static(dist));
  app.get('*', (req, res) => res.sendFile(path.join(dist, 'index.html')));
}

if (require.main === module) {
  app.listen(port, () => {
    console.log(`Listening at http://localhost:${port}`);
  });
}

module.exports = app;
// dev-server.js
const path = require('path');
const Bundler = require('parcel-bundler');
const app = require('.');

const entry = path.resolve('src/index.html');
const bundle = new Bundler(entry);

app.use(bundle.middleware());

app.listen(process.env.PORT || 8080, (err) => {
  if (err) throw err;

  console.log(`Listening at http://localhost:${process.env.PORT}`);
});

Client-side

src/index.html

<!DOCTYPE html>
<html>
  <head>
    <title>Express with Parcel</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="./index.js"></script>
  </body>
</html>

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';

import './index.css';

const App = () => (
  <div className="app">
    <h1>Express / React / Parcel / Now</h1>
  </div>
);

ReactDOM.render(<App />, document.querySelector('#root'));

src/index.css

html,
body {
  margin: 0;
  padding: 0;
  background: #1d1d35;
  color: white;
  display: flex;
  flex-direction: column;
  height: 100vh;
  text-align: center;
  justify-content: center;
  -webkit-font-smoothing: antialiased;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
    Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}

Add scripts

Add dev script to run the app in development mode. We'll run it using yarn dev.

{
  "scripts": {
    "dev": "NODE_ENV=development PORT=8080 node server/dev-server.js"
  }
}

Next, we'll add a start script to run the app in production mode.

{
  "scripts": {
    "dev": "NODE_ENV=development PORT=8080 node server/dev-server.js",
    "start": "NODE_ENV=production node server/index.js"  }
}

And finally we'll add a build script to build a production version of our client side code and output it to a dist folder. The production server will send the dist/index.html file for all incoming requests, with the exception of API requests.

{
  "scripts": {
    "build": "rimraf dist && NODE_ENV=production parcel build src/index.html --public-url=/static",    "now-build": "yarn build",    "dev": "NODE_ENV=development PORT=8080 node server/dev-server.js",
    "start": "NODE_ENV=production node server/index.js"
  }
}

Deploy your app

The original project used Now 2.0 by Zeit to deploy.

Create a now.json file at the root of your project and add the following:

{
  "version": 2,
  "name": "my-project",
  "routes": [
    { "src": "/static/(.*)", "dest": "/dist/$1" },
    { "src": "/(.*)", "dest": "/server/index.js" }
  ],
  "builds": [
    { "src": "dist/*", "use": "@now/static" },
    { "src": "server/index.js", "use": "@now/node" }
  ]
}
Copyright © Mark Murray, 2026