Building a real‑time chat application is a classic way to showcase the power of Python Flask combined with Socket.IO. Whether you’re a beginner looking to dip your toes into WebSockets or an experienced developer aiming to add live communication features to an existing Flask project, this guide will walk you through every step—from setting up the environment to deploying a production‑ready chat server. By the end of the article, you’ll have a fully functional, SEO‑friendly chat app that you can extend with user authentication, message persistence, and more.
Why Choose Flask + SocketIO for Real‑Time Chat?
Flask is lightweight, flexible, and has a thriving ecosystem of extensions. When paired with Flask‑SocketIO, it gains native support for WebSocket communication, allowing bi‑directional data flow between the client and server without the overhead of constant HTTP polling. Here are the top reasons developers love this stack:
- Simplicity: Minimal boilerplate code and clear routing.
- Scalability: Works with asynchronous workers (eventlet or gevent) for high‑throughput scenarios.
- Extensibility: Easy integration with Flask extensions like
Flask‑Login,Flask‑SQLAlchemy, andFlask‑Migrate. - Cross‑platform: Runs on any OS that supports Python, and the client side works in all modern browsers.
Prerequisites and Project Setup
System Requirements
- Python 3.9+ installed.
- Virtual environment tool (
venvorconda). - Basic knowledge of HTML, CSS, and JavaScript.
Creating the Project Structure
Start by creating a clean directory layout. This structure keeps templates, static assets, and server code organized.
my_chat_app/
├── app.py
├── requirements.txt
├── static/
│ ├── css/
│ │ └── style.css
│ └── js/
│ └── chat.js
└── templates/
└── index.html
Installing Dependencies
Run the following commands inside your virtual environment:
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install Flask Flask-SocketIO eventlet
pip freeze > requirements.txt
Building the Flask Backend
Initializing Flask and SocketIO
In app.py, import the necessary modules and create the Flask app instance. We’ll use eventlet as the asynchronous worker because it’s lightweight and integrates seamlessly with Flask‑SocketIO.
from flask import Flask, render_template
from flask_socketio import SocketIO, emit, join_room, leave_room
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key' # Replace with a secure key in production
# Initialize SocketIO with eventlet async mode
socketio = SocketIO(app, async_mode='eventlet')
Defining Routes and Event Handlers
The main route renders the chat UI, while SocketIO events handle user connections, messages, and room management.
@app.route('/')
def index():
return render_template('index.html')
@socketio.on('connect')
def handle_connect():
emit('status', {'msg': 'You are connected!'})
print('Client connected')
@socketio.on('join')
def on_join(data):
username = data['username']
room = data['room']
join_room(room)
emit('status', {'msg': f'{username} has entered the room.'}, room=room)
@socketio.on('message')
def handle_message(data):
room = data['room']
emit('message', data, room=room)
@socketio.on('leave')
def on_leave(data):
username = data['username']
room = data['room']
leave_room(room)
emit('status', {'msg': f'{username} has left the room.'}, room=room)
Running the Application
At the bottom of app.py, add the entry point that starts the server with eventlet support.
if __name__ == '__main__':
socketio.run(app, host='0.0.0.0', port=5000, debug=True)
Crafting the Front‑End Interface
HTML Template (templates/index.html)
The template includes a simple UI for entering a username, selecting a chat room, and sending messages. It also loads the Socket.IO client library from a CDN.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flask SocketIO Real‑Time Chat</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
</head>
<body>
<div class="chat-container">
<h2>Real‑Time Chat</h2>
<div id="status"></div>
<div id="messages" class="messages"></div>
<form id="chat-form">
<input type="text" id="username" placeholder="Your name" required>
<input type="text" id="room" placeholder="Room name" required>
<button type="submit">Join Room</button>
</form>
<form id="message-form" style="display:none;">
<input type="text" id="msg" autocomplete="off" placeholder="Type a message..." required>
<button type="submit">Send</button>
<button type="button" id="leave-btn">Leave</button>
</form>
</div>
<script src="{{ url_for('static', filename='js/chat.js') }}"></script>
</body>
</html>
Client‑Side JavaScript (static/js/chat.js)
The script establishes a Socket.IO connection, handles UI events, and updates the chat log in real time.
document.addEventListener('DOMContentLoaded', () => {
const socket = io();
const statusDiv = document.getElementById('status');
const messagesDiv = document.getElementById('messages');
const chatForm = document.getElementById('chat-form');
const messageForm = document.getElementById('message-form');
const leaveBtn = document.getElementById('leave-btn');
let currentRoom = '';
let username = '';
// Helper to append messages
const addMessage = (msg, type = 'msg') => {
const p = document.createElement('p');
p.className = type;
p.textContent = msg;
messagesDiv.appendChild(p);
messagesDiv.scrollTop = messagesDiv.scrollHeight;
};
// SocketIO event listeners
socket.on('connect', () => addMessage('Connected to server.', 'status'));
socket.on('status', data => addMessage(data.msg, 'status'));
socket.on('message', data => {
addMessage(`${data.username}: ${data.msg}`);
});
// Join room
chatForm.addEventListener('submit', e => {
e.preventDefault();
username = document.getElementById('username').value.trim();
currentRoom = document.getElementById('room').value.trim();
if (username && currentRoom) {
socket.emit('join', {username, room: currentRoom});
chatForm.style.display = 'none';
messageForm.style.display = 'flex';
}
});
// Send message
messageForm.addEventListener('submit', e => {
e.preventDefault();
const msgInput = document.getElementById('msg');
const msg = msgInput.value.trim();
if (msg) {
socket.emit('message', {username, room: currentRoom, msg});
msgInput.value = '';
}
});
// Leave room
leaveBtn.addEventListener('click', () => {
socket.emit('leave', {username, room: currentRoom});
messageForm.style.display = 'none';
chatForm.style.display = 'flex';
messagesDiv.innerHTML = '';
});
});
Basic Styling (static/css/style.css)
Keep the UI clean and responsive with a few CSS rules.
body {
font-family: Arial, Helvetica, sans-serif;
background: #f4f7f9;
margin: 0;
padding: 20px;
}
.chat-container {
max-width: 600px;
margin: auto;
background: #fff;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h2 { text-align: center; }
#messages {
height: 300px;
overflow-y: auto;
border: 1px solid #ddd;
padding: 10px;
margin-bottom: 10px;
}
#messages p { margin: 5px 0; }
p.status { color: #555; font-style: italic; }
form { display: flex; gap: 10px; }
input[type="text"] { flex: 1; padding: 8px; }
button { padding: 8px 12px; }
Leave a Reply