In this tutorial, we will learn how to use react with Django. We will cover the basics of setting up a build tool, structuring the project, working with the backend API, and more.
This article needs updating but the content is still relevant.
I now use exclusively use
Django + InertiaJS.
LEVEL - Intermediate-Advanced. This will not be a step-by-step tutorial but we will cover the main concepts. The tutorial assumes knowledge of Django and React.
The complete code for this
[
tutorial can be found here
](https://github.com/advantch/django-react-vite/tree/django-react-vite-tutorial)
.
Django react
starter project -
[
download from this link
](https://github.com/advantch/simple-django-starter)
¶
What are we building
We will build a chart component that fetches external sensor data from the backend and displays it on the frontend. We will be using React for the chart component and
[
Vite
](https://vitejs.dev/)
as our frontend build tool. Vite provides fast dev server, JSX support out of the box, and a bunch of other cool features. Check out their docs for more info.
¶
Core concepts
Javascript build tools find, process, and combine javascript source modules into files that can run in the browser. Popular tools include
[
webpack
](https://webpack.js.org/)
,
[
Rollup
](https://rollupjs.org/)
,
[
snowpack
](https://www.snowpack.dev/)
,
[
Parcel
](https://parceljs.org/)
, and
[
Vite
](https://vitejs.dev/guide/features.html)
. Choosing the right tool is a matter of personal preference. In this tutorial, we will be using Vite and the
[
django_vite
](https://github.com/MrBin99/django-vite)
library.
The django_vite library provides a set of templatetags for serving Vite assets in Django HTML templates. We will revisit this later when we set up the template.
Vite consists of two major parts.
a dev server that provides features such as fast hot module replacement (HMR)
a build command which uses Rollup, pre-configured to bundle highly optimized production assets.
Notes to explain the above
the green part - this is the standard Django setup. When you run python manage.py runserver 8000 the Django dev server & the staticfiles app will serve your HTML & static files to the browser. In this example,
js/site.js
will be served provided to your browser from
http://localhost:8000/static/
the orange section - the Vite dev server will pre-bundle and serve your source files to your browser
¶
Set up the project
In this section, we will clone the repository, and organize our project folder structure and frontend tooling.
¶
Setup our development environment
Clone the repo to your local machine and follow the instructions in
README.md
on how to set up your local development environment.
*
Clone the project
*
text
`mkdir django_react
cd django_react
git clone https://github.com/advantch/django-react-vite .
`
*
Create virtual env and install python dependencies
*
text
`python3 -m venv .venv
source ./.venv/bin/activate
pip install -r requirements.txt`
¶
Project folder/file structure
text
`oject folder/file structure
├─django_react
| └── config #
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ ├── asgi.py
│ ├── api.py
│ └── wsgi.py
| └── apps # All apps in this folder
├─── templates
| ├── base.html
| ├── home.html
| └── dashboard
| ├── components
| ├── layout.html
| ├── home.html
| └── components
| ├── header.html
| ├── logo.html
├──── static -> static assets
| ├── js
| ├── css
| ├── dist -> frontend build assets go here
| └── images
├──── assets # frontend assets
| ├── js
| ├── css
├──── vite.config.js # vite config
├──── package.json # node
├──── assets #
└─── manage.py`
Notes
assets folder
- our frontend javascript assets go here. Keep this separate from the project's static files.
vite.config.js
-configuration for Vitejs
package.json
- metadata about the project
¶
Configure Vite & django_vite
There is additional configuration required before we can use the django_vite library. This is already included in the starter repository. We will review the important sections below.
python
`INSTALLED_APPS = [
"django.contrib.admin",
......
"django_vite"
]
# STATIC
STATIC_URL = "/static/"
STATICFILES_FINDERS = [
"django.contrib.staticfiles.finders.FileSystemFinder",
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
]
# Where ViteJS production assets are built.
DJANGO_VITE_ASSETS_PATH = BASE_DIR / "static" / "dist"
# use HMR or not.
DJANGO_VITE_DEV_MODE = DEBUG
# Name of static files folder (called by python manage.py collectstatic)
STATIC_ROOT = "staticfiles"
# Include DJANGO_VITE_ASSETS_PATH in STATICFILES_DIRS list
STATICFILES_DIRS = [
str(BASE_DIR / "static"),DJANGO_VITE_ASSETS_PATH
]`
*
Notes
*
INSTALLED_APPS
- include django_vite in your installed apps.
DJANGO_VITE_ASSETS_PATH
- the is the destination folder for production assets
DJANGO_VITE_DEV_MODE
- this tells the library to either let the Vite client-server serve your assets with HMR enabled or serve production assets from the
static/dist
folder.
STATICFILES_DIRS
- Includes
DJANGO_VITE_ASSETS_PATH.
Instructs Django staticfiles app to also check this folder when searching for static files.
Next, we will look at the vite.config.js file. This is where we include settings for the dev server as well as configuration for the build command
javascript
`const { resolve } = require('path');
import react from '@vitejs/plugin-react'
module.exports = {
plugins: [react()],
root: resolve('./assets'),
base: '/static/',
server: {
host: 'localhost',
port: 3000,
open: false,
watch: {
usePolling: true,
disableGlobbing: false,
},
},
resolve: {
extensions: ['.js', '.json', '.jsx', '.ts', '.tsx'],
},
build: {
outDir: resolve('./static/dist'),
assetsDir: '',
manifest: true,
emptyOutDir: true,
target: 'es2015',
rollupOptions: {
input: {
main: resolve('./assets/js/main.jsx'),
}
},
},
};`
*
Notes
*
The config file looks daunting, but it is quite straightforward once you understand the different config options. We suggest you read the
[
Vite
](https://vitejs.dev/guide/)
config reference for a comprehensive overview. To summarise the relevant bits:
plugins:[react()]
- the react plugin to be used in the project. Refer to the docs for more information on plugins.
root
- project root
base
- base url where the static assets will be served from during development.
server
- settings for the dev server are included here, including port, host
resolve
- list of file extensions to try for imports
build.outDir
- specifies the output directory for your build files.
build.rollupOptions.input.main
- the bundle's entry points (e.g. main.jsx), if an array is provided the output will be mapped to separate chunks. See the
[
rollup.js docs
](https://rollupjs.org/guide/en/#big-list-of-options)
for more info.
text
` .....
"scripts": {
"dev": "vite",
"build": "vite build"
},
...`
Notes
"scripts"
- running
npm run dev
will start the dev server. When you want to build your production assets run
npm run build
¶
Django html templates & URL configuration
python
`urlpatterns = [
....
path("vite/", TemplateView.as_view(template_name="vite.html"), name="vite"),
...
]
`
Notes
This will be our React view. We are using the built-in Django
TemplateView
class for the view with
vite.html
as the template.
The project includes a base template that has HTML/CSS/JS that is reused in all site pages
html
`{% load static i18n %}
<!DOCTYPE html>
{% get_current_language as language_code %}
<html lang="{{ language_code }}">
<head>
<link href="https://cdn.jsdelivr.net/npm/daisyui@1.16.2/dist/full.css" rel="stylesheet" type="text/css" />
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2/dist/tailwind.min.css" rel="stylesheet" type="text/css" />
</head>
<body>
....
{% block footer_js %}
<\script type="text/javascript" src=\"{% static 'js/cookie.js' %}"></script>
<\script src="https://unpkg.com/swagger-client"><\/script>
<\script>
const csrftoken = getCookie('csrftoken');
const authHeaders = {'X-CSRFToken': csrftoken}
// initialise swaggerjs client to interact with the api
const client = SwaggerClient('/api/openapi.json', {
requestInterceptor: (req) => {
req.headers['X-CSRFToken'] = csrftoken;
req.headers['Content-Type'] = 'application/json';
return req;
},
})
// attach to window object for easy access from any component
window.client = client;
<\/script>
{% endblock %}
</body>
</html>`
Notes
const client - SwaggerClient('/api/openapi.json/',
- set up a swagger-js client and attach it to the window object.
if django authentication is enabled, the client can use SessionAuthentication to interact with the API. This means you use the login_required decorator or LoginRequiredMixin in your react view on the backend to only allow authenticated users to access the view. You can then call
window.client.then(client => client.apis.charts.getSensorData()
directly from your react component without worrying about authenticating with tokens.
Almost done. Let’s look at the template we will use for our react view.
text
`{% extends 'base.html' %}{% load django_vite %}
{% block title %}Vite React Echarts Example{% endblock %}
{% block head_js %}
<\script type="module">
import RefreshRuntime from 'http://localhost:3000/@react-refresh'
RefreshRuntime.injectIntoGlobalHook(window)
window.$RefreshReg$ = () => {}
window.$RefreshSig$ = () => (type) => type
window.__vite_plugin_react_preamble_installed__ = true
<\/script>
{% vite_hmr_client %}
{% endblock %}
{% block content %}
{% include "components/nav.html" with btn_text="connected" %}
<section class="container mx-auto pt-12 py-8 px-4 lg:px-6 xl:px-12">
<div class="flex flex-row p-10">
<div id="chart-container" class="w-full md:w-1/2 h-96"></div
<div id="controls-container" class="w-full md:w-1/2 h-96"></div
</div
</section>
{% endblock %}
{% block footer_js %}
{% vite_asset 'js/main.jsx' %}
{{block.super}}
{% endblock %}`
Notes
{% extends 'base.html' %}
- extends the base.html template provided earlier
{% load django_vite %}
- loads django_vite templatetags, you will need this on any template where you will be using django_vite tags
{% vite_hmr_client %}
- this template tag generates a script tag for the
[
HMR Vite client
](https://vitejs.dev/guide/features.html#hot-module-replacement)
which will watch your assets for changes and reload them in place. When using react, you will need to include the script snippet
import RefreshRuntime..
at the top. See more on that
[
here
](https://vitejs.dev/guide/backend-integration.html)
.
<div id="chart-container" ..>
- the container where our chart component will be rendered
{% vite_asset 'js/main.jsx' %}
- the entry point file contains the main logic for the module. You will note that the path is set as
'js/main.jsx'
and not
'static/src/js/main.jsx'
. This is because the template tag will load the file from the Vite project root which is set in vite.config.js.
¶
Add a backend endpoint for sensor data
Our front end will fetch sensor data from the backend. Since we don’t have an actual sensor, we will mock this in our backend. Each request to this endpoint will return a unique value. We are using the Django ninja library to quickly build an API. You could also use the django-rest-framework library.
python
`from random import randint
from ninja import NinjaAPI
api = NinjaAPI(csrf=True)
@api.get("/chartData",operation_id="getSensorData", tags=["charts"])
def get_chart_data(request):
"""Mock sensor data for frontend"""
chart_values = [randint(300, 1000) for i in range(7)]
return {"data": chart_values}
`
Include the API urls.
python
`....
from .api import api
urlpatterns = [
....
path("api/", api.urls), # include api urls
]
`
Notes
Here we added the API endpoint and updated the URL config.
api = NinjaAPI(csrf=True)
- this endpoint will only allow requests with a valid CSRF token.
authentication - you can visit the
[
django-ninja
](https://django-ninja.rest-framework.com/tutorial/authentication/)
docs on how to implement authentication for the API.
¶
Building the React Chart Component
We are now ready to build the react component. We will be using the beautiful
[
Apache Echarts
](https://echarts.apache.org/handbook/en/get-started/)
library along with the react
[
echarts-for-react
](https://www.npmjs.com/package/echarts-for-react)
wrapper to render our charts.
¶
Install dependencies and add chart options.
text
`npm install --save echarts-for-react echarts`
javascript
`const CHART_OPTIONS = {
title: {
text: "Factory Sensor Data",
},
toolbox: {
show: true,
feature: {
dataView: { readOnly: false },
restore: {},
saveAsImage: {},
},
},
grid: {
top: 60,
left: 80,
right: 60,
bottom: 45,
},
xAxis: {
type: "category",
name: "Factory area",
nameLocation: "middle",
nameTextStyle: {
verticalAlign: "middle",
fontWeight: "bold",
},
nameGap: 35,
bondaryGap: true,
data: [
"lobby",
"hallway",
"office",
"floor",
"roof",
"storage",
"grounds",
],
},
yAxis: {
type: "value",
name: "kwh",
nameGap: 45,
nameLocation: "middle",
nameTextStyle: {
verticalAlign: "middle",
fontWeight: "bold",
},
max: 1200,
min: 300,
},
series: [
{
data: [820, 932, 901, 934, 1290, 1233, 1320],
type: "bar",
smooth: true,
},
],
tooltip: {
trigger: "axis",
},
};
export default CHART_OPTIONS;
`
Notes
standard echarts configuration,
[
see the docs
](https://echarts.apache.org/en/api.html#echarts)
for more info
¶
Create the Chart Component
The chart component is a simple react component that will fetch live sensor data from the backend every 500 milliseconds. If you need a refresher on react
[
visit the docs
](https://reactjs.org/docs/getting-started.html)
.
javascript
`import React from 'react';
import ReactECharts from 'echarts-for-react';
import CHART_OPTIONS from './options.js';
class Chart extends React.Component {
constructor(props) {
super(props);
this.state = {
chartData: CHART_OPTIONS,
autoRefresh: true
}
}
async refreshOnInterval() {
setInterval(()=> this.state.autoRefresh ? this.refreshOptions() : null, 500);
}
toggleAutoRefresh() {
this.setState({autoRefresh: !this.state.autoRefresh});
}
async fetchbackendChartData(){
return await window.client.then(client => client.apis.charts.getSensorData()
.then(res => res.obj.data)
.catch(err => console.error(err))
)
}
async refreshOptions() {
let chartOptions = JSON.parse(JSON.stringify(CHART_OPTIONS));
let data = await this.fetchbackendChartData();
chartOptions.series[0].data = data;
this.setState({chartData: chartOptions});
}
componentDidMount() {
this.refreshOnInterval();
}
render() {
return <div>
<ReactECharts option={this.state.chartData} />
<div className="flex">
<div className="p-4 card bordered">
<div className="form-control">
<label className="cursor-pointer label">
<span className="label-text mr-2">Show Live Data</span>
<input onChange={()=>this.toggleAutoRefresh()}
value={this.state.autoRefresh}
checked={this.state.autoRefresh} type="checkbox"
className="toggle toggle-primary toggle-sm"/>
</label>
</div>
</div>
<div className="btn btn-primary btn-sm mt-5" disabled={this.state.autoRefresh} onClick={()=> this.refreshOptions()}>Refresh</div>
</div>
</div>;
}
};
export default Chart;
`
Notes
this.state
- initialize component state with chart options and
autoRefresh
boolean
fetchBackendChartData
- retrieve backend data from the API. You will note that we are using the swagger.js client we declared and attached to the window object in templates/base.html
refreshOnInterval
- if autoRefresh is set to true, fetch new chart data every 500milliseconds
toggleAutoRefresh
- boolean value for whether the component should show live data
refreshOptions
- update chart options data based on values from the backend
className
attributes - we are using tailwindcss & daisyui attributes here. The stylesheets are included in the head section of
templates/base.html
.
Add the Component to our entry point file
javascript
`import React from 'react';
import ReactDOM from "react-dom";
import Chart from './components/chart.jsx'
const domContainer = document.querySelector('#chart-container');
ReactDOM.render(<Chart/>, document.getElementById('chart-container'));`
Notes
ReactDOM.render(<Chart/>
- this will render the react
Chart
element into the DOM in the provided container, i.e. element with id
chart-container
and mount the Chart component.
¶
Run the app
To run the app you will need to run both the django development server and the vite dev server.
text
`$ python manage.py runserver
$ npm run dev # in another terminal
`
Go to http://localhost:8000/vite/ and your app should running
¶
Conclusion
That’s it, in this post we have learned how to include a react app in a Django project. We covered the basics of structuring our codebase and setting up a build tool for the project.
¶
A note on deploying to production
When deploying to production, run
npm run build
to compile your production assets. In this example, we used a CDN for our TailwindCSS and DaisyUI stylesheets. In production, you can also use Vite to minify your production CSS assets. Visit the tailwind docs for how to
[
install and configure
](https://tailwindcss.com/docs/installation)
tailwind for use with a build tool.
¶
Additional Reading material
[
ViteJS docs
](https://vitejs.dev/)
- ViteJS fundamentals and api reference
[
Rollup.js docs
](https://rollupjs.org/guide/en/#plugins-overview)
- ViteJS uses rollup for bundling assets.
[
Javascript modules(MDN)
](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules)
- What is ESM, CJS, AMD?
[
Javascript modules(Article)
](https://dev.to/iggredible/what-the-heck-are-cjs-amd-umd-and-esm-ikm)
- A good summary on javascript modules
