sfcod/socketio
Socket.IO integration for Laravel applications that makes adding real-time, bidirectional communication simple. Broadcast server events to connected clients, handle live updates over websockets, and build interactive features with a familiar, easy-to-use API.
Install the package and its published config in three commands:
composer require sfcod/socketio
php artisan vendor:publish --provider="Sfcod\Socketio\SocketioServiceProvider"
php artisan socketio:serve # or whatever the serve command is named in the package
First place to look is the published config/socketio.php (host, port, worker count, Redis adapter toggle) and the service provider's register()/boot() to see which artisan commands and bindings it actually registers — the package is small enough that reading src/ end-to-end takes ten minutes and is worth it, since documentation is thin.
The first use case is almost always the same: a Laravel app that already broadcasts events via broadcastOn()/ShouldBroadcast, and you want a working Socket.IO endpoint without standing up a Node server. Wire the frontend socket.io-client to the port the artisan serve command exposes, fire a test event from php artisan tinker, and confirm it lands in the browser. That round-trip validates the three things that usually break: the Redis adapter, the auth handshake, and the event-name mapping.
Event broadcasting as the primary path. Treat the package as a transport layer behind Laravel's existing broadcast system. Define events with ShouldBroadcast, let the package's queue/bridge deliver them to the Socket.IO server, and keep your controllers unaware of Socket.IO entirely. This is the pattern that survives upgrades and lets you swap in Pusher/Reverb later unchanged.
Redis as the inter-process bus. Nearly every real deployment runs more than one PHP-FPM process, so you need the Redis adapter (or the package's equivalent pub/sub bridge). Run one socketio:serve worker group and let all web workers publish to Redis. Without this, broadcasts only reach clients connected to the same process that handled the request — a bug that looks like "sometimes messages are missing."
Custom event handlers for non-broadcast traffic. For presence, typing indicators, or any client→server message that isn't a Laravel event, register handler classes in the config's event map. Keep handlers idempotent and free of Eloquent writes where possible; they run inside the long-lived worker, so a slow query stalls the event loop.
Supervisor + dedicated queue worker. The serve command is a long-running process. Run it under Supervisor with autorestart=true, and run a separate queue:work for the broadcast events. Deploying means supervisorctl restart socketio:* — plan that into your deploy script, because a stale worker holds old code forever.
Auth at handshake time. Use the package's connection callback (usually configured in config/socketio.php or an auth class) to validate the session token or Sanctum guard before joining private channels. The pattern: frontend sends auth: { token } in the io() options, worker validates against the DB/cache, rejects or attaches user_id to the socket for later toUser() calls.
Room conventions. Mirror Laravel's private/presence channel naming (private-user.1, presence-chat.5) so the same authorization logic drives both Pusher-style and Socket.IO-style delivery. Then a single authorize() method covers both transports.
composer.json and be prepared to fork. Verify compatibility with your PHP and Laravel versions before committing — a two-year-old package may not declare support for Laravel 11 and Composer will refuse to install, or install something that fails at runtime.broadcast()->toOthers() semantics differ subtly between the Laravel broadcaster and a raw Socket.IO emit. Pick one style per feature; mixing produces duplicate events on some clients and none on others.Auth::user() inside a handler, it will be null. Authenticate in the handshake and stash the identity on the socket, never assume the session is shared.config/cors.php. If the browser console shows a failed upgrade, it's almost always the package's own origin allowlist, not Laravel's.transports: ['websocket'] in the client if you can't configure stickiness.Log::debug in the connection and message hooks first; dd()/dump() will kill the worker. Watch storage/logs/laravel.log and the Supervisor stdout log together — the package's own errors often bypass the Laravel log.ServiceProvider::register() to override defaults rather than editing vendor files; it's the only upgrade-safe path in a package this lightly maintained.How can I help you explore Laravel packages today?