Real‑time communication has become a cornerstone of modern web applications, and developers often turn to Python Flask combined with SocketIO to build fast, interactive chat systems. In this guide you’ll learn step‑by‑step how to create a fully functional real‑time chat app using Flask‑SocketIO, from setting up the environment to deploying a production‑ready solution. Whether you’re a beginner looking for a hands‑on tutorial or an experienced developer seeking best practices, this article covers everything you need to know.
Why Choose Flask with SocketIO for Real‑Time Chat?
Flask is lightweight, easy to extend, and integrates seamlessly with python‑socketio. Together they provide:
- Bi‑directional communication – messages travel instantly between client and server.
- Scalable architecture – works with eventlet, gevent, or asyncio for high concurrency.
- Simple API – clear event decorators make code readable and maintainable.
- Cross‑platform support – works on Windows, macOS, and Linux without major changes.
Prerequisites
Before diving into code, make sure you have the following installed:
- Python 3.8+ (recommended 3.11)
- Virtual environment tool (
venvorconda) - Basic knowledge of Flask routing and HTML/JavaScript
We’ll also use eventlet as the asynchronous server, but you can switch to gevent or asyncio later.
Step 1 – Setting Up the Project Structure
A clean folder layout helps keep your code organized:
flask_chat/
│
├── app.py # Main Flask application
├── requirements.txt # Python dependencies
├── templates/
│ └── index.html # Chat UI
└── static/
└── js/
└── chat.js # Client‑side SocketIO logic
Step 2 – Installing Dependencies
Activate your virtual environment and install the required packages:
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install flask flask-socketio eventlet
pip freeze > requirements.txt
Step 3 – Creating the Flask Application
app.py
The core of the chat server lives in app.py. Below is a minimal yet complete implementation:
from flask import Flask, render_template, request
from flask_socketio import SocketIO, emit, join_room, leave_room
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key' # Change in production
socketio = SocketIO(app, async_mode='eventlet')
@app.route('/')
def index():
return render_template('index.html')
# ---------- SocketIO Event Handlers ----------
@socketio.on('connect')
def handle_connect():
emit('status', {'msg': f'User {request.sid} has connected.'}, broadcast=True)
@socketio.on('disconnect')
def handle_disconnect():
emit('status', {'msg': f'User {request.sid} has left.'}, broadcast=True)
@socketio.on('join')
def on_join(data):
room = data['room']
join_room(room)
emit('status', {'msg': f'{request.sid} entered room {room}.'}, room=room)
@socketio.on('leave')
def on_leave(data):
room = data['room']
leave_room(room)
emit('status', {'msg': f'{request.sid} left room {room}.'}, room=room)
@socketio.on('message')
def handle_message(data):
room = data.get('room')
msg = data['msg']
if room:
emit('message', {'msg': msg, 'user': request.sid}, room=room)
else:
emit('message', {'msg': msg, 'user': request.sid}, broadcast=True)
if __name__ == '__main__':
socketio.run(app, host='0.0.0.0', port=5000)
Step 4 – Building the Front‑End UI
templates/index.html
The HTML page loads the SocketIO client library, connects to the server, and provides a simple chat interface:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flask Real‑Time Chat</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/milligram/1.4.1/milligram.min.css">
<style>
#chat { height: 400px; overflow-y: scroll; border: 1px solid #ccc; padding: 10px; }
.msg { margin: 5px 0; }
.status { color: #888; font-style: italic; }
</style>
</head>
<body>
<h2>Flask‑SocketIO Real‑Time Chat</h2>
<div id="chat"></div>
<form id="msgForm">
<input type="text" id="msgInput" placeholder="Type your message…" autocomplete="off" required>
<button type="submit">Send</button>
</form>
<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
<script src="{{ url_for('static', filename='js/chat.js') }}"></script>
</body>
</html>
static/js/chat.js
The JavaScript file handles client‑side events and updates the DOM:
document.addEventListener('DOMContentLoaded', () => {
const socket = io();
const chatBox = document.getElementById('chat');
const form = document.getElementById('msgForm');
const input = document.getElementById('msgInput');
// Helper to append messages
function addMessage(content, className='msg') {
const p = document.createElement('p');
p.className = className;
p.innerHTML = content;
chatBox.appendChild(p);
chatBox.scrollTop = chatBox.scrollHeight;
}
// Receive status updates (connect/disconnect, join/leave)
socket.on('status', data => {
addMessage(data.msg, 'status');
});
// Receive chat messages
socket.on('message', data => {
const safeMsg = data.msg.replace(//g, '>');
addMessage(`${data.user}: ${safeMsg}`);
});
// Submit new message
form.addEventListener('submit', e => {
e.preventDefault();
const msg = input.value.trim();
if (msg) {
socket.emit('message', {msg: msg});
input.value = '';
}
});
});
Step 5 – Understanding Core SocketIO Events
Flask‑SocketIO uses decorators to bind Python functions to client‑side events. The most common events are:
- connect – Fired when a client establishes a WebSocket connection.
- disconnect – Triggered when the client drops the connection.
- join / leave – Manage logical “rooms” so users can chat in separate channels.
- message – Custom event for sending chat text; you can create additional events such as
typingorprivate_message.
Step 6 – Adding Rooms and Private Messaging
Rooms enable group chats without creating separate namespaces. To add a private chat feature, you can use the session ID as a room name:
# Private message handler
@socketio.on('private_message')
def handle_private(data):
target_sid = data['to']
msg = data['msg']
emit('private_message', {'msg': msg, 'from': request.sid},
room=target_sid)
On the client side, you would emit private_message with the recipient’s socket ID and listen for the same event to display the message.
Step 7 – Scaling with Eventlet, Gevent, or AsyncIO
For production, you’ll want a server that can handle thousands of concurrent connections. Flask‑SocketIO abstracts the async layer, allowing you to swap the underlying worker with a single line change:
# Use gevent
socketio = SocketIO(app, async_mode='gevent')
# Or asyncio (Python 3.8+)
socketio = SocketIO(app, async_mode='asgi')
When you choose eventlet, remember to install it with pip install eventlet and run the app via socketio.run(app) as shown earlier.
Step 8 – Deploying to Production
Deploying a real‑time Flask app differs from a classic WSGI deployment because you need a WebSocket‑aware server. Common options include:
- Gunicorn with event
Leave a Reply