Serving static React.js content with Django (part 1)

Vitaly Belkin
6 min readMar 13, 2021

This is going to be a series of posts which will ultimately end up in deploying a containerised Django/React.js app into a Digital Ocean Kubernetes cluster.

Disclaimer:
By NO MEANS this is a complete guide to creating a top-notch production-ready application that would serve a heavy-load traffic, I’m going to try to cover all the basics and share my own knowledge and discoveries I’ve made on my journey to deploying a small SPA app with Django & nginx into a K8s cluster

Photo by Milada Vigerova on Unsplash

Preface

I had a relatively simple SPA written with react.js, it was deployed on Netlify. At the time I’ve had no problems with that setup, but then I started thinking about new features to introduce by implementing backend functionality, and the thought of having a backend working on a different server (afaik Netlify doesn’t support Django out of the box) for such a small app as mine was daunting, not to mention all the routing problems with having it all under one domain name.
I’ve decided that my SPA didn’t need a whole server to host few of it’s static files and thought of using Django to serve them (which is kind of an oversimplification but we’ll get to it). I have also wanted to try out Kubernetes and see if I could create a working setup for my app with it. Skipping forward, now I have a Digital Ocean K8's cluster that has two replicas of two pods each, one of the pods is running a dockerized nginx web-server and the other is a dockerized Django REST app, both of ’em having a shared volume space for static content.

reject create-react-app, embrace webpack

There are some complications that come with creating a Django/React fusion. Originally I’ve had my simple app use create-react-app package, but that takes care of too many things for you. That might be okay if you are, like I had it in the beginning, lacking any sort of backend functionality whatsoever. Django, on the other end, has a weird way of dealing with static files (and simply doesn’t understand javascript), and having no control over the react build bundle imposes too many constraints on your final setup, especially if you are using PWA features.

For the sake of my own sanity, I’ve rewritten my app without create-react-app and done my own webpack configs. I am explicitly stating that as I have no idea if your own setup will work with this tutorial if you are using c-r-a. If you know how to make Django work without ejecting react app, please let me know, I’m keen to find out!

creating a simple django app

make a virtual environment & activate it

D:\>python -m venv tutorialenvD:\>tutorialenv\Scripts\activate

Then do pip install django and pip install djangorestframework

Now create a new folder wherever and call it whatever, mine is going to be tutorial , then inside that folder do django-admin startproject djangoapp . (note the trailing dot).

Run python manage.py migrate for the first time - this will create a SQLITE3 database file, since that is what Django’s stock db engine is. After that try python manage.py runserver and you should see something like this in your browser:

Hit ctrl + c to bring the server down and we’re done here for now.

create react app without create-react-app

Navigate outside of your Django folder and create a new one wherever and call it whatever, I’ll call it tutorialreact

Inside that folder do npm init -y that should give you a json output in the command line, this is going to be your package.json

You can put together any code you like, but I’m just going to keep it simple and create an src folder with App.js and index.js files. First file will look like this:

Second file will look like this:

You will also need to add some dev-dependencies to the package.json file and any other packages you might require, so in the end it should look something like this:

Now create a webpack.config.js file at the root of your tutorialreact folder. Since we’re not using create-react-app here, we’ll throw together a simple config ourselves to suit our needs (or you can do whatever you require for your setup):

Sometimes you would have an extra plugin there to generate an index.html template, but working with Django we’re just going to write one later and keep it forever. For the same reason, we’re also omitting a start script in the package.json since we’re only generating .js output.

Then create a .babelrc file and type the following:

{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}

At this point this should be your react folder structure:

Now run npm run build and this should generate a new dist/static/js path with a javascript file in it, we will get back to it later.

Creating an html template

Get back to the Django folder and create a templates directory at the root of the project. Let’s put a dead simple index.html inside of it:

Now we have to tell Django that we have a template we want it to use. Create another directory at root and call it apps , inside create another folder called indexview (don’t forget the __init__.py files). Here, create a file called apps.py with the following:

Then create another folder inside the indexview and call it views as we want Django to treat index.html as a view. There, create file index_html_view.py that will be returning our html template:

Now, update INSTALLED_APPS in settings.py with the indexview app we’ve just created:

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'apps.indexview',
]

Also, you can edit TEMPLATES list to include the templates directory we’ve created:

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR, 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]

This is not required and you might try and leave the ‘DIRS’ populated with BASE_DIR.

Finally, go to urls.py and add our new route to urlpatterns :

At this point, when you do python manage.py runserver and open your browser, you should see the contents of our tiny html page instead of Django’s welcome template.

Making Django serve React app

We have our react app compiled down to a single js file, and can simply link it in our html template, just as we would with any other react app. Create a new directory build in the root of your Django project folder, then create another folder inside it called js and paste the index_bundle.js we got from running npm run build

Now, edit index.html so it can display the contents of our js file instead of what was there previously:

Because index_bundle.js is treated as a static file, we need to tell Django where it is located so it can respond to its requests. For that we need to add the following to the settings.py :

STATICFILES_DIRS = [
BASE_DIR / "build",
]

Run python manage.py runserver again and you should see this in your browser:

Now, the catch here is that Django is being generous and serving static files for us. You can try and link an img tag into your index.html and drop the picture into the build folder and it will work, but as soon as you go to settings.py and change DEBUG to False, everything falls apart, because in production you would have a reverse-proxy sitting in front of your backend and serving everything that’s not related to Django’s logic.

In part 2 I’ll describe how to dockerize what we’ve just done and add an nginx web-server to the setup.

--

--