Set up React in your Django Project with webpack

No webpack experience required. Promise.

Tutorials for this kind of setup are out there like this one, which was extremely helpful, but a touch out of date. This guide is modeled after it, but includes (1) extra steps to get Babel working (2) a webpack config setup to a file directory structure in tune to a more recent version of Django — as of writing this article: 1.11.6 and (3) no webpack pretentiousness.

The prerequisites:

  • an existing Django project
  • familiarity with Django

The gameplan:

  1. Install npm
  2. Set up node package
  3. Create the webpack config file
  4. Set up Babel
  5. Compile bundles
  6. Create React starter
  7. Integrate with Django
  8. Send Django model data to React

1. Install npm

First, download the LTS version of Node.js

Then open terminal to test that you have Node.js which includes npm.

node -v

The output should match the LTS version number you downloaded.

2. Set up node package

Generate a package.json file: In terminal, go to the root of your project and enter

npm init

Press enter for all the questions, modifying if necessary. It should look something like this:

name: (djangoproject)
version: (1.0.0)
description: description here
entry point: (index.js)
test command:
git repository: (https://github.com/user/djangoproject.git)
keywords:
author: User <user@gmail.com> (http://user.com)
license: (ISC)
About to write to /Users/tmpvar/djangoproject/package.json:

{
"name": "djangoproject",
"version": "1.0.0",
"description": "description here",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/user/djangoproject.git"
},
"author": "User <user@gmail.com> (http://user.com)",
"license": "ISC"
}


Is this ok? (yes)

Add the following to your package.json:

"scripts": {
"build": "webpack --config webpack.config.js --progress --colors",
"watch": "webpack --config webpack.config.js --watch"
},

Install dependencies.

npm install --save-dev react webpack webpack-bundle-tracker install babel-loader babel-core babel-preset-env

This should install react, webpack, and babel things.

3. Create the webpack config file

If you haven’t already, create a js directory wherever you serve your static files. Create an index jsx file as well.

mkdir -p djangoproject/djangoapp/static/js
touch djangoproject/djangoapp/static/js/index.js

Create the webpack config file

touch webpack.config.js

Paste the following into webpack.config.js

var path = require("path");
var webpack = require('webpack');
var BundleTracker = require('webpack-bundle-tracker');

module.exports = {
context: __dirname,

entry: './djangoproject/djangoapp/static/js/index',

output: {
path: path.resolve('./djangoproject/djangoapp/static/bundles/'),
filename: "[name]-[hash].js",
},

plugins: [
new BundleTracker({filename: './djangoproject/webpack-stats.json'}),
],

module: {
loaders: [
{ test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ },
{ test: /\.jsx$/, loader: 'babel-loader', exclude: /node_modules/ }
]
},

resolve: {
modules: ['node_modules', 'bower_components'],
extensions: ['.js', '.jsx']
},


};

4. Set up Babel

Create a Babel configuration file

touch .babelrc

Paste the following into the file:

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

Your project directory should now look something like this, depending on where you are serving your static files.

repodirectory/
├── djangoproject/
│ └── djangoproject/ # project level files
│ └── djangoapp/ # app level files
│ └── static/
│ └── bundles/ # generated by webpack
│ └── js/
│ └── manage.py
│ └── webpack-stats.json # generated by webpack
├── .babelrc
├── package.json
├── webpack.config.js
├── node_modules/ #contains our JS dependencies

5. Compile bundles

Let’s create a bundle.

./node_modules/.bin/webpack --config webpack.config.js

Creating a bundle after every change you make to your files gets tedious. Thankfully, because of a command we specified in package.json, we can continuously watch for changes and compile bundles automatically using the command below:

npm run watch

6. Create React starter

Let’s create a “Hello World” example using a code snippet from the reactjs website.

static/js/index.jsx:

import React from 'react'
import
ReactDOM from 'react-dom'


function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}

const element = <Welcome name="world" />;
ReactDOM.render(
element,
document.getElementById('react')
);

7. Integrate with Django

If you don’t already have a requirements.txt file in the root of your project repository folder, go ahead and create one, adding this line to the file:

django-webpack-loader

From the terminal, execute (make sure your virtualenv is activated if you’re using one):

pip install -r requirements.txt

In settings.py, add webpack_loader to your installed apps and copy and paste the webpack loader configurations.

INSTALLED_APPS = (
...
'webpack_loader',
)
WEBPACK_LOADER = {
'DEFAULT': {
'BUNDLE_DIR_NAME': 'bundles/',
'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'),
}
}

In your templates folder, add the following to a new or existing html file:

{% load render_bundle from webpack_loader %}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Example</title>
</head>
 <body>
<div id="react"></div>
{% render_bundle 'main' %}

</body>
</html>

Connect your template to a view, and the the view to a url. Navigate to the page and you should see “Hello, world”.

8. Send Django model data to React

Let’s display something more interesting and send model data information from a database to React using Nick Sweeting’s Page-As-A-Component solution.

You can modify the rest of the code snippets to fit your existing django project. I’ll assume we have a class in models.py called Post:

class Post(models.Model):
title = models.CharField(max_length=100, blank=False)
description = models.TextField(max_length=350)

We can create a list of all post objects with these values in a view.

views.py

class Posts(View):
title = 'Posts'
template = 'posts.html'

def get(self, request, *args, **kwargs):
posts = list(Post.objects.values('pk', 'title', 'description'))

context = {
'title': self.title,
'props': posts,
}

return render(request, self.template, context)

You’ll notice we never defined a value called ‘pk,’ (primary key) but we can ask for it’s value in the list because django assigns one automatically when the model instance is created. React will require some kind of key so we use pk here.

If you are curious, you can fire up the django shell: python manage.py shell and view the serialized query of your model instances with ‘pk’ included:

>>> from djangoapp.models import Post
>>> posts = Post.objects.all()
>>> from django.core import serializers
>>> serializers.serialize("json", posts)
{your model results here}

Hook up the view to the url:

urls.py

url(r'^posts/', Posts.as_view(), name='posts'),

Now let’s take the html we created above and make sure the render_bundle script is loaded after the props are declared. The ‘safe’ flag is there so that we later pass a raw python list to our javascript.

{% load render_bundle from webpack_loader %}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Example</title>
</head>
<body>
<div id="react"></div>
<script>
window.props = {{ props|safe }}
window.react_mount = document.getElementById('react');
</script>

{% render_bundle 'main' %}
</body>
</html>

Now let’s modify the index.jsx we made above:

class Test extends React.Component {
render() {
var list = window.props;
return <div>{list.map(item => <TestChild key={item.pk}
title={item.title}
description={item.description}/> )}</div>;
}
}

class TestChild extends React.Component {
render() {
return <li><b>{this.props.title}</b>: {this.props.description}</li>;
}
}


ReactDOM.render(
<Test/>,
window.react_mount,
)

That’s it! You should now see a list of some information from your model data.

Hope this helped! Let us know in the comments if you run into trouble.


One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.