WebSockets have become an essential tool for building real-time web applications. Whether you're building a chat app, live notifications, or a collaborative tool, WebSockets allow your server and client to communicate instantly without the overhead of repeated HTTP requests. In this guide, we'll walk through how to set up WebSockets in Django using Django Channels.
What Are WebSockets?
HTTP is a request-response protocol — the client asks, the server answers, and the connection closes. This works fine for most web interactions, but falls apart when you need real-time communication. Imagine a chat app where you have to refresh the page to see new messages — that's the HTTP way.
WebSockets solve this by keeping a persistent, two-way connection open between the client and server. Once the connection is established, both sides can send messages to each other at any time, making it perfect for real-time features.
Prerequisites
Before we dive in, make sure you have the following installed:
- Python 3.10+
- Django 4.x+
- Redis (for the channel layer)
Setting Up Django Channels
Django doesn't support WebSockets out of the box. Django Channels extends Django to handle WebSockets, HTTP2, and other protocols alongside standard HTTP.
Start by installing the required packages:
pip install channels channels-redis daphne
Next, add Channels to your installed apps:
# settings.py
INSTALLED_APPS = [
'daphne', # must be first
'django.contrib.auth',
# ... rest of your apps
'channels',
]
Then configure the ASGI application:
# settings.py
ASGI_APPLICATION = 'myproject.asgi.application'
Configuring the Channel Layer
The channel layer allows different parts of your application to communicate with each other. We'll use Redis as the backend:
settings.py
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
'hosts': [('127.0.0.1', 6379)],
},
},
}
Make sure Redis is running locally:
sudo service redis-server start
Setting Up the ASGI Application
Replace the contents of your asgi.py file:
# asgi.py
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
import myapp.routing
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
application = ProtocolTypeRouter({
'http': get_asgi_application(),
'websocket': AuthMiddlewareStack(
URLRouter(
myapp.routing.websocket_urlpatterns
)
),
})
Writing a Consumer
A consumer is the Channels equivalent of a Django view — it handles WebSocket connections. Let's write a simple chat consumer:
# myapp/consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = f'chat_{self.room_name}'
# join room group
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
# leave room group
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
async def receive(self, text_data):
data = json.loads(text_data)
message = data['message']
# broadcast message to room group
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message',
'message': message,
}
)
async def chat_message(self, event):
# send message to WebSocket
await self.send(text_data=json.dumps({
'message': event['message']
}))
Routing
Wire up the consumer to a URL:
# myapp/routing.py
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),
]
Connecting from React
On the frontend, the native WebSocket API is all you need:
// hooks/useWebSocket.js
import { useEffect, useRef, useState } from 'react'
export const useWebSocket = (roomName) => {
const [messages, setMessages] = useState([])
const ws = useRef(null)
useEffect(() => {
ws.current = new WebSocket(`ws://localhost:8000/ws/chat/${roomName}/`)
ws.current.onopen = () => console.log('Connected')
ws.current.onmessage = (e) => {
const data = JSON.parse(e.data)
setMessages(prev => [...prev, data.message])
}
ws.current.onclose = () => console.log('Disconnected')
return () => ws.current.close()
}, [roomName])
const sendMessage = (message) => {
if (ws.current.readyState === WebSocket.OPEN) {
ws.current.send(JSON.stringify({ message }))
}
}
return { messages, sendMessage }
}
// ChatRoom.jsx
const ChatRoom = ({ roomName }) => {
const { messages, sendMessage } = useWebSocket(roomName)
const [input, setInput] = useState('')
const handleSend = () => {
if (!input.trim()) return
sendMessage(input)
setInput('')
}
return (
<div>
<div className="messages">
{messages.map((msg, i) => (
<p key={i}>{msg}</p>
))}
</div>
<input value={input} onChange={e => setInput(e.target.value)} />
<button onClick={handleSend}>Send</button>
</div>
)
}
Running the Server
Django Channels uses Daphne instead of the standard runserver:
daphne -p 8000 myproject.asgi:application
Or during development you can still use:
python manage.py runserver
Channels patches the development server automatically when Daphne is in INSTALLED_APPS.
What to Build Next
Now that you have the foundation, here are some directions to take it further. You could add authentication to your consumers using self.scope['user'] to identify who is sending messages. You could also persist messages to the database using Django ORM inside the consumer — just make sure to use database_sync_to_async when making synchronous ORM calls from an async consumer. For production, swap out the local Redis instance for a managed Redis service like Redis Cloud or AWS ElastiCache, and deploy using Daphne behind Nginx with WebSocket proxy support configured.
Conclusion
Django Channels bridges the gap between Django's synchronous nature and the real-time web. With just a few configuration steps and a consumer, you have a fully functional WebSocket server capable of powering chat apps, live notifications, collaborative editors, and more. The combination of Django Channels on the backend and the native WebSocket API on the frontend gives you a clean, scalable foundation to build on.