In this tutorial, you will learn to create a hybrid web application using Django, react, and webpack. A hybrid web app means:
It's up to us to decide which page is a single-page application or a Django-powered jinja frontend or both.
URLs or routes are managed by Django.
Webpack will handle all the static files headache i.e., all the optimizing stuff such as compressing and minifying.
Static files bundled by webpack will be served by Django. You can refer to this django-react-webpack repository to refer to the code snippets explained in this tutorial. Follow these steps to run the project.
Step 1: Setting up the django-react-webpack boilerplate locally
Clone the repository
CD to the location where
manage.py
is located.Create a virtual environment using python3:
python -m venv venv
.Activate virtual environment:
source venv/bin/activate
Install requirements:
pip install -r requirements.txt
.CD to frontend directory
cd frontend
Install node packages:
npm install
If you struggle to install nodejs, npm and npx, follow this tutorial
Step 2: Run the project
Open the first terminal where manage.py is located and run
python
manage.py
runserver
, to run the Django server.Open the second terminal inside the frontend directory
cd frontend
For development purpose run
npm run watch
.For production run:
npm run build
.
Now that if you want to learn to create the same project from scratch, read further.
How to integrate react with Django
To integrate react with Django you need webpack-bundle-tracker to save output generated by webpack and django-webpack-loader to consume the output saved by webpack bundle tracker.
💡 In this tutorial, you will learn to create a hybrid Django and react web app.
You can also watch the step-by-step video for the same.
%[https://youtu.be/Et5EfPQF8iI]
Step 1:Install python requirements
This code is tested on Django 2.2, you can use the same version till the project is up and running and then upgrade to the newer version to avoid any version-related issues.
pip install django==2.2
pip install django-webpack-loader==0.7.0
Django Webpack Loader - It consumes the output generated by the webpack-bundle-tracker and lets you use the generated bundles in Django.
Step 2: Setup the backend
Create a Django project
django-admin startproject conf .
Create required directories
mkdir {templates,static,frontend}
templates: To serve Django templates.
static: To serve Django static files.
frontend: We will install react project inside this directory.
Your directory structure should look like the one in the repository
Now configure your settings.py and urls.py
#settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(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',
],
},
},
]
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, "static_root")
STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),)
#urls.py
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [path('admin/', admin.site.urls), ]
if settings.DEBUG: urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
Create a new app pages
python manage.py startapp pages
Add it in INSTALLED_APPS inside settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'pages',
]
Now inside pages/views.py create two views to serve different URLs
from django.shortcuts import render
def index1(request):
return render(request, 'index1.html', {})
def index2(request):
return render(request, 'index2.html', {})
Create a file urls.py inside the pages app and two URLs inside pages/urls.py to serve the above two views.
touch pages/urls.py
from django.urls import path
from .views import index1, index2
app_name = 'pages'
urlpatterns = [
path('', index1, name='index1'),
path('index2', index2, name='index2')
]
Now update conf/urls.py to link pages app and inside the pages/urls.py link the created views (index1, index2).
#conf/urls.py
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('pages.urls', namespace='pages')), #Linked here
]
if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL,
document_root=settings.STATIC_ROOT)
#pages/urls.py
from django.urls import path
from .views import index1, index2
app_name = 'pages'
urlpatterns = [
path('', index1, name='index1'),
path('index2', index2, name='index2')
]
Now create the two HTML files index1.html and index2.html inside the templates directory to be rendered by the two views, you just created.
<!-- templates/index1.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Django React Webpack - Raturi.in</title></head>
<body>
<ul>
<li><a href="{% url 'pages:index1' %}">Index1</a></li>
<li><a href="{% url 'pages:index2' %}">Index2</a></li>
</ul>
<center> At index1</center>
</body>
</html>
<!-- templates/index2.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Django React Webpack - Raturi.in</title></head>
<body>
<ul>
<li><a href="{% url 'pages:index1' %}">Index1</a></li>
<li><a href="{% url 'pages:index2' %}">Index2</a></li>
</ul>
<center> At index2</center>
</body>
</html>
You have configured the backend part, let's configure the frontend one.
Step 3: Setup frontend
Change your directory to frontend
cd frontend/
Now create a react app using
npx create-react-app .
Now install the required npm packages using package.json. First create a file called package.json if does not exist, and then replace or write the entire code snippet from below. This will avoid conflicts due to different versions.
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-scripts": "3.4.1"
},
"scripts": {
"build": "./node_modules/.bin/webpack --mode=production --config webpack.config.js",
"watch": "npm run start -- --watch",
"start": "./node_modules/.bin/webpack --mode=development --config webpack.config.js"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-core": "^6.26.3",
"babel-loader": "^8.1.0",
"babel-preset-env": "^1.7.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babel-register": "^6.26.0",
"webpack": "^4.42.1",
"webpack-bundle-tracker": "^0.4.3",
"webpack-cli": "^3.3.11"
}
}
Now install the packages using the command below. Make sure you are inside the frontend directory.
npm install
Now create two files webpack.config.js, .babelrc and a directory for storing your webpack-generated bundles inside fronted as assets/dist
touch .babelrc webpack.config.js
mkdir assets assets/dist
webpack.config.js is required by webpack for its configuration. .babelrc is used by babel that is used as a transpiler and syntax converter for the code to be compatible with older versions of browsers.
Now configure webpack.config.js, .babelrc.
webpack.config.json
const path = require("path");
const BundleTracker = require('webpack-bundle-tracker');
var config = {
context: __dirname,
entry: {
'staticfiles': './src/index.js',
},
resolve: {
alias: {
'@': path.resolve(__dirname, 'src/'),
'@django': path.resolve(__dirname, '../static/'),
}
},
output: {
path: path.join(__dirname, './assets/dist'),
filename: "[name]-[hash].js",
publicPath: '/static/dist/'
},
plugins: [
new BundleTracker({ filename: './webpack-stats.json' }),
],
devtool: 'cheap-module-eval-source-map',
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader'],
include: [
path.resolve(__dirname, "src"),
],
},
{
test: /\.css$/,
use: [
'style-loader', 'css-loader'
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader',
],
},
]
}
}
module.exports = (env, argv) => {
if (argv.mode === 'production') {
config.devtool = 'none';
}
return config
};
.babelrc
{ "presets": [ "@babel/preset-env", "@babel/preset-react" ] }
Now in your conf/settings.py add webpack-related settings.
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'pages',
'webpack_loader',
]
FRONTEND_DIR = os.path.join(BASE_DIR, 'frontend')
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, "static_root")
STATICFILES_DIRS = (
os.path.join(BASE_DIR, "static"),
os.path.join(FRONTEND_DIR, "assets"),
)
WEBPACK_LOADER = {
'DEFAULT': {
'BUNDLE_DIR_NAME': 'dist/', # must end with slash
'STATS_FILE': os.path.join(FRONTEND_DIR, 'webpack-stats.json')
}
}
Now update your templates index1.html and index2.html to load webpack-generated files something like this
<!-- templates/index1.html -->
{% load render_bundle from webpack_loader %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Django React Webpack - Raturi.in</title>
{% render_bundle 'staticfiles' 'css' %}
</head>
<body>
<ul>
<li><a href="{% url 'pages:index1' %}">Index1</a></li>
<li><a href="{% url 'pages:index2' %}">Index2</a></li>
</ul>
<center>
At index1
</center>
{% render_bundle 'staticfiles' 'js' %}
</body>
</html>
<!-- templates/index2.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Django React Webpack - Raturi.in</title>
</head>
<body>
<ul>
<li><a href="{% url 'pages:index1' %}">Index1</a></li>
<li><a href="{% url 'pages:index2' %}">Index2</a></li>
</ul>
<center>
At index2
</center>
</body>
</html>
render_bundle 'staticfiles': staticfiles is the name of the entrypoint and render_bundle will load the files(js,css) generated by webpack inside dist folder.
Step 4: Run the server
Open the first terminal where manage.py is located and run
python
manage.py
runserver
, to run the Django server.Open the second terminal inside the frontend directory
cd frontend
For development purpose run
npm run watch
.For production run:
npm run build
.
npm run watch will automatically look for changes for static files and will update the dist folder. npm run build will generate the compressed and optimized files for production. Tip: You check the source code of html file in browser you can see webpack generated files inside dist folder.
That's all for this tutorial.