Python Flask Real-Time Chat With Socketio

Written by

in

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:

  1. Python 3.8+ (recommended 3.11)
  2. Virtual environment tool (venv or conda)
  3. 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 typing or private_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

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *