IOPlugin verified by Strapi
A plugin for Strapi CMS that provides the ability for Socket IO integration
@strapi-community/plugin-io
Real-time WebSocket integration for Strapi v5 with Socket.IO
Add real-time capabilities to your Strapi application with WebSocket support. Automatically broadcast content changes, manage user connections, and build live features like chat, notifications, and collaborative editing.
Table of Contents
- Features
- Quick Start
- Installation
- Configuration
- Usage Examples
- Helper Functions
- Authentication
- Admin Panel
- Monitoring Service
- TypeScript Support
- Performance
- Migration Guide
- Documentation
- Contributing
- License
Features
Core Functionality
- Automatic Real-Time Events - CRUD operations broadcast automatically to connected clients
- Entity-Specific Subscriptions - Subscribe to individual entities for targeted updates
- Role-Based Access Control - Built-in permission checks for JWT and API tokens
- Multi-Client Support - Handle 2500+ concurrent connections efficiently
Live Presence (NEW)
- Real-Time Presence Awareness - See who else is editing the same content
- Typing Indicator - See when someone is typing and in which field
- Admin Panel Sidebar - Live presence panel integrated into Content Manager
- Session-Based Auth - Secure admin authentication for Socket.IO connections
Developer Experience
- Visual Admin Panel - Configure everything through the Strapi admin interface
- TypeScript Support - Full type definitions for IntelliSense
- Helper Functions - 12 utility methods for common tasks
- Comprehensive Documentation - Detailed guides and examples
Production Ready
- Redis Adapter - Scale horizontally across multiple servers
- Rate Limiting - Prevent abuse with configurable limits
- Monitoring Dashboard - Live connection stats and event logs
- Security Features - IP whitelisting, authentication, input validation
Quick Start
1. Install the plugin
npm install @strapi-community/plugin-io2. Enable in your Strapi project
Create or update config/plugins.js:
module.exports = {
io: {
enabled: true,
config: {
contentTypes: ['api::article.article'],
socket: {
serverOptions: {
cors: {
origin: 'http://localhost:3000',
methods: ['GET', 'POST']
}
}
}
}
}
};3. Start your Strapi server
npm run develop4. Connect from your frontend
import { io } from 'socket.io-client';
const socket = io('http://localhost:1337');
socket.on('article:create', (article) => {
console.log('New article published:', article);
});
socket.on('article:update', (article) => {
console.log('Article updated:', article);
});That's it! Your application now has real-time updates.
Installation
Requirements
- Node.js: 18.x - 22.x
- Strapi: v5.x
- npm: 6.x or higher
Install the package
# Using npm
npm install @strapi-community/plugin-io
# Using yarn
yarn add @strapi-community/plugin-io
# Using pnpm
pnpm add @strapi-community/plugin-ioConfiguration
Basic Configuration
The simplest setup to get started:
// config/plugins.js
module.exports = {
io: {
enabled: true,
config: {
// Monitor these content types for changes
contentTypes: [
'api::article.article',
'api::comment.comment'
]
}
}
};Advanced Configuration
Fine-tune the plugin behavior:
// config/plugins.js
module.exports = {
io: {
enabled: true,
config: {
// Content types with specific actions and populate
contentTypes: [
{
uid: 'api::article.article',
actions: ['create', 'update'], // Only these events
populate: '*' // Include all relations
},
{
uid: 'api::comment.comment',
actions: ['create', 'delete'],
populate: ['author', 'article'] // Only specific relations
},
{
uid: 'api::order.order',
populate: { // Strapi populate syntax
customer: { fields: ['name', 'email'] },
items: { populate: ['product'] }
}
}
],
// Socket.IO server configuration
socket: {
serverOptions: {
cors: {
origin: process.env.CLIENT_URL || 'http://localhost:3000',
methods: ['GET', 'POST'],
credentials: true
},
pingTimeout: 60000,
pingInterval: 25000
}
},
// Custom event handlers
events: [
{
name: 'connection',
handler: ({ strapi, io }, socket) => {
strapi.log.info(`Client connected: ${socket.id}`);
}
},
{
name: 'disconnect',
handler: ({ strapi, io }, socket) => {
strapi.log.info(`Client disconnected: ${socket.id}`);
}
}
],
// Initialization hook
hooks: {
init: ({ strapi, $io }) => {
strapi.log.info('[Socket.IO] Server initialized');
}
}
}
}
};Populate Configuration
Include relations in emitted events by adding the populate option to content types. When configured, the plugin refetches entities with populated relations after create/update operations.
Populate Formats
| Format | Example | Description |
|---|---|---|
'*' or true | populate: '*' | Include all relations (1 level deep) |
| String array | populate: ['author', 'category'] | Only specific relations |
| Object | populate: { author: { fields: ['name'] } } | Full Strapi populate syntax |
Examples
contentTypes: [
// All relations with wildcard
{ uid: 'api::article.article', populate: '*' },
// Specific relations only
{
uid: 'api::comment.comment',
populate: ['author', 'post']
},
// Strapi populate syntax with field selection
{
uid: 'api::order.order',
populate: {
customer: { fields: ['username', 'email'] },
items: {
populate: { product: { fields: ['name', 'price'] } }
}
}
},
// No populate (only base fields)
{ uid: 'api::log.log' } // populate not set = no relations
]Note: The
populateoption only affectscreateandupdateevents. Delete events always contain minimal data (id, documentId) since the entity no longer exists.
Sensitive Fields Protection
The plugin automatically removes sensitive fields from all emitted data for security. This works in addition to Strapi's built-in private: true field filtering.
Default Blocked Fields
The following fields are always removed from emitted data:
password,salt,hashresetPasswordToken,confirmationTokenrefreshToken,accessToken,tokensecret,apiKey,api_keyprivateKey,private_key
Custom Sensitive Fields
Add your own sensitive fields to the block list:
// config/plugins.js
module.exports = {
io: {
enabled: true,
config: {
contentTypes: ['api::user.user'],
// Additional fields to never emit
sensitiveFields: [
'creditCard',
'ssn',
'socialSecurityNumber',
'bankAccount',
'internalNotes'
]
}
}
};How It Works
- Schema-level filtering: Strapi's
sanitize.contentAPIremovesprivate: truefields - Blocklist filtering: Plugin removes all sensitive field names recursively
- Applies to all emits: Both
emit()andraw()methods are protected
// Even with populate, sensitive fields are stripped
contentTypes: [
{
uid: 'api::user.user',
populate: '*' // Relations included, but passwords etc. still removed
}
]Security Note: Fields are matched case-insensitively and also partial matches work (e.g.,
apiKeyblocksmyApiKey,user_api_key, etc.)
Environment Variables
Recommended environment-based configuration:
// config/plugins.js
module.exports = ({ env }) => ({
io: {
enabled: env.bool('SOCKET_IO_ENABLED', true),
config: {
contentTypes: env.json('SOCKET_IO_CONTENT_TYPES', []),
socket: {
serverOptions: {
cors: {
origin: env('CLIENT_URL', 'http://localhost:3000')
}
}
}
}
}
});# .env
SOCKET_IO_ENABLED=true
CLIENT_URL=https://your-app.com
SOCKET_IO_CONTENT_TYPES=["api::article.article","api::comment.comment"]Usage Examples
Server-Side Usage
Access the Socket.IO instance anywhere in your Strapi application:
// In a controller, service, or lifecycle
const io = strapi.$io;
// Emit to all connected clients
strapi.$io.raw({
event: 'notification',
data: {
message: 'Server maintenance in 5 minutes',
type: 'warning'
}
});
// Send private message to a specific socket
strapi.$io.sendPrivateMessage(socketId, 'order:updated', {
orderId: 123,
status: 'shipped'
});
// Emit to all clients in a room
strapi.$io.server.to('admin-room').emit('dashboard:update', {
activeUsers: 42,
revenue: 15000
});
// Emit to a specific namespace
strapi.$io.emitToNamespace('admin', 'alert', {
message: 'New user registered'
});Client-Side Usage
Basic Connection
import { io } from 'socket.io-client';
const socket = io('http://localhost:1337');
socket.on('connect', () => {
console.log('Connected to server');
});
socket.on('disconnect', () => {
console.log('Disconnected from server');
});
// Listen for content type events
socket.on('article:create', (data) => {
console.log('New article:', data);
});
socket.on('article:update', (data) => {
console.log('Article updated:', data);
});
socket.on('article:delete', (data) => {
console.log('Article deleted:', data.documentId);
});With React
import { useEffect, useState } from 'react';
import { io } from 'socket.io-client';
function ArticlesList() {
const [articles, setArticles] = useState([]);
useEffect(() => {
const socket = io('http://localhost:1337');
// Listen for new articles
socket.on('article:create', (article) => {
setArticles(prev => [article, ...prev]);
});
// Listen for updates
socket.on('article:update', (article) => {
setArticles(prev =>
prev.map(a => a.documentId === article.documentId ? article : a)
);
});
// Listen for deletions
socket.on('article:delete', (data) => {
setArticles(prev =>
prev.filter(a => a.documentId !== data.documentId)
);
});
return () => socket.disconnect();
}, []);
return (
<div>
{articles.map(article => (
<div key={article.documentId}>{article.title}</div>
))}
</div>
);
}With Vue 3
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { io } from 'socket.io-client';
const articles = ref([]);
let socket;
onMounted(() => {
socket = io('http://localhost:1337');
socket.on('article:create', (article) => {
articles.value = [article, ...articles.value];
});
socket.on('article:update', (article) => {
const index = articles.value.findIndex(a => a.documentId === article.documentId);
if (index !== -1) {
articles.value[index] = article;
}
});
socket.on('article:delete', (data) => {
articles.value = articles.value.filter(a => a.documentId !== data.documentId);
});
});
onUnmounted(() => {
socket?.disconnect();
});
</script>
<template>
<div v-for="article in articles" :key="article.documentId">
{{ article.title }}
</div>
</template>Entity-Specific Subscriptions
Subscribe to updates for specific entities only:
// Client-side: Subscribe to a specific article
socket.emit('subscribe-entity', {
uid: 'api::article.article',
id: 123
}, (response) => {
if (response.success) {
console.log('Subscribed to article 123');
}
});
// Now you only receive updates for article 123
socket.on('article:update', (data) => {
console.log('Article 123 was updated:', data);
});
// Unsubscribe when done
socket.emit('unsubscribe-entity', {
uid: 'api::article.article',
id: 123
});Benefits:
- Reduced bandwidth - only receive relevant updates
- Better performance - less client-side processing
- Built-in permission checks - respects user roles
Room Management
Organize connections into rooms:
// Server-side: Add socket to a room
strapi.$io.joinRoom(socketId, 'premium-users');
// Get all sockets in a room
const sockets = await strapi.$io.getSocketsInRoom('premium-users');
console.log(`${sockets.length} premium users online`);
// Broadcast to a specific room
strapi.$io.server.to('premium-users').emit('exclusive-offer', {
discount: 20,
expiresIn: '24h'
});
// Remove socket from a room
strapi.$io.leaveRoom(socketId, 'premium-users');
// Disconnect a specific socket
strapi.$io.disconnectSocket(socketId, 'Kicked by admin');Helper Functions
The plugin provides 12 utility functions available on strapi.$io:
Room Management
joinRoom(socketId, roomName)
Add a socket to a room.
const success = strapi.$io.joinRoom(socketId, 'premium-users');
// Returns: true if socket found and joined, false otherwiseleaveRoom(socketId, roomName)
Remove a socket from a room.
const success = strapi.$io.leaveRoom(socketId, 'premium-users');
// Returns: true if socket found and left, false otherwisegetSocketsInRoom(roomName)
Get all sockets in a specific room.
const sockets = await strapi.$io.getSocketsInRoom('premium-users');
// Returns: [{ id: 'socket-id', user: { username: 'john' } }, ...]Messaging
sendPrivateMessage(socketId, event, data)
Send a message to a specific socket.
strapi.$io.sendPrivateMessage(socketId, 'order:shipped', {
orderId: 123,
trackingNumber: 'ABC123'
});broadcast(socketId, event, data)
Broadcast from a socket to all other connected clients.
strapi.$io.broadcast(socketId, 'user:typing', {
username: 'john',
channel: 'general'
});emitToNamespace(namespace, event, data)
Emit an event to all clients in a namespace.
strapi.$io.emitToNamespace('admin', 'alert', {
message: 'New user registered',
level: 'info'
});Connection Management
disconnectSocket(socketId, reason)
Force disconnect a specific socket.
const success = strapi.$io.disconnectSocket(socketId, 'Session expired');
// Returns: true if socket found and disconnected, false otherwiseEntity Subscriptions
subscribeToEntity(socketId, uid, id)
Subscribe a socket to a specific entity (server-side).
const result = await strapi.$io.subscribeToEntity(socketId, 'api::article.article', 123);
// Returns: { success: true, room: 'api::article.article:123', uid, id }
// or: { success: false, error: 'Socket not found' }unsubscribeFromEntity(socketId, uid, id)
Unsubscribe a socket from a specific entity.
const result = strapi.$io.unsubscribeFromEntity(socketId, 'api::article.article', 123);
// Returns: { success: true, room: 'api::article.article:123', uid, id }getEntitySubscriptions(socketId)
Get all entity subscriptions for a socket.
const result = strapi.$io.getEntitySubscriptions(socketId);
// Returns: {
// success: true,
// subscriptions: [
// { uid: 'api::article.article', id: '123', room: 'api::article.article:123' }
// ]
// }emitToEntity(uid, id, event, data)
Emit an event to all clients subscribed to a specific entity.
strapi.$io.emitToEntity('api::article.article', 123, 'article:commented', {
commentId: 456,
author: 'jane'
});getEntityRoomSockets(uid, id)
Get all sockets subscribed to a specific entity.
const sockets = await strapi.$io.getEntityRoomSockets('api::article.article', 123);
// Returns: [{ id: 'socket-id', user: { username: 'john' } }, ...]Authentication
The plugin supports multiple authentication strategies.
Public Access (No Authentication)
const socket = io('http://localhost:1337');
// No auth - placed in 'Public' role roomJWT Authentication (Users & Permissions)
// 1. Get JWT token from login
const response = await fetch('http://localhost:1337/api/auth/local', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
identifier: 'user@example.com',
password: 'password123'
})
});
const { jwt } = await response.json();
// 2. Connect with JWT
const socket = io('http://localhost:1337', {
auth: {
strategy: 'jwt',
token: jwt
}
});
// User is placed in their role room (e.g., 'Authenticated')API Token Authentication
// 1. Create API token in Strapi Admin:
// Settings > Global Settings > API Tokens > Create new token
// 2. Connect with API token
const socket = io('http://localhost:1337', {
auth: {
strategy: 'api-token',
token: 'your-api-token-here'
}
});Permission Enforcement
Events are automatically filtered based on the user's role:
// Authenticated user with 'Editor' role
socket.on('article:create', (data) => {
// Only receives events for content types they have permission to access
});Configure permissions in the Strapi admin panel: 1. Go to Settings > Users & Permissions > Roles 2. Select a role (e.g., "Authenticated") 3. Configure Socket.IO permissions per content type
Security
The plugin implements multiple security layers to protect your real-time connections.
Admin Session Tokens
For admin panel connections (Live Presence), the plugin uses secure session tokens:
+------------------+ +------------------+ +------------------+
| Admin Browser | ---> | Session Endpoint| ---> | Socket.IO |
| (Strapi Admin) | | /io/presence/ | | Server |
+------------------+ +------------------+ +------------------+
| | |
| 1. Request session | |
| (Admin JWT in header) | |
+------------------------>| |
| | |
| 2. Return session token | |
| (UUID, 10 min TTL) | |
|<------------------------+ |
| | |
| 3. Connect Socket.IO | |
| (Session token in auth) | |
+-------------------------------------------------->|
| | |
| 4. Validate & connect | |
|<--------------------------------------------------+Security Features:
- Token Hashing: Tokens stored as SHA-256 hashes (plaintext never persisted)
- Short TTL: 10-minute expiration with automatic refresh at 70%
- Usage Limits: Max 10 reconnects per token to prevent replay attacks
- Rate Limiting: 30-second cooldown between token requests
- Minimal Data: Only essential user info stored (ID, firstname, lastname)
Rate Limiting
Prevent abuse with configurable rate limits:
// In config/plugins.js
module.exports = {
io: {
enabled: true,
config: {
security: {
rateLimit: {
enabled: true,
maxEventsPerSecond: 10, // Max events per socket per second
maxConnectionsPerIp: 50 // Max connections from single IP
}
}
}
}
};IP Whitelisting/Blacklisting
Restrict access by IP address:
// In config/plugins.js
module.exports = {
io: {
enabled: true,
config: {
security: {
ipWhitelist: ['192.168.1.0/24', '10.0.0.1'], // Only these IPs allowed
ipBlacklist: ['203.0.113.50'], // These IPs blocked
requireAuthentication: true // Require JWT/API token
}
}
}
};Security Monitoring API
Monitor active sessions via admin API:
# Get session statistics
GET /io/security/sessions
Authorization: Bearer <admin-jwt>
# Response:
{
"data": {
"activeSessions": 5,
"expiringSoon": 1,
"activeSocketConnections": 3,
"sessionTTL": 600000,
"refreshCooldown": 30000
}
}
# Force logout a user (invalidate all their sessions)
POST /io/security/invalidate/:userId
Authorization: Bearer <admin-jwt>
# Response:
{
"data": {
"userId": 1,
"invalidatedSessions": 2,
"message": "Successfully invalidated 2 session(s)"
}
}Best Practices
- Always use HTTPS in production for encrypted WebSocket connections
- Enable authentication for sensitive content types
- Configure CORS to only allow your frontend domains
- Monitor connections via the admin dashboard
- Set reasonable rate limits based on your use case
- Review access logs periodically for suspicious activity
Admin Panel
The plugin provides a full admin interface for configuration and monitoring.
Dashboard Widgets
Requires Strapi v5.13+
After installation, live statistics widgets appear on your Strapi admin homepage:
Socket.IO Statistics Widget
Shows:
- Live connection status (pulsing indicator when active)
- Active connections count
- Active rooms count
- Events per second
- Total events processed since startup
Updates automatically every 5 seconds.
Who's Online Widget
Shows:
- List of currently online admin users
- User avatars with role badges
- Online status and last activity
- Quick access to view all activity
Settings Page
Navigate to Settings > Socket.IO > Settings for visual configuration:
Path: /admin/settings/io/settings
General Settings
- Enable/disable the plugin
- Configure CORS origins
- Set server options (ping timeout, etc.)
Content Types
- Enable automatic events for content types per role
- Select specific actions (create, update, delete)
- Configure role-based permissions
Events
- Include relations in events (
includeRelations) - Custom event names
- Exclude specific fields
- Only published content mode
Security
- Require authentication
- Rate limiting configuration
- IP whitelisting/blacklisting
- Input validation rules
Rooms
- Auto-join by role configuration
- Enable/disable private rooms
Advanced
- Configure namespaces
- Redis adapter settings for scaling
- Entity subscription limits
Monitoring Page
Navigate to Settings > Socket.IO > Monitoring for live statistics:
Path: /admin/settings/io/monitoring
- View active connections with user details
- See event logs in real-time
- Monitor performance metrics (events/second)
- View entity subscription statistics
- Send test events
- Reset statistics
Live Presence Panel
When editing content in the Content Manager, a Live Presence panel appears in the sidebar showing:
- Connection Status - Live indicator showing real-time sync is active
- Active Editors - List of other users editing the same content
- Typing Indicator - Shows when someone is typing and in which field
How It Works:
- When you open a content entry, the panel connects via Socket.IO
- Other editors on the same entry appear in the panel
- Typing in any field broadcasts a typing indicator to others
- When you leave, others are notified
Monitoring Service
Access monitoring data programmatically via the monitoring service:
const monitoringService = strapi.plugin('io').service('monitoring');Available Methods
getConnectionStats()
Get current connection statistics.
const stats = monitoringService.getConnectionStats();
// Returns:
// {
// connected: 42,
// rooms: [
// { name: 'Authenticated', members: 35, isEntityRoom: false },
// { name: 'api::article.article:123', members: 5, isEntityRoom: true }
// ],
// sockets: [
// {
// id: 'abc123',
// connected: true,
// rooms: ['Authenticated'],
// entitySubscriptions: [{ uid: 'api::article.article', id: '123', room: '...' }],
// handshake: { address: '::1', time: '...', query: {} },
// user: { id: 1, username: 'john' }
// }
// ],
// entitySubscriptions: {
// total: 15,
// byContentType: { 'api::article.article': 10, 'api::comment.comment': 5 },
// rooms: ['api::article.article:123', ...]
// }
// }getEventStats()
Get event statistics.
const stats = monitoringService.getEventStats();
// Returns:
// {
// totalEvents: 1234,
// eventsByType: { 'create': 500, 'update': 600, 'delete': 134 },
// lastReset: 1703123456789,
// eventsPerSecond: '2.50'
// }getEventLog(limit)
Get recent event log entries.
const logs = monitoringService.getEventLog(50);
// Returns:
// [
// { timestamp: 1703123456789, type: 'create', data: {...} },
// { timestamp: 1703123456790, type: 'connect', data: {...} }
// ]logEvent(type, data)
Manually log an event.
monitoringService.logEvent('custom-event', {
action: 'user-action',
userId: 123
});resetStats()
Reset all statistics and clear event log.
monitoringService.resetStats();sendTestEvent(eventName, data)
Send a test event to all connected clients.
const result = monitoringService.sendTestEvent('test', { message: 'Hello!' });
// Returns:
// {
// success: true,
// eventName: 'test',
// data: { message: 'Hello!', timestamp: 1703123456789, test: true },
// recipients: 42
// }Use Cases
Custom Analytics Dashboard:
// In a custom controller
async getAnalytics(ctx) {
const monitoring = strapi.plugin('io').service('monitoring');
ctx.body = {
connections: monitoring.getConnectionStats(),
events: monitoring.getEventStats(),
recentActivity: monitoring.getEventLog(10)
};
}Health Check Endpoint:
async healthCheck(ctx) {
const monitoring = strapi.plugin('io').service('monitoring');
const stats = monitoring.getConnectionStats();
ctx.body = {
status: 'healthy',
websocket: {
connected: stats.connected,
rooms: stats.rooms.length
}
};
}TypeScript Support
Full TypeScript definitions are included for excellent IDE support.
Import Types
import type {
SocketIO,
SocketIOConfig,
EmitOptions,
RawEmitOptions,
PluginSettings,
MonitoringService,
SettingsService,
ConnectionStats,
EventStats
} from '@strapi-community/plugin-io/types';Configuration Example
// config/plugins.ts
import type { SocketIOConfig } from '@strapi-community/plugin-io/types';
export default {
io: {
enabled: true,
config: {
contentTypes: [
{
uid: 'api::article.article',
actions: ['create', 'update', 'delete']
}
],
socket: {
serverOptions: {
cors: {
origin: process.env.CLIENT_URL || 'http://localhost:3000',
methods: ['GET', 'POST']
}
}
}
} satisfies SocketIOConfig
}
};Usage Example
// In your Strapi code
import type { SocketIO } from '@strapi-community/plugin-io/types';
// Type-safe access
const io: SocketIO = strapi.$io;
// All methods have full IntelliSense
await io.emit({
event: 'create',
schema: strapi.contentTypes['api::article.article'],
data: { title: 'New Article' }
});
// Helper functions are typed
const sockets = await io.getSocketsInRoom('premium-users');
io.sendPrivateMessage(sockets[0].id, 'welcome', { message: 'Hello!' });Performance
The plugin is optimized for production environments.
Benchmarks
- Concurrent Connections: 2500+ simultaneous connections
- Memory Usage: ~17KB per connection
- Event Throughput: 10,000+ events/second
- Latency: <10ms for local broadcasts
Optimizations
Intelligent Caching
Role and permission data is cached for 5 minutes, reducing database queries by up to 90%.
Debouncing
Bulk operations are automatically debounced to prevent event flooding during data imports.
Parallel Processing
All event emissions are processed in parallel for maximum throughput.
Connection Pooling
Efficient connection management with automatic cleanup of stale connections.
Production Configuration
// config/plugins.js (production)
module.exports = ({ env }) => ({
io: {
enabled: true,
config: {
contentTypes: env.json('SOCKET_IO_CONTENT_TYPES'),
socket: {
serverOptions: {
cors: {
origin: env('CLIENT_URL'),
credentials: true
},
// Optimize for production
pingTimeout: 60000,
pingInterval: 25000,
maxHttpBufferSize: 1e6,
transports: ['websocket', 'polling']
}
},
// Use Redis for horizontal scaling
hooks: {
init: async ({ strapi, $io }) => {
const { createAdapter } = require('@socket.io/redis-adapter');
const { createClient } = require('redis');
const pubClient = createClient({ url: env('REDIS_URL') });
const subClient = pubClient.duplicate();
await Promise.all([pubClient.connect(), subClient.connect()]);
$io.server.adapter(createAdapter(pubClient, subClient));
strapi.log.info('[Socket.IO] Redis adapter connected');
}
}
}
}
});Migration Guide
From v2 (Strapi v4) to v5 (Strapi v5)
Good news: The API is 100% compatible! Most projects migrate in under 1 hour.
Quick Migration Steps
Update Strapi to v5
npm install @strapi/strapi@5 @strapi/plugin-users-permissions@5Update the plugin
npm uninstall strapi-plugin-io npm install @strapi-community/plugin-io@latestTest your application
npm run develop
Your configuration stays the same - no code changes needed!
What Changed
- Package name:
strapi-plugin-io->@strapi-community/plugin-io - Package structure: Uses new Strapi v5 Plugin SDK
- Dependencies: Updated to Strapi v5 peer dependencies
- Build process: Optimized build with modern tooling
What Stayed the Same
- All API methods work identically
- Configuration format unchanged
- Client-side code works as-is
- Same helper functions
- Same event format
For detailed migration instructions, see docs/guide/migration.md.
Documentation
Official Documentation
- Online Documentation - Complete interactive docs
- API Reference - All methods and properties
- Configuration Guide - Detailed configuration options
- Usage Examples - Real-world use cases
- Migration Guide - Upgrade from v4 to v5
Guides
- Getting Started - Step-by-step setup
- Widget Guide - Dashboard widget configuration
Examples
- Content Types - Automatic CRUD events
- Custom Events - Server-client communication
- Hooks & Adapters - Redis, MongoDB integration
Related Plugins
Build complete real-time applications with these complementary Strapi v5 plugins:
Magic-Mail
Enterprise email management with OAuth 2.0 support. Perfect for sending transactional emails triggered by Socket.IO events.
Use case: Send email notifications when real-time events occur.
Magic-Sessionmanager
Advanced session tracking and monitoring. Track Socket.IO connections, monitor active users, and analyze session patterns.
Use case: Monitor who's connected to your WebSocket server in real-time.
Magicmark
Bookmark management system with real-time sync. Share bookmarks instantly with your team using Socket.IO integration.
Use case: Collaborative bookmark management with live updates.
Contributing
We welcome contributions! Here's how you can help:
Report Bugs
Found a bug? Open an issue with:
- Strapi version
- Plugin version
- Steps to reproduce
- Expected vs actual behavior
Suggest Features
Have an idea? Start a discussion to:
- Describe the feature
- Explain the use case
- Discuss implementation
Submit Pull Requests
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Development Setup
# Clone the repository
git clone https://github.com/strapi-community/strapi-plugin-io.git
cd strapi-plugin-io
# Install dependencies
npm install
# Build the plugin
npm run build
# Run in watch mode
npm run watch
# Verify structure
npm run verifySupport
- Documentation: https://strapi-plugin-io.netlify.app/
- GitHub Issues: https://github.com/strapi-community/strapi-plugin-io/issues
- GitHub Discussions: https://github.com/strapi-community/strapi-plugin-io/discussions
- Strapi Discord: https://discord.strapi.io
License
Copyright (c) 2024 Strapi Community
Credits
Original Authors:
Enhanced and Maintained by:
Maintained until: December 2026
Changelog
v5.1.0 (Latest)
- Live Presence System - Real-time presence awareness in Content Manager
- Typing Indicator - See when others are typing and in which field
- Admin Panel Sidebar - Live presence panel integrated into edit view
- Admin Session Authentication - Secure session tokens for Socket.IO
- Admin JWT Strategy - New authentication strategy for admin users
- Enhanced Security - Token hashing (SHA-256), usage limits, rate limiting
- Automatic Token Refresh - Tokens auto-refresh at 70% of TTL
- Security Monitoring API - Session stats and force-logout endpoints
v5.0.0
- Strapi v5 support
- Package renamed to
@strapi-community/plugin-io - Enhanced TypeScript support
- Entity-specific subscriptions
- 12 helper functions
- Admin panel with monitoring dashboard
- Performance optimizations
- Updated documentation
For full changelog, see CHANGELOG.md.
Made with love for the Strapi community
Install now
npm install @strapi-community/plugin-io
Create your own plugin
Check out the available plugin resources that will help you to develop your plugin or provider and get it listed on the marketplace.