Feedback Components
User feedback components including tooltips, popovers, and toast notifications for enhanced user experience.
Feedback Components
Feedback components provide contextual information, hints, and notifications to users. These include tooltips for quick hints, popovers for detailed information, and toast notifications for non-blocking messages.
Available Feedback Components
| Component | Purpose |
|---|---|
<x-tooltip> |
Hover hints and information |
<x-popover> |
Click-triggered info panels |
<x-toast-notifications> |
Non-blocking notifications |
Tooltip Component
Displays contextual information on hover, ideal for explaining UI elements or providing additional context.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
id |
string | null |
Tooltip ID for accessibility |
title |
string | '' |
Main tooltip text |
description |
string | '' |
Additional description |
position |
string | 'top' |
Position: top, bottom, left, right |
width |
string | '' |
Custom width |
arrowAlign |
string | 'center' |
Arrow alignment: left, center, right |
Basic Usage
<x-tooltip title="Click to save your changes">
<x-buttons.button variant="primary" icon="lucide:save">
Save
</x-buttons.button>
</x-tooltip>
With Position
{{-- Top (default) --}}
<x-tooltip title="Top tooltip" position="top">
<span class="text-gray-500 cursor-help">Hover me</span>
</x-tooltip>
{{-- Bottom --}}
<x-tooltip title="Bottom tooltip" position="bottom">
<span class="text-gray-500 cursor-help">Hover me</span>
</x-tooltip>
{{-- Left --}}
<x-tooltip title="Left tooltip" position="left">
<span class="text-gray-500 cursor-help">Hover me</span>
</x-tooltip>
{{-- Right --}}
<x-tooltip title="Right tooltip" position="right">
<span class="text-gray-500 cursor-help">Hover me</span>
</x-tooltip>
With Title and Description
<x-tooltip
title="Pro Feature"
description="Upgrade to Pro to unlock this feature"
>
<iconify-icon icon="lucide:crown" class="text-yellow-500"></iconify-icon>
</x-tooltip>
On Form Labels
<div class="flex items-center gap-2">
<label class="form-label">API Key</label>
<x-tooltip
title="Your API Key"
description="Keep this key secret. It provides full access to your account."
>
<iconify-icon icon="lucide:info" class="text-gray-400 cursor-help"></iconify-icon>
</x-tooltip>
</div>
Icon Button with Tooltip
<x-tooltip title="Edit user">
<button class="p-2 hover:bg-gray-100 rounded">
<iconify-icon icon="lucide:edit" class="text-gray-600"></iconify-icon>
</button>
</x-tooltip>
Real Example from Codebase
{{-- Permission indicator --}}
<x-tooltip
title="{{ __('Permission Required') }}"
description="{{ __('You need the :permission permission to access this feature.', ['permission' => 'manage settings']) }}"
>
<iconify-icon icon="lucide:shield" class="text-warning"></iconify-icon>
</x-tooltip>
{{-- Status indicator --}}
<x-tooltip title="{{ $user->is_active ? __('Active') : __('Inactive') }}">
<span class="w-2 h-2 rounded-full {{ $user->is_active ? 'bg-green-500' : 'bg-red-500' }}"></span>
</x-tooltip>
Popover Component
Click-triggered panel for displaying more detailed information or interactive content.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
id |
string | auto-generated | Unique popover ID |
position |
string | 'bottom' |
Position: top, bottom, left, right |
width |
string | 'max-w-xs' |
Max width class |
trigger |
string | 'click' |
Trigger type |
triggerClass |
string | default styles | Trigger button classes |
contentClass |
string | '' |
Content container classes |
Slots
| Slot | Description |
|---|---|
trigger |
The element that opens the popover |
default |
Popover content |
Basic Usage
<x-popover>
<x-slot name="trigger">
<iconify-icon icon="lucide:info" class="text-gray-500 cursor-pointer"></iconify-icon>
</x-slot>
<div class="p-4">
<h4 class="font-medium mb-2">Help Information</h4>
<p class="text-sm text-gray-600">
This is detailed help text that appears when you click the icon.
</p>
</div>
</x-popover>
With Position
<x-popover position="top">
<x-slot name="trigger">
<x-buttons.button variant="secondary">Show Above</x-buttons.button>
</x-slot>
<div class="p-4">
<p>This popover appears above the trigger.</p>
</div>
</x-popover>
<x-popover position="right">
<x-slot name="trigger">
<x-buttons.button variant="secondary">Show Right</x-buttons.button>
</x-slot>
<div class="p-4">
<p>This popover appears to the right.</p>
</div>
</x-popover>
Interactive Content
<x-popover width="max-w-sm">
<x-slot name="trigger">
<x-buttons.button variant="primary" icon="lucide:share">
Share
</x-buttons.button>
</x-slot>
<div class="p-4">
<h4 class="font-medium mb-3">Share this page</h4>
<div class="flex gap-2">
<button class="p-2 hover:bg-gray-100 rounded" title="Twitter">
<iconify-icon icon="mdi:twitter" class="text-xl"></iconify-icon>
</button>
<button class="p-2 hover:bg-gray-100 rounded" title="Facebook">
<iconify-icon icon="mdi:facebook" class="text-xl"></iconify-icon>
</button>
<button class="p-2 hover:bg-gray-100 rounded" title="LinkedIn">
<iconify-icon icon="mdi:linkedin" class="text-xl"></iconify-icon>
</button>
</div>
<div class="mt-3 pt-3 border-t">
<label class="text-sm text-gray-600">Or copy link</label>
<div class="flex mt-1">
<input
type="text"
value="{{ url()->current() }}"
class="form-control text-sm"
readonly
/>
<button class="btn-secondary ml-2" onclick="copyToClipboard()">
Copy
</button>
</div>
</div>
</div>
</x-popover>
User Profile Popover
<x-popover position="bottom" width="max-w-xs">
<x-slot name="trigger">
<img src="{{ $user->avatar_url }}" class="w-8 h-8 rounded-full cursor-pointer" />
</x-slot>
<div class="p-4">
<div class="flex items-center gap-3 mb-3">
<img src="{{ $user->avatar_url }}" class="w-12 h-12 rounded-full" />
<div>
<h4 class="font-medium">{{ $user->name }}</h4>
<p class="text-sm text-gray-500">{{ $user->email }}</p>
</div>
</div>
<div class="space-y-1">
<a href="{{ route('profile') }}" class="block px-2 py-1.5 hover:bg-gray-100 rounded text-sm">
View Profile
</a>
<a href="{{ route('settings') }}" class="block px-2 py-1.5 hover:bg-gray-100 rounded text-sm">
Settings
</a>
<hr class="my-2" />
<form method="POST" action="{{ route('logout') }}">
@csrf
<button class="w-full text-left px-2 py-1.5 hover:bg-gray-100 rounded text-sm text-red-600">
Logout
</button>
</form>
</div>
</div>
</x-popover>
Toast Notifications
Non-blocking notifications that appear in the corner of the screen. Great for success messages, errors, and status updates.
Setup
Include the component in your main layout (usually already included):
{{-- In your layout file --}}
<x-toast-notifications />
Props
| Prop | Type | Default | Description |
|---|---|---|---|
soundEffect |
boolean | false |
Play sound on notification |
displayDuration |
integer | 5000 |
Auto-dismiss time (ms) |
Triggering from JavaScript
// Success toast
window.dispatchEvent(new CustomEvent('notify', {
detail: {
variant: 'success',
title: 'Success!',
message: 'Your changes have been saved.'
}
}));
// Error toast
window.dispatchEvent(new CustomEvent('notify', {
detail: {
variant: 'error',
title: 'Error',
message: 'Failed to save changes. Please try again.'
}
}));
// Warning toast
window.dispatchEvent(new CustomEvent('notify', {
detail: {
variant: 'warning',
title: 'Warning',
message: 'Your session will expire in 5 minutes.'
}
}));
// Info toast
window.dispatchEvent(new CustomEvent('notify', {
detail: {
variant: 'info',
title: 'New Feature',
message: 'Check out the new dashboard widgets!'
}
}));
Triggering from Livewire
// In Livewire component
public function save()
{
$this->validate();
$this->model->save();
$this->dispatch('notify', [
'variant' => 'success',
'title' => 'Saved!',
'message' => 'Your changes have been saved successfully.'
]);
}
public function delete()
{
try {
$this->model->delete();
$this->dispatch('notify', [
'variant' => 'success',
'title' => 'Deleted',
'message' => 'The item has been removed.'
]);
} catch (\Exception $e) {
$this->dispatch('notify', [
'variant' => 'error',
'title' => 'Delete Failed',
'message' => $e->getMessage()
]);
}
}
Triggering from Alpine.js
<button
@click="$dispatch('notify', {
variant: 'success',
title: 'Copied!',
message: 'Link copied to clipboard.'
})"
>
Copy Link
</button>
Available Variants
| Variant | Color | Icon | Use Case |
|---|---|---|---|
success |
Green | Checkmark | Successful operations |
error / danger |
Red | Exclamation | Errors and failures |
warning |
Amber | Warning | Warnings and cautions |
info |
Blue | Info | General information |
With Sound Effect
<x-toast-notifications :soundEffect="true" />
Requires a notification sound file at /public/sounds/notification.mp3.
Custom Display Duration
{{-- Show for 10 seconds --}}
<x-toast-notifications :displayDuration="10000" />
{{-- Show for 3 seconds --}}
<x-toast-notifications :displayDuration="3000" />
Real Example from Codebase
// After successful form submission
public function updateProfile()
{
$this->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email,' . auth()->id(),
]);
auth()->user()->update([
'name' => $this->name,
'email' => $this->email,
]);
$this->dispatch('notify', [
'variant' => 'success',
'title' => __('Profile Updated'),
'message' => __('Your profile has been updated successfully.')
]);
}
// After bulk operation
public function bulkDelete()
{
$count = count($this->selectedItems);
Model::whereIn('id', $this->selectedItems)->delete();
$this->selectedItems = [];
$this->dispatch('resetSelectedItems');
$this->dispatch('notify', [
'variant' => 'success',
'title' => __('Items Deleted'),
'message' => __(':count items have been deleted.', ['count' => $count])
]);
}
Variable Selector Component
A specialized component for selecting and inserting template variables.
Basic Usage
<x-variable-selector
:variables="[
'user.name' => 'User Name',
'user.email' => 'User Email',
'site.name' => 'Site Name',
'date.today' => 'Today\'s Date',
]"
target="content-editor"
/>
Used in email template builders and content editors to insert dynamic placeholders.
Text Editor Component
Rich text editing component with toolbar and formatting options.
Basic Usage
<x-text-editor
name="content"
wire:model="content"
:value="$content"
/>
Searchable Select Component
Enhanced select with search/filter functionality.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
label |
string | null |
Label text |
name |
string | null |
Input name |
options |
array | [] |
Available options |
value |
mixed | null |
Selected value |
placeholder |
string | 'Search...' |
Search placeholder |
multiple |
boolean | false |
Allow multiple selection |
Basic Usage
<x-searchable-select
label="Select User"
name="user_id"
:options="$users->pluck('name', 'id')->toArray()"
wire:model="user_id"
placeholder="Search users..."
/>
Multiple Selection
<x-searchable-select
label="Select Categories"
name="categories"
:options="$categories"
wire:model="selectedCategories"
:multiple="true"
/>
Best Practices
Tooltips
- Keep text short - 1-2 sentences max
- Use for hints - Not essential information
- Position appropriately - Don't cover other elements
- Don't overuse - Only where needed
Popovers
- Use for more detail - When tooltips aren't enough
- Include actions - Links, buttons, forms
- Close on outside click - Standard behavior
- Manage focus - Trap focus when open
Toast Notifications
- Use correct variants - Match message type
- Keep messages brief - Title + 1 sentence
- Don't block UI - Non-intrusive placement
- Auto-dismiss - Unless action required
- Allow manual dismiss - Always include close button
Accessibility Considerations
- Tooltips use
aria-describedbyfor screen readers - Popovers trap focus when open
- Toast notifications use
role="alert" - Escape key closes interactive components
- Focus returns to trigger on close