Overview
The Wirechat widget component provides a modal-based chat interface that can be triggered from anywhere in your application. It manages the state of chat modals and handles opening/closing conversations in a widget format.
Component Location
Wirechat\Wirechat\Livewire\Widgets\Wirechat
Usage
Blade
Add the widget to your layout:
<livewire:wirechat.widget />
From Livewire Component
$this->dispatch('openChatWidget',
conversation: $conversationId,
arguments: [],
modalAttributes: []
);
From Blade
<button wire:click="$dispatch('openChatWidget', { conversation: {{ $conversationId }} })">
Open Chat
</button>
From JavaScript
Livewire.dispatch('openChatWidget', {
conversation: 123
});
Properties
activeWirechatWidgetComponent
The ID of the currently active widget component
The ID of the selected conversation
Array of active widget components
Public Methods
Opens a chat conversation in a modal widget.
The conversation ID or instance
Additional arguments to pass to the chat component (default: [])
Custom modal configuration attributes (default: [])
public function openChatWidget($conversation, $arguments = [], $modalAttributes = []): void
Example:
// Basic usage
$this->dispatch('openChatWidget', conversation: 123);
// With custom modal attributes
$this->dispatch('openChatWidget',
conversation: 123,
modalAttributes: [
'closeOnEscape' => false,
'destroyOnClose' => false,
]
);
Destroys a specific widget instance.
public function destroyChatWidget($id): void
resetState()
Resets the widget to its initial state.
public function resetState(): void
Example:
$this->dispatch('reset-widget-state');
Modal Attributes
Default Attributes
public static function modalAttributes(): array
{
return [
'closeOnEscape' => true,
'closeOnEscapeIsForceful' => false,
'dispatchCloseEvent' => true,
'destroyOnClose' => true,
];
}
Available Attributes
Allow closing modal with ESC key (default: true)
Force close on ESC without confirmation (default: false)
Dispatch event when modal closes (default: true)
Destroy component when modal closes (default: true)
Custom Modal Configuration
$this->dispatch('openChatWidget',
conversation: $conversationId,
modalAttributes: [
'closeOnEscape' => false,
'closeOnEscapeIsForceful' => false,
'dispatchCloseEvent' => true,
'destroyOnClose' => false,
]
);
Event Listeners
The widget listens for these events:
Opens a chat modal.
Livewire.dispatch('openChatWidget', { conversation: 123 });
Destroys a widget instance.
Livewire.dispatch('destroyChatWidget', { id: 'widget-id' });
Closes the current widget.
Livewire.dispatch('closeChatWidget');
open-chat
Alias for openChatWidget.
Livewire.dispatch('open-chat', { conversation: 123 });
Dispatched Events
Dispatched when the active widget changes.
The new active widget component ID
Livewire.on('activeChatWidgetComponentChanged', (event) => {
console.log('Active widget:', event.id);
});
Component Resolution
The widget automatically resolves component properties:
public function resolveComponentProps(array $attributes, Component $component): Collection
{
return $this->getPublicPropertyTypes($component)
->intersectByKeys($attributes)
->map(function ($className, $propName) use ($attributes) {
return $this->resolveParameter($attributes, $propName, $className);
});
}
This handles:
- Model binding (e.g., Conversation models)
- Enum resolution
- URL routable instances
Complete Example
Layout Setup
<!-- resources/views/layouts/app.blade.php -->
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
@livewireStyles
</head>
<body>
{{ $slot }}
<!-- Add widget to layout -->
<livewire:wirechat.widget />
@livewireScripts
</body>
</html>
Trigger from Blade
<!-- resources/views/users/show.blade.php -->
<div class="user-profile">
<h1>{{ $user->name }}</h1>
<!-- Open chat with user -->
<button
wire:click="$dispatch('openChatWidget', { conversation: {{ $conversation->id }} })">
Send Message
</button>
</div>
Trigger from Livewire Component
namespace App\Livewire;
use Livewire\Component;
use App\Models\User;
class UserProfile extends Component
{
public User $user;
public function startChat()
{
// Create or get conversation
$conversation = auth()->user()
->createConversationWith($this->user);
// Open widget
$this->dispatch('openChatWidget',
conversation: $conversation->id
);
}
public function render()
{
return view('livewire.user-profile');
}
}
<!-- Open different conversations -->
<div class="contacts-list">
@foreach($contacts as $contact)
<div class="contact">
<span>{{ $contact->name }}</span>
<button
wire:click="$dispatch('openChatWidget', {
conversation: {{ $contact->conversation_id }}
})">
Chat
</button>
</div>
@endforeach
</div>
With Custom Modal Behavior
public function openPersistentChat()
{
$this->dispatch('openChatWidget',
conversation: $this->conversationId,
modalAttributes: [
'closeOnEscape' => false, // Can't close with ESC
'closeOnEscapeIsForceful' => false,
'dispatchCloseEvent' => true,
'destroyOnClose' => false, // Keep state when closed
]
);
}
JavaScript Integration
// Open chat from vanilla JavaScript
document.querySelector('#chat-button').addEventListener('click', () => {
Livewire.dispatch('openChatWidget', { conversation: 123 });
});
// Listen for widget state changes
Livewire.on('activeChatWidgetComponentChanged', (event) => {
console.log('Chat widget opened:', event.id);
// Update UI or analytics
trackEvent('chat_opened', { conversation_id: event.id });
});
// Close widget programmatically
function closeChat() {
Livewire.dispatch('closeChatWidget');
}
Alpine.js Integration
<div x-data="{ chatOpen: false }">
<button
@click="
chatOpen = true;
$dispatch('openChatWidget', { conversation: {{ $conversationId }} })
">
Open Chat
</button>
<!-- Listen for chat close -->
<div
@close-chat.window="chatOpen = false"
x-show="chatOpen">
Chat is open
</div>
</div>
The widget automatically manages state:
protected function widgetComponents
[
'abc123' => [
'name' => 'wirechat.chat',
'conversation' => 123,
'modalAttributes' => [...],
],
]
Each widget instance gets a unique ID based on:
- Component name
- Conversation ID
- Arguments hash
$id = md5($component . $conversation . serialize($arguments));
Best Practices
Always include the widget component in your main layout
Use conversation IDs rather than full models for better performance
Set destroyOnClose: false only when you need to preserve state
Handle widget open events for analytics or UI updates
The widget requires user authentication. Ensure users are logged in before dispatching open events.
Troubleshooting
Verify Widget is in Layout
Ensure <livewire:wirechat.widget /> is present in your layout
Check User Authentication
User must be authenticated to open widget
Verify Conversation Exists
The conversation ID must exist in the database
Check JavaScript Console
Look for Livewire errors in browser console
By design, only one widget is active at a time. Opening a new widget replaces the current one.
Set destroyOnClose: false in modal attributes to preserve state:
modalAttributes: ['destroyOnClose' => false]