A plugin for Strapi CMS that provides the ability for Socket IO integration
npm install @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.
---
- Features
- Quick Start
- Installation
- Configuration
- Usage Examples
- Helper Functions
- Authentication
- Admin Panel
- Monitoring Service
- TypeScript Support
- Performance
- Migration Guide
- Documentation
- Contributing
- License
---
---
``bash`
npm install @strapi-community/plugin-io
Create or update config/plugins.js:
`javascript`
module.exports = {
io: {
enabled: true,
config: {
contentTypes: ['api::article.article'],
socket: {
serverOptions: {
cors: {
origin: 'http://localhost:3000',
methods: ['GET', 'POST']
}
}
}
}
}
};
`bash`
npm run develop
`javascript
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.
---
- Node.js: 18.x - 22.x
- Strapi: v5.x
- npm: 6.x or higher
`bashUsing npm
npm install @strapi-community/plugin-io
---
Configuration
$3
The simplest setup to get started:
`javascript
// config/plugins.js
module.exports = {
io: {
enabled: true,
config: {
// Monitor these content types for changes
contentTypes: [
'api::article.article',
'api::comment.comment'
]
}
}
};
`$3
Fine-tune the plugin behavior:
`javascript
// 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');
}
}
}
}
};
`$3
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
`javascript
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
populate option only affects create and update events. Delete events always contain minimal data (id, documentId) since the entity no longer exists.$3
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, hash
- resetPasswordToken, confirmationToken
- refreshToken, accessToken, token
- secret, apiKey, api_key
- privateKey, private_key#### Custom Sensitive Fields
Add your own sensitive fields to the block list:
`javascript
// 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
1. Schema-level filtering: Strapi's
sanitize.contentAPI removes private: true fields
2. Blocklist filtering: Plugin removes all sensitive field names recursively
3. Applies to all emits: Both emit() and raw() methods are protected`javascript
// 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.,
apiKey blocks myApiKey, user_api_key, etc.)$3
Recommended environment-based configuration:
`javascript
// 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
.env
SOCKET_IO_ENABLED=true
CLIENT_URL=https://your-app.com
SOCKET_IO_CONTENT_TYPES=["api::article.article","api::comment.comment"]
`---
Usage Examples
$3
Access the Socket.IO instance anywhere in your Strapi application:
`javascript
// 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'
});
`$3
#### Basic Connection
`javascript
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
`jsx
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 (
{articles.map(article => (
{article.title}
))}
);
}
`#### With Vue 3
`vue
{{ article.title }}
`$3
Subscribe to updates for specific entities only:
`javascript
// 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
$3
Organize connections into rooms:
`javascript
// 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:$3
####
joinRoom(socketId, roomName)
Add a socket to a room.`javascript
const success = strapi.$io.joinRoom(socketId, 'premium-users');
// Returns: true if socket found and joined, false otherwise
`####
leaveRoom(socketId, roomName)
Remove a socket from a room.`javascript
const success = strapi.$io.leaveRoom(socketId, 'premium-users');
// Returns: true if socket found and left, false otherwise
`####
getSocketsInRoom(roomName)
Get all sockets in a specific room.`javascript
const sockets = await strapi.$io.getSocketsInRoom('premium-users');
// Returns: [{ id: 'socket-id', user: { username: 'john' } }, ...]
`$3
####
sendPrivateMessage(socketId, event, data)
Send a message to a specific socket.`javascript
strapi.$io.sendPrivateMessage(socketId, 'order:shipped', {
orderId: 123,
trackingNumber: 'ABC123'
});
`####
broadcast(socketId, event, data)
Broadcast from a socket to all other connected clients.`javascript
strapi.$io.broadcast(socketId, 'user:typing', {
username: 'john',
channel: 'general'
});
`####
emitToNamespace(namespace, event, data)
Emit an event to all clients in a namespace.`javascript
strapi.$io.emitToNamespace('admin', 'alert', {
message: 'New user registered',
level: 'info'
});
`$3
####
disconnectSocket(socketId, reason)
Force disconnect a specific socket.`javascript
const success = strapi.$io.disconnectSocket(socketId, 'Session expired');
// Returns: true if socket found and disconnected, false otherwise
`$3
####
subscribeToEntity(socketId, uid, id)
Subscribe a socket to a specific entity (server-side).`javascript
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.`javascript
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.`javascript
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.`javascript
strapi.$io.emitToEntity('api::article.article', 123, 'article:commented', {
commentId: 456,
author: 'jane'
});
`####
getEntityRoomSockets(uid, id)
Get all sockets subscribed to a specific entity.`javascript
const sockets = await strapi.$io.getEntityRoomSockets('api::article.article', 123);
// Returns: [{ id: 'socket-id', user: { username: 'john' } }, ...]
`---
Authentication
The plugin supports multiple authentication strategies.
$3
`javascript
const socket = io('http://localhost:1337');
// No auth - placed in 'Public' role room
`$3
`javascript
// 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')
`$3
`javascript
// 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'
}
});
`$3
Events are automatically filtered based on the user's role:
`javascript
// 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.
$3
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)
$3
Prevent abuse with configurable rate limits:
`javascript
// 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
}
}
}
}
};
`$3
Restrict access by IP address:
`javascript
// 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
}
}
}
};
`$3
Monitor active sessions via admin API:
`bash
Get session statistics
GET /io/security/sessions
Authorization: Bearer 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 Response:
{
"data": {
"userId": 1,
"invalidatedSessions": 2,
"message": "Successfully invalidated 2 session(s)"
}
}
`$3
1. Always use HTTPS in production for encrypted WebSocket connections
2. Enable authentication for sensitive content types
3. Configure CORS to only allow your frontend domains
4. Monitor connections via the admin dashboard
5. Set reasonable rate limits based on your use case
6. Review access logs periodically for suspicious activity
---
Admin Panel
The plugin provides a full admin interface for configuration and monitoring.
$3
> 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
$3
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
$3
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
$3
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:
1. When you open a content entry, the panel connects via Socket.IO
2. Other editors on the same entry appear in the panel
3. Typing in any field broadcasts a typing indicator to others
4. When you leave, others are notified
---
Monitoring Service
Access monitoring data programmatically via the monitoring service:
`javascript
const monitoringService = strapi.plugin('io').service('monitoring');
`$3
####
getConnectionStats()
Get current connection statistics.`javascript
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.`javascript
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.`javascript
const logs = monitoringService.getEventLog(50);
// Returns:
// [
// { timestamp: 1703123456789, type: 'create', data: {...} },
// { timestamp: 1703123456790, type: 'connect', data: {...} }
// ]
`####
logEvent(type, data)
Manually log an event.`javascript
monitoringService.logEvent('custom-event', {
action: 'user-action',
userId: 123
});
`####
resetStats()
Reset all statistics and clear event log.`javascript
monitoringService.resetStats();
`####
sendTestEvent(eventName, data)
Send a test event to all connected clients.`javascript
const result = monitoringService.sendTestEvent('test', { message: 'Hello!' });
// Returns:
// {
// success: true,
// eventName: 'test',
// data: { message: 'Hello!', timestamp: 1703123456789, test: true },
// recipients: 42
// }
`$3
Custom Analytics Dashboard:
`javascript
// 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:
`javascript
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.
$3
`typescript
import type {
SocketIO,
SocketIOConfig,
EmitOptions,
RawEmitOptions,
PluginSettings,
MonitoringService,
SettingsService,
ConnectionStats,
EventStats
} from '@strapi-community/plugin-io/types';
`$3
`typescript
// 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
}
};
`$3
`typescript
// 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.
$3
- Concurrent Connections: 2500+ simultaneous connections
- Memory Usage: ~17KB per connection
- Event Throughput: 10,000+ events/second
- Latency: <10ms for local broadcasts
$3
#### 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.
$3
`javascript
// 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
$3
Good news: The API is 100% compatible! Most projects migrate in under 1 hour.
#### Quick Migration Steps
1. Update Strapi to v5
`bash
npm install @strapi/strapi@5 @strapi/plugin-users-permissions@5
`2. Update the plugin
`bash
npm uninstall strapi-plugin-io
npm install @strapi-community/plugin-io@latest
`3. Test your application
`bash
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
$3
- 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
$3
- Getting Started - Step-by-step setup
- Widget Guide - Dashboard widget configuration
$3
- 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:
$3
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.
$3
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.
$3
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:
$3
Found a bug? Open an issue with:
- Strapi version
- Plugin version
- Steps to reproduce
- Expected vs actual behavior
$3
Have an idea? Start a discussion to:
- Describe the feature
- Explain the use case
- Discuss implementation
$3
1. Fork the repository
2. Create a feature branch (
git checkout -b feature/amazing-feature)
3. Commit your changes (git commit -m 'Add amazing feature')
4. Push to the branch (git push origin feature/amazing-feature)
5. Open a Pull Request$3
`bash
Clone the repository
git clone https://github.com/strapi-community/strapi-plugin-io.git
cd strapi-plugin-ioInstall dependencies
npm installBuild the plugin
npm run buildRun in watch mode
npm run watchVerify structure
npm run verify
`---
Support
- 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:
- @ComfortablyCoding
- @hrdunn
Enhanced and Maintained by:
- @Schero94
Maintained until: December 2026
---
Changelog
$3
- 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$3
- Strapi v5 support
- Package renamed to @strapi-community/plugin-io`For full changelog, see CHANGELOG.md.
---