Create React App and Django
Posted by gavin on April 10, 2017, 5:02 p.m.
To start, generate a Django project:
$ pip install django $ django-admin startproject myapp $ cd myapp
Then generate the react app in a subdirectory:
$ yarn global add create-react-app $ create-react-app frontend
Now we have a Django project with a React app in the
To make them work together, we need to figure out how to let the React app talk
to Django over an API, and how to serve the files for the React app itself in a
way that makes sense within a Django project.
For development, will have to run two different servers -- one for Django, and
one for React. Add
package.json (within the frontend directory). This will proxy
requests from the React app to Django's runserver. Then start both servers in
./manage.py runserver cd frontend && yarn start
Access the frontend like you do in a regular create-react-app project, at
http://localhost:3000. Any API requests the frontend makes to
http://localhost:3000 will get proxied to Django. This works as long as the
frontend uses only relative URLS, and doesn't follow links provided by the
backend (Common with HATEOAS and Django Rest Framework (DRF)). If the frontend code
does follow API links, they will be directly to runserver
http://localhost:8000), making them cross-origin requests. To make this work
we'll need a way to add CORS headers only in local development.
We can do this by writing a middleware. In
1 2 3 4 5 6 7 8 9 10 11 12 13 14
def dev_cors_middleware(get_response): """ Adds CORS headers for local testing only to allow the frontend, which is served on localhost:3000, to access the API, which is served on localhost:8000. """ def middleware(request): response = get_response(request) response['Access-Control-Allow-Origin'] = 'http://localhost:3000' response['Access-Control-Allow-Methods'] = 'GET, POST, PUT, PATCH, OPTIONS, DELETE, HEAD' response['Access-Control-Allow-Headers'] = 'Content-Type, X-CSRFToken' response['Access-Control-Allow-Credentials'] = 'true' return response return middleware
Make sure this is only used for local development. I do this by manipulating
the value of
MIDDLEWARE in my
Now the frontend will be able to make requests to both
http://localhost:8000, so the links generated by DRF will work. In
production, we'll use a different solution that puts the React app and the API
on the same domain.
In production, we can't use two different URLs and run the yarn server. We will need to run the create-react-build process to create a production build, then serve that through Django in order to get everything onto the same domain.
yarn run build outputs its build artifacts into
frontend/build/. We can
configure Django's staticfiles to serve the JS and CSS from create-react-app's
build with these settings:
1 2 3 4 5
REACT_APP_DIR = os.path.join(BASE_DIR, 'frontend') STATICFILES_DIRS = [ os.path.join(REACT_APP_DIR, 'build', 'static'), ]
Now collectstatic will automatically find the static build artifacts, and
deploy them however you have configured staticfiles. The only thing left is to
serve the entry point to the React app,
index.html. We can't use staticfiles
for this because it needs to be served at the root of our site, not under
We can do this with a Django view, in
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
import logging from django.views.generic import View from django.http import HttpResponse from django.conf import settings class FrontendAppView(View): """ Serves the compiled frontend entry point (only works if you have run `yarn run build`). """ def get(self, request): try: with open(os.path.join(settings.REACT_APP_DIR, 'build', 'index.html')) as f: return HttpResponse(f.read()) except FileNotFoundError: logging.exception('Production build of app not found') return HttpResponse( """ This URL is only used when you have built the production version of the app. Visit http://localhost:3000/ instead, or run `yarn run build` to test the production version. """, status=501, )
This view must be installed with a catch-all urlpattern in order for pushState
routing to work. In
1 2 3 4 5 6 7 8
from django.conf.urls import url from . import views urlpatterns = [ # ... the rest of the urlpatterns ... # must be catch-all for pushState to work url(r'^', views.FrontendAppView.as_view()), ]
Now once we configure out deployment process to run the create-react-app build script, users who visit our site will be served the React app. Other Django URLs like the API and admin will still continue to work, as long as their urlpatterns come before the catch all.
Fusionbox is a world-class custom Django development company with hundreds of Django applications under our belt. We've also contributed to the Django Core. Contact us today to learn how we can help you with your Django project.