Hot reloading with React and Flask

Andrew Hyndman
5 min readMay 22, 2020

--

Introduction

A friend of mine recently set up a new project. Having positive experiences with both in the past, he chose Flask and React as the backbone of his stack. By serving the React build output as Flask static files, he was able to quickly get a production-ready application off the ground.

As the application grew though, compilation of the React App began to take more time, slowing his development progress. He reached out to me asking if I knew a good solution to restore hot reloading and speed up the feedback cycle for making React changes. This article shares the result we came up with.

If you would rather to skip to the code, I have published the resulting boilerplate project here: https://github.com/ajhyndman/flask-react

If you feel comfortable setting up a React and Flask application on your own, you may want to skip to: Support hot-reloading in development

Prerequisites

To run Flask and React, you’ll need Python, Node and a package manager for each language installed on your computer. For this article, I use (and recommend) the following:

Bootstrap your project

Create a new folder to hold your project.

I recommend going ahead and initialising a version control repository right away. As I write this, git is the most popular by far:

~/Projects/my-app $ git init

Flask

Using Poetry, you can create a new folder, Python virtualenv and project definition file with a single command.

~/Projects/my-app $ poetry new server

This should create the following folder structure for you.

server
├── pyproject.toml
├── README.rst
├── server
│ └── __init__.py
└── tests
├── __init__.py
└── test_server.py

Next, install Flask.

~/Projects/my-app/server $ poetry add flask

Define your Flask application.

# my-app/server/server/app.py
from flask import Flask
app = Flask(__name__)@app.route("/")
def getRoot():
return "Welcome!"

I recommend using a .flaskenv file to configure persistent environment variables, so that you don't need to remember to type them every time.

~/Projects/my-app/server $ poetry add python-dotenv# my-app/server/.flaskenv
FLASK_APP=server.app

Now you can start your Flask application with the flask CLI.

~/Projects/my-app/server $ poetry run flask run

In a web browser, open http://localhost:5000/, and you should see your brand new Flask-powered website.

A web browser opened to http://localhost:5000 rendering the text “Welcome!”

React

Use create-react-app to initialise a React application in the my-app/client folder:

~/Projects/my-app $ yarn create react-app client

You should immediately have a ready-to-launch React application.

~/Projects/my-app/client $ yarn start
A web browser opened to http://localhost:3000 rendering a sample React application.

Serve your React application from Flask

There are many solutions for serving static assets alongside a Flask application, but until your project needs more I think it’s a good idea to keep things simple.

Flask has out of the box support for serving static files from the /static folder, relative to app.py.

Update your React app’s build script to publish static files to this folder.

// my-app/client/package.json
{
// ...
"scripts": {
"build": "BUILD_PATH='../server/server/static' react-scripts build",
}
}

Now mount your React application at a new Flask route.

# my-app/server/server/app.py# ...@app.route("/app/", defaults={"path": "index.html"})
@app.route("/app/<path:path>")
def getApp(path):
return app.send_static_file(path)

Lastly, tell your React application about the public path you mounted it at.

// package.json
{
// ...
"homepage": "/app/"
}

Build and launch your application.

~/Projects/my-app/client $ yarn build~/Projects/my-app/server $ poetry run flask run

Now you should see your react application mounted at http://localhost:5000/app/

A web browser opened to http://localhost:5000/app/  rendering a sample React application.

Support hot-reloading in development

You’ve done it! Now you have a functional React application mounted at a Flask route. Everything you love about React and Flask are ready to go at your fingertips. But that’s not why you came here. Right now, every time you update any React code, you need to rebuild the application:

~/Projects/my-app/client $ yarn build

And every time you update any python files, you need to restart your flask server:

~/Projects/my-app/server $ poetry run flask run

You’ll never get anything done like that! Fortunately, with a little more configuration, we don’t have to compromise anything we’ve done to this point to get a hot-reloading workflow in development.

Enable Flask development mode

By default, flask run launches a production-friendly server process. However, you can opt into a hot-reloading debug mode by setting the FLASK_ENV environment variable.

# my-app/server/.flaskenv
FLASK_APP=server.app
FLASK_ENV=development

Now, changing any python source files will automatically restart the flask process.

Serve React Application from Webpack Dev Server

Create React App’s supported hot-reloading workflow is the yarn start script. This starts Webpack Dev Server, which serves static assets from localhost:3000. In Flask, during development, we can pass static asset requests on to Webpack Dev Server, using a simple reverse proxy implementation.

~/Projects/my-app/server $ poetry add requests# my-app/server/server/app.py
from os import environ
from flask import Flask, request
from requests import get
IS_DEV = environ["FLASK_ENV"] == "development"
WEBPACK_DEV_SERVER_HOST = "http://localhost:3000"
app = Flask(__name__)
def proxy(host, path):
response = get(f"{host}{path}")
excluded_headers = [
"content-encoding",
"content-length",
"transfer-encoding",
"connection",
]
headers = {
name: value
for name, value in response.raw.headers.items()
if name.lower() not in excluded_headers
}
return (response.content, response.status_code, headers)
@app.route("/app/", defaults={"path": "index.html"})
@app.route("/app/<path:path>")
def getApp(path):
if IS_DEV:
return proxy(WEBPACK_DEV_SERVER_HOST, request.path)
return app.send_static_file(path)

Since you’ll be serving your app via Flask, you can disable Webpack Dev Server’s default behaviour of launching a new browser tab.

# my-app/client/.env
BROWSER=none

Now if you start Webpack Dev Server alongside Flask in development mode, you should see your application restored at http://localhost:5000/app/.

~/Projects/my-app/client $ yarn start
~/Projects/my-app/server $ poetry run flask run

Bypass Flask with Webpack Dev Server’s socket connection.

You’ll notice that at this point one last issue remains. Flask is automatically restarting when you change a Python file. Webpack recompiles when you change a JavaScript file. But you still have to manually refresh your page to see those changes.

We can do better.

Webpack Dev Server opens a web socket connection with the browser which allows it to ping the browser when something has changed. Flask doesn’t support web socket connections, but that’s okay, because we can easily bypass Flask for this connection. We can tell Webpack to look for this connection on port 3000 instead of using the default window.location.host.

# my-app/client/.env
BROWSER=none
WDS_SOCKET_PORT=3000

Conclusion

And that’s it!

Now, with FLASK_ENV=production you have a deployable flask application serving statically built React assets, and with FLASK_ENV=development you have a modern, hot-reloading workspace that incrementally rebuilds changes to both your React and Flask applications.

--

--