How to use React with Django (in 10 mins) ⚛️

Photo of Tom Dekan
by Tom Dekan
Updated: Thu 23 May 2024

In the simplest way possible, we'll set up a React frontend (using Typescript) with a Django backend, using good practices as we go.

Q. Why would I use React? Couldn't I just use Django templates with HTMX?

A. Yes. Often, the fastest route to building a product is to use Django templates with HTMX (Example here 🔴🟡), or Alpine js Example here 🌤️)

To give you some concrete examples:

  • I could have built v2 of Photon Designer with Django and HTMX
  • I built RedstoneHR largely with Django and HTMX (which I later sold)
  • I built my first major product with Django and HTMX (a tool for assessing medical claims at least 7x faster. Still used by the company today 🙂)
  • I should have built Amazing.photos with Django and HTMX
  • A close friend's company is growing quickly, has over $1M ARR, and is profitable. They have a powerful product built using Django and HTMX. He also needs fewer developers because they are each so productive with Django and HTMX.

Q. For my next project, I'm considering using Django + HTMX or Django + React. Which should I pick?

In short, it is generally much faster to build with Django and HTMX because you must handle less complexity.

With Django and HTMX/Alpine, you just need one server. And you don't need to handle communicating between a frontend and backend server.

But, using Django + HTMX isn't always the best fit. If you're building something like Webflow or Figma - where you have a lot of client state - I'd recommend using a frontend framework.

Anyway, let's start building with React! ⚛️

Here's what our final product will look like. We're looking at a React component, with all the data coming from Django:

1. Set up React

We'll use Vite to set up our React frontend. Vite is a build tool provide a fast development experience for React (and other frontend frameworks).

Q. Why do I need Vite? A. Without Vite, you'll need to set up a lot of things manually. Vite provides a lot of things, like hot module replacement, and fast builds when you're developing. There's negligible value to doing this yourself. So, I'd recommend using Vite.

Install node and npm

Check if you have node and npm installed by running the following commands:

node -v
npm -v

If you see the version numbers, you're good to go. If you don't, install them from here.

Q. What are node and npm? A. Node.js allows you to run javascript on your computer, and outside the browser. A. npm is a package manager for node.js, which allows you to install and manage packages.

Install Vite

npm create vite@latest

And then enter the following options: - Project name: react+django - Package name: frontend - Framework: React - Variant: TypeScript

You should see a message saying "Done"

Run your React app in development

  • Following Vite's instructions, run:
cd react+django
npm install
npm run dev

You should see something like this:

VITE v  ready in 449 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help
  • Visit http://localhost:5173/ in your browser. You should see your React app.

Style your React app

Generate UI with Photon Designer or paste my code

To add some custom styles, I'll use Photon Designer to design a simple gallery to display apples, perhaps I'm a fruit seller.

Here's a video using Photon Designer to create this UI in 2 minutes:

So, you can either:

a. use Photon Designer for free to create your own UI, or

b. copy the below code into react+django/src/App.tsx:

import './App.css'

function App() {
    const apples = [
        {name: 'Bramley', color: 'red and green', photoUrl: 'https://upload.wikimedia.org/wikipedia/commons/thumb/5/52/Bramley%27s_Seedling_Apples.jpg/320px-Bramley%27s_Seedling_Apples.jpg'},
        {name: 'Cox Orange Renette', color: 'yellow', photoUrl: 'https://upload.wikimedia.org/wikipedia/commons/thumb/e/ed/Cox_orange_renette2.JPG/320px-Cox_orange_renette2.JPG'},
        {name: 'Granny Smith', color: 'green', photoUrl: 'https://upload.wikimedia.org/wikipedia/commons/thumb/b/bf/Granny_smith_closeup.jpg/320px-Granny_smith_closeup.jpg'},
        {name: 'SugarBee', color: 'red and yellow', photoUrl: 'https://upload.wikimedia.org/wikipedia/commons/thumb/5/5b/The_SugarBee_Apple_now_grown_in_Washington_State.jpg/320px-The_SugarBee_Apple_now_grown_in_Washington_State.jpg'},
    ]

    return (
        <>
            <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
                {apples.map((apple, index) => (
                    <div className="bg-white rounded-lg shadow-md overflow-hidden" key={index}>
                        <img
                            src={apple.photoUrl}
                            alt={`A beautiful round apple, with a shiny skin, perfect for eating or baking. The ${apple.name} apple has a ${apple.color} color.`}
                            className="w-full h-48 object-cover"
                            width="400"
                            height="300"
                        />
                        <div className="p-4">
                            <h3 className="text-lg font-semibold text-gray-800 mb-2">{apple.name}</h3>
                            <p className="text-gray-600">Color: {apple.color}</p>
                        </div>
                    </div>
                ))}
            </div>
        </>
    )
}

export default App

Add Tailwind CSS to your React app

  • Paste the below into react+django/index.html:
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + React + TS</title>
    <script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

Great - you've now set up a React frontend with Vite and Tailwind CSS! Let's now send data from Django to React.

2. Set up Django

We'll set up a simple Django backend to send data to our React frontend.

Create a new Django project

pip install --upgrade django django-cors-headers
django-admin startproject core .
python manage.py startapp apples

Update our settings in core/settings.py

Register installed apps

  • Add apples and 'corsheaders' (necessary to prevent Django from blocking our React frontend to the INSTALLED_APPS list in core/settings.py
INSTALLED_APPS = [
    ...
    "corsheaders",
    "apples",
]

Add CORS settings

  • Add the following to the bottom of core/settings.py
CORS_ALLOWED_ORIGINS = ["http://localhost:5173"]  # Make sure the port number matches the one you're using for the React app.

Add middleware

  • Add the following to the MIDDLEWARE list in core/settings.py
MIDDLEWARE = [
    ...
    "corsheaders.middleware.CorsMiddleware",
    ...
]

Create a model for the apples in apples/models.py

from django.db import models


class Apple(models.Model):
    name = models.CharField(max_length=100)
    color = models.CharField(max_length=100)
    photo_url = models.URLField()

    def __str__(self):
        return self.name

Create and apply migrations

python manage.py makemigrations
python manage.py migrate

Add some apples to the database using the Django ORM

  • Open your Django shell via the terminal
python manage.py shell
  • Add some apples to your database (using the Django ORM)
from apples.models import Apple

Apple.objects.create(name='Bramley', color='red and green', photo_url='https://upload.wikimedia.org/wikipedia/commons/thumb/5/52/Bramley%27s_Seedling_Apples.jpg/320px-Bramley%27s_Seedling_Apples.jpg')
Apple.objects.create(name='Cox Orange Renette', color='yellow', photo_url='https://upload.wikimedia.org/wikipedia/commons/thumb/e/ed/Cox_orange_renette2.JPG/320px-Cox_orange_renette2.JPG')
Apple.objects.create(name='Granny Smith', color='green', photo_url='https://upload.wikimedia.org/wikipedia/commons/thumb/b/bf/Granny_smith_closeup.jpg/320px-Granny_smith_closeup.jpg')
Apple.objects.create(name='SugarBee', color='red and yellow', photo_url='https://upload.wikimedia.org/wikipedia/commons/thumb/5/5b/The_SugarBee_Apple_now_grown_in_Washington_State.jpg/320px-The_SugarBee_Apple_now_grown_in_Washington_State.jpg')

Create a serializer for the apples

  • Create a file in your Django app called serializers.py (i.e., apples/serializers.py) containing:
from .models import Apple
from typing import Iterable, List, Dict, Any


def serialize_apples(apples: Iterable[Apple]) -> List[Dict[str, Any]]:
    data = []
    for apple in apples:
        data.append({
            'name': apple.name,
            'color': apple.color,
            'photo_url': apple.photo_url,
        })
    return data

Create a view to return the apples as JSON in apples/views.py

from .models import Apple
from .serializers import serialize_apples
from django.http import JsonResponse


def apple_list(request):
    apples = Apple.objects.all()
    return JsonResponse(serialize_apples(apples), safe=False)

Add your urls

  • Paste this into core/urls.py:
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('apples/', include('apples.urls')),
]
  • Create a file called urls.py in your Django app (i.e., apples/urls.py), and paste in the below:
from django.urls import path
from . import views


urlpatterns = [
    path('', views.apple_list),
]

Check your Django API

  • Run your Django server
python manage.py runserver
  • Visit http://localhost:8000/apples in your browser (Don't forget the /apples slug). You should see the apples you added to the database in JSON format.

You should see something like this: Django JSON response

You've now set up a Django backend with a simple API to send data to your React frontend.

3. Connect React and Django

We have a Django backend and a React frontend. Let's connect them.

Install Axios in the React app

  • Make sure you're in the react+django directory
cd react+django
  • Install axios (a popular library for making HTTP requests) in your React app
npm install axios

Fetch the apples from Django in your React app

  • Paste the below into react+django/src/App.tsx
import { useEffect, useState } from 'react'
import axios from 'axios'
import './App.css'

function App() {
    const [apples, setApples] = useState<Apple[]>([])

    interface Apple {
        name: string;
        color: string;
        photo_url: string;
    }

    useEffect(() => {
        const applesListUrl = 'http://localhost:8000/apples/'
        axios.get<Apple[]>(applesListUrl)
            .then(response => setApples(response.data))
    }, [])

    return (
        <>
            <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
                {apples.map((apple, index) => (
                    <div className="bg-white rounded-lg shadow-md overflow-hidden" key={index}>
                        <img
                            src={apple.photo_url}
                            alt={`${apple.name} apple`}
                            className="w-full h-48 object-cover"
                        />
                        <div className="p-4">
                            <h3 className="text-lg font-semibold text-gray-800 mb-2">{apple.name}</h3>
                            <p className="text-gray-600">Color: {apple.color}</p>
                        </div>
                    </div>
                ))}
            </div>
        </>
    )
}

export default App

See your React app fetch data from Django

  • Run your Django server
python manage.py runserver
  • In a separate terminal window, run your React server
npm run dev
  • Visit your React app at http://localhost:5173/.

🎉Bonus: Style your React app using the powerful Framer Motion library

For a bonus sheen, let's add some animations to our React app using the Framer Motion library.

We'll make the apples in our gallery (from our data that Django sent) grow when hovered over, and shrink when clicked on, and then display a larger version of the apple in a modal.

npm install framer-motion
  • Replace the contents of react+django/src/App.tsx with the below:
import { useEffect, useState } from 'react'
import axios from 'axios'
import './App.css'
import { motion } from 'framer-motion'


function App() {
    const [apples, setApples] = useState<Apple[]>([])
    const [selectedApple, setSelectedApple] = useState<Apple | null>(null)

    interface Apple {
        name: string;
        color: string;
        photo_url: string;
    }

    useEffect(() => {
        const applesListUrl = 'http://localhost:8000/apples/'
        axios.get<Apple[]>(applesListUrl)
            .then(response => setApples(response.data))
    }, [])

    const handleAppleClick = (apple: Apple) => {
        setSelectedApple(apple)
    }

    const handleAppleClose = () => {
        setSelectedApple(null)
    }

    return (
        <>
            <motion.div
                className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4"
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                transition={{ duration: 0.5 }}
            >
                {apples.map((apple, index) => (
                    <motion.div
                        className="bg-white rounded-lg shadow-md overflow-hidden cursor-pointer"
                        key={index}
                        whileHover={{ scale: 1.05 }}
                        whileTap={{ scale: 0.95 }}
                        onClick={() => handleAppleClick(apple)}
                    >
                        <motion.img
                            src={apple.photo_url}
                            alt={`${apple.name} apple`}
                            className="w-full h-48 object-cover"
                            initial={{ opacity: 0, y: 50 }}
                            animate={{ opacity: 1, y: 0 }}
                            transition={{ delay: index * 0.1, duration: 0.5 }}
                        />
                        <div className="p-4">
                            <h3 className="text-lg font-semibold text-gray-800 mb-2">{apple.name}</h3>
                            <p className="text-gray-600">Color: {apple.color}</p>
                        </div>
                    </motion.div>
                ))}
            </motion.div>

            {selectedApple && (
                <motion.div
                    className="fixed top-0 left-0 w-full h-full bg-black bg-opacity-50 flex items-center justify-center"
                    initial={{ opacity: 0 }}
                    animate={{ opacity: 1 }}
                    exit={{ opacity: 0 }}
                    onClick={handleAppleClose}
                >
                    <motion.div
                        className="bg-white rounded-lg shadow-md overflow-hidden max-w-md mx-auto"
                        initial={{ scale: 0 }}
                        animate={{ scale: 1 }}
                        exit={{ scale: 0 }}
                        transition={{ duration: 0.3 }}
                    >
                        <img
                            src={selectedApple.photo_url}
                            alt={`${selectedApple.name} apple`}
                            className="w-full h-64 object-cover"
                        />
                        <div className="p-4">
                            <h3 className="text-2xl font-semibold text-gray-800 mb-2">{selectedApple.name}</h3>
                            <p className="text-gray-600">Color: {selectedApple.color}</p>
                        </div>
                    </motion.div>
                </motion.div>
            )}
        </>
    )
}

export default App

Congrats - React frontend connected to Django ✅

You now have a working React frontend with a Django backend. The React app fetches data from the Django API and displays it.

Some ideas for next steps - Deployment or Auth?

  • Add authentication to the Django API and React frontend
  • Add create, update, delete functionality to the Django API and connect it to the React frontend
  • Deploy the Django backend and React frontend to production

I'd be happy to do a part 2 showing you:

i. how to deploy both to production (it's very quick to do (10 mins) once you know how).

ii. how to add simple authentication to the Django API and React frontend (also very quick to do (10 mins) once you know how).

Email me / Comment on Youtube if you'd like either 🙂

Final repo for reference

Here's all the code in a repo if you'd like to use it.

Let's get visual.

Do you want to create beautiful frontends effortlessly?
Click below to book your spot on our early access mailing list (as well as early adopter prices).
Copied link to clipboard 📋

Made with care by Tom Dekan

© 2024 Photon Designer