BlogEngineering

Engineering

How to use React in Django Templates

Themba Mahlangu · 12 min read

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

[

Code
tutorial can be found here

](https://github.com/advantch/django-react-vite/tree/django-react-vite-tutorial)

.

Django react

starter project -

[

Code
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

[

Code
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

[

Code
webpack

](https://webpack.js.org/)

,

[

Code
Rollup

](https://rollupjs.org/)

,

[

Code
snowpack

](https://www.snowpack.dev/)

,

[

Code
Parcel

](https://parceljs.org/)

, and

[

Code
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

[

Code
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.

Code
 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

Code
 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

Code
 will be served provided to your browser from

http://localhost:8000/static/

Code
 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
*

Code
 text
Code
`mkdir django_react
cd django_react

git clone https://github.com/advantch/django-react-vite .
`

* Create virtual env and install python dependencies
*

Code
 text
Code
`python3 -m venv .venv
source ./.venv/bin/activate

pip install -r requirements.txt`

Project folder/file structure

Code
 text
Code
`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

Code
 - our frontend javascript assets go here. Keep this separate from the project's static files.

vite.config.js

Code
 -configuration for Vitejs

package.json

Code
 - 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.

Code
 python
Code
`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

Code
 - include django_vite in your installed apps.

DJANGO_VITE_ASSETS_PATH

Code
 - the is the destination folder for production assets

DJANGO_VITE_DEV_MODE

Code
 - 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

Code
 folder.

STATICFILES_DIRS

Code
 - Includes

DJANGO_VITE_ASSETS_PATH.

Code
 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

Code
 javascript
Code
`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

[

Code
Vite

](https://vitejs.dev/guide/)

config reference for a comprehensive overview. To summarise the relevant bits:

plugins:[react()]

Code
 - the react plugin to be used in the project. Refer to the docs for more information on plugins.

root

Code
 - project root

base

Code
 - base url where the static assets will be served from during development.

server

Code
 - settings for the dev server are included here, including port, host

resolve

Code
 - list of file extensions to try for imports

build.outDir

Code
 - specifies the output directory for your build files.

build.rollupOptions.input.main

Code
 - the bundle's entry points (e.g. main.jsx), if an array is provided the output will be mapped to separate chunks. See the

[

Code
  rollup.js docs

](https://rollupjs.org/guide/en/#big-list-of-options)

Code
 for more info.

 text
Code
` .....
"scripts": {
    "dev": "vite",
    "build": "vite build"
  },
...`


Notes

"scripts"

Code
 - running

npm run dev

Code
 will start the dev server. When you want to build your production assets run

npm run build

Django html templates & URL configuration

Code
 python
Code
`urlpatterns = [
    ....
    path("vite/", TemplateView.as_view(template_name="vite.html"), name="vite"),
    ...
]
`


Notes

Code
 This will be our React view. We are using the built-in Django

TemplateView

Code
 class for the view with

vite.html

Code
 as the template.

The project includes a base template that has HTML/CSS/JS that is reused in all site pages

Code
 html
Code
`{% 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/',

Code
 - 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()

Code
 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.

Code
 text
Code
`{% 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' %}

Code
 - extends the base.html template provided earlier

{% load django_vite %}

Code
 - loads django_vite templatetags, you will need this on any template where you will be using django_vite tags

{% vite_hmr_client %}

Code
 - this template tag generates a script tag for the

[

Code
  HMR Vite client

](https://vitejs.dev/guide/features.html#hot-module-replacement)

Code
 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..

Code
 at the top. See more on that

[

Code
  here

](https://vitejs.dev/guide/backend-integration.html)

Code
 .

<div id="chart-container" ..>

Code
 - the container where our chart component will be rendered

{% vite_asset 'js/main.jsx' %}

Code
 - the entry point file contains the main logic for the module. You will note that the path is set as

'js/main.jsx'

Code
 and not

'static/src/js/main.jsx'

Code
 . 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.

Code
 python
Code
`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.

Code
 python
Code
`....
from .api import api

urlpatterns = [
    ....
    path("api/", api.urls), # include api urls
]
`


Notes

Code
 Here we added the API endpoint and updated the URL config.

api = NinjaAPI(csrf=True)

Code
 - this endpoint will only allow requests with a valid CSRF token.

 authentication - you can visit the

[

Code
  django-ninja

](https://django-ninja.rest-framework.com/tutorial/authentication/)

Code
 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

[

Code
Apache Echarts

](https://echarts.apache.org/handbook/en/get-started/)

library along with the react

[

Code
echarts-for-react

](https://www.npmjs.com/package/echarts-for-react)

wrapper to render our charts.

Install dependencies and add chart options.

Code
 text
Code
`npm install --save echarts-for-react echarts`
Code
 javascript
Code
`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

Code
 standard echarts configuration,

[

Code
  see the docs

](https://echarts.apache.org/en/api.html#echarts)

Code
 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

[

Code
visit the docs

](https://reactjs.org/docs/getting-started.html)

.

Code
 javascript
Code
`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

Code
 - initialize component state with chart options and

autoRefresh

Code
 boolean

fetchBackendChartData

Code
 - 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

Code
 - if autoRefresh is set to true, fetch new chart data every 500milliseconds

toggleAutoRefresh

Code
 - boolean value for whether the component should show live data

refreshOptions

Code
 - update chart options data based on values from the backend

className

Code
 attributes - we are using tailwindcss & daisyui attributes here. The stylesheets are included in the head section of

templates/base.html

Code
 .

Add the Component to our entry point file

Code
 javascript
Code
`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/>

Code
 - this will render the react

Chart

Code
 element into the DOM in the provided container, i.e. element with id

chart-container

Code
 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.

Code
 text
Code
`$ 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

[

Code
install and configure

](https://tailwindcss.com/docs/installation)

tailwind for use with a build tool.

Additional Reading material

[

Code
  ViteJS docs

](https://vitejs.dev/)

Code
 - ViteJS fundamentals and api reference

[

Code
  Rollup.js docs

](https://rollupjs.org/guide/en/#plugins-overview)

Code
 - ViteJS uses rollup for bundling assets.

[

Code
  Javascript modules(MDN)

](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules)

Code
 - What is ESM, CJS, AMD?

[

Code
  Javascript modules(Article)

](https://dev.to/iggredible/what-the-heck-are-cjs-amd-umd-and-esm-ikm)

Code
 - A good summary on javascript modules