In the simplest way possible, we'll set up a React frontend (using Typescript) with a Django backend, using good practices as we go.
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.
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 theINSTALLED_APPS
list incore/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 incore/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:
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.