fuyad / chat
Laravel real-time chat package with Reverb WebSocket support, typing indicators, read receipts, and presence.
Requires
- php: ^8.2
- laravel/framework: ^11.0|^12.0|^13.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- laravel/pint: ^1.0
- phpunit/phpunit: ^11.0
Suggests
- laravel/reverb: WebSocket server for real-time chat broadcasting
- laravel/sanctum: When using API routes for SPA authentication
This package is auto-updated.
Last update: 2026-05-22 10:36:59 UTC
README
A real-time chat package for Laravel inspired by Facebook Messenger with support for direct messages, group chats, media sharing, typing indicators, read receipts, and online status.
Features
- ✅ Direct Messaging - One-to-one conversations
- ✅ Group Chats - Multi-user conversations
- ✅ Real-time Messaging - Instant message delivery using WebSockets (Reverb)
- ✅ Typing Indicators - "User is typing..." functionality
- ✅ Read Receipts - Message status (sent, delivered, read)
- ✅ Online Status - User presence tracking
- ✅ Message Reactions - Emoji reactions to messages
- ✅ Message Editing - Edit sent messages
- ✅ Message Deletion - Soft delete with notifications
- ✅ User Blocking - Block/unblock users
- ✅ Message Search - Search across conversations
- ✅ Media Sharing - Images, files, and links support
- ✅ Notification Badges - Unread message counts
Installation
1. Install the package via Composer
composer require fuyad/chat
2. Publish the configuration
php artisan vendor:publish --provider="Fuyad\Chat\ChatServiceProvider" --tag="chat-config"
3. Run the migrations
php artisan migrate
4. Configure Broadcasting (Laravel Reverb)
php artisan install:broadcasting php artisan reverb:start
Add to your .env file:
BROADCAST_CONNECTION=reverb REVERB_APP_ID=your_app_id REVERB_APP_KEY=your_app_key REVERB_APP_SECRET=your_app_secret REVERB_HOST=127.0.0.1 REVERB_PORT=8080 REVERB_SCHEME=http VITE_REVERB_APP_KEY="${REVERB_APP_KEY}" VITE_REVERB_HOST="${REVERB_HOST}" VITE_REVERB_PORT="${REVERB_PORT}" VITE_REVERB_SCHEME="${REVERB_SCHEME}"
5. Publish the React chat UI
php artisan vendor:publish --tag=chat-ui
Wire the published page in your app (e.g. Inertia route to resources/js/vendor/chat/ChatPage.tsx).
Frontend dependencies (host app):
@laravel/echo-react,laravel-echo,pusher-js- A Dialog UI component at
@/components/ui/dialog(used by the members popup) - Call
configureEcho({ broadcaster: 'reverb', ... })in your app entry (e.g.app.tsx)
See PUBLISHING.md for Packagist release and sync steps.
Database Schema
The package creates the following tables:
conversations
id- Primary keytype- 'direct' or 'group'name- Conversation name (nullable)avatar_path- Avatar URL (nullable)created_by- User ID who created the conversationtimestamps
conversation_members
id- Primary keyconversation_id- Foreign key to conversationsuser_id- Foreign key to usersjoined_at- When user joinedleft_at- When user left (nullable)is_admin- Boolean flag for admin statusmuted_at- When user muted (nullable)blocked_at- When blocked (nullable)
messages
id- Primary keyconversation_id- Foreign key to conversationsuser_id- Foreign key to users (sender)body- Message contentedited_at- When message was editeddeleted_at- Soft delete timestamptimestamps
message_attachments
id- Primary keymessage_id- Foreign key to messagesfile_path- Path to the filefile_type- Type of file (image, document, video, audio)file_size- Size in bytesmime_type- MIME type of filetimestamps
message_reactions
id- Primary keymessage_id- Foreign key to messagesuser_id- Foreign key to usersreaction- Emoji or reaction stringcreated_at
read_receipts
id- Primary keymessage_id- Foreign key to messagesuser_id- Foreign key to usersread_at- When message was read
user_blocks
id- Primary keyblocker_id- User who blockedblocked_user_id- User who was blockedcreated_at
API Endpoints
Conversations
GET /api/conversations - List all conversations
POST /api/conversations - Create new conversation
GET /api/conversations/{id} - Get conversation details
PATCH /api/conversations/{id} - Update conversation
DELETE /api/conversations/{id} - Archive conversation
GET /api/conversations/{id}/members - Get members
POST /api/conversations/{id}/members - Add member
DELETE /api/conversations/{id}/members/{userId} - Remove member
GET /api/conversations/search - Search conversations
Messages
GET /api/conversations/{id}/messages - List messages (paginated)
POST /api/conversations/{id}/messages - Send message
PATCH /api/messages/{id} - Edit message
DELETE /api/messages/{id} - Delete message
POST /api/messages/{id}/read - Mark as read
POST /api/conversations/{id}/mark-as-read - Mark all as read
POST /api/messages/{id}/reactions - Add reaction
DELETE /api/messages/{id}/reactions/{reaction} - Remove reaction
GET /api/conversations/{id}/search-messages - Search messages
Presence & Status
POST /api/presence/online - Set user online
POST /api/presence/offline - Set user offline
GET /api/presence/{userId}/check - Check user status
POST /api/presence/typing - Broadcast typing indicator
User Blocking
POST /api/users/{userId}/block - Block user
DELETE /api/users/{userId}/block - Unblock user
GET /api/users/blocked - Get blocked users list
WebSocket Events
Server to Client Events
message:received - New message arrived
message:delivered - Message delivered to conversation
message:read - Message(s) read by a user
message:updated - Message was edited
message:deleted - Message was deleted
message:reaction:added - Reaction added to message
user:typing - User is typing
user:online - User came online
user:offline - User went offline
Services
ConversationService
// Create a conversation $conversation = app(ConversationService::class)->create( 'group', // type $userId, // created_by $memberIds, // array of user IDs 'Chat Name', // optional name 'avatar_path' // optional avatar ); // Get user's conversations $conversations = app(ConversationService::class)->getForUser($userId); // Add member app(ConversationService::class)->addMember($conversation, $userId); // Remove member app(ConversationService::class)->removeMember($conversation, $userId); // Search conversations $results = app(ConversationService::class)->search($userId, 'search query');
MessageService
// Send message $message = app(MessageService::class)->send($conversationId, $userId, 'Hello'); // Edit message app(MessageService::class)->edit($message, 'Updated message'); // Delete message app(MessageService::class)->delete($message); // Add reaction app(MessageService::class)->addReaction($message, $userId, '👍'); // Mark as read app(MessageService::class)->markAsRead($message, $userId); // Mark conversation as read app(MessageService::class)->markConversationAsRead($conversation, $userId);
BlockService
// Block user app(BlockService::class)->block($blockerId, $blockedUserId); // Unblock user app(BlockService::class)->unblock($blockerId, $blockedUserId); // Check if blocked $isBlocked = app(BlockService::class)->isBlocked($blockerId, $blockedUserId); // Get blocked users $blockedUsers = app(BlockService::class)->getBlockedUsers($userId);
PresenceService
// Set online app(PresenceService::class)->setOnline($userId); // Set offline app(PresenceService::class)->setOffline($userId); // Check if online $isOnline = app(PresenceService::class)->isOnline($userId); // Get online users in conversation $onlineUsers = app(PresenceService::class)->getOnlineInConversation($conversation);
Configuration
Edit config/chat.php:
return [ // WebSocket driver: 'reverb' or 'pusher' 'websocket_driver' => env('CHAT_WEBSOCKET_DRIVER', 'reverb'), // Typing indicator timeout in milliseconds 'typing_timeout' => env('CHAT_TYPING_TIMEOUT', 3000), // Message retention period in days 'message_retention' => env('CHAT_MESSAGE_RETENTION', 90), // Maximum attachment size in MB 'max_attachment_size' => env('CHAT_MAX_ATTACHMENT_SIZE', 25), // Pagination per page 'pagination_per_page' => env('CHAT_PAGINATION_PER_PAGE', 50), // Feature toggles 'features' => [ 'message_reactions' => true, 'message_editing' => true, 'message_deletion' => true, 'media_uploads' => true, 'read_receipts' => true, 'typing_indicators' => true, 'user_blocking' => true, 'group_chats' => true, ], ];
Advanced Usage
Broadcasting with Reverb
use Fuyad\Chat\Events\MessageSent; // Broadcast an event broadcast(new MessageSent($message));
Using Models
use Fuyad\Chat\Models\Conversation; use Fuyad\Chat\Models\Message; // Get all messages in conversation $messages = $conversation->messages()->get(); // Get members $members = $conversation->members()->get(); // Get unread count $unreadCount = $conversation->unreadCount($userId); // Get last message $lastMessage = $conversation->lastMessage(); // Get reactions on message $reactions = $message->reactions()->get(); // Check if message is read by user $isRead = $message->isReadBy($userId);
Testing
Run tests with:
composer test
License
MIT License - see LICENSE file for details
Support
For issues, questions, or contributions, visit the package repository.