Appearance
Debugging Livewire Components
Livewire's reactive nature makes building dynamic interfaces efficient, but sometimes, when things don't behave as expected, you need to peek "under the hood" to understand what's happening. Debugging is a crucial skill for identifying issues with component state, data flow, events, or rendering logic. Fortunately, Livewire offers several ways to gain insights into your components in real-time.
In this chapter, we'll explore two effective methods for debugging your Livewire components: using the dedicated Livewire DevTools browser extension and building a custom Blade component for on-page inspection.
Understanding the Need for Debugging
Why might you need to debug a Livewire component? Common scenarios include:
- Unexpected State: A public property isn't updating as expected after an action.
- Rendering Issues: The component's view isn't reflecting the current data correctly.
- Event Problems: Listeners aren't firing, or events aren't dispatching properly.
- Data Discrepancies: Data fetched or manipulated within the component seems incorrect.
- Performance Bottlenecks: Identifying slow actions or updates.
Having tools to inspect the component's state and data at various points helps pinpoint the root cause of these issues much faster.
Method 1: Livewire DevTools Browser Extension
One of the most convenient tools is the official Livewire DevTools extension (available for Chrome and Firefox). Once installed, it adds a "Livewire" tab to your browser's developer console.
This extension allows you to:
- Inspect Component Hierarchy: See the structure of Livewire components rendered on the page.
- View Public Properties: Examine the current values of all public properties for a selected component in real-time.
- Track Events: Monitor events being dispatched and listened to by components.
- See Network Updates: Observe the AJAX requests Livewire makes in the background and the data payload being sent and received.
The DevTools extension is excellent for getting a quick overview of component state and activity without modifying your code. It's often the first tool to reach for when troubleshooting basic state or event issues.
Method 2: Custom Livewire Log Blade Component
While DevTools is great for public properties and events, sometimes you need to inspect variables that aren't public properties (like variables calculated within the render
method) or visualize specific data structures directly on the page during development. For this, we can create a reusable Blade component.
This component will display a small debug icon, which, when clicked, opens a flyout panel showing the component's public properties and any other data explicitly passed to it.
Preparation
Let's create the Blade component file.
- Create a new file named
livewire-log.blade.php
insideresources/views/components/itf/
. - Paste the following complete code into
resources/views/components/itf/livewire-log.blade.php
:
php
@php
// Configuration: Set to 'false' to allow the log component even when app.debug is false
$onlyDebugMode = true;
// Safety Check: Only render this component if app.debug is true (or if $onlyDebugMode is false)
// This prevents accidentally exposing debug info in production.
if(!config('app.debug') && $onlyDebugMode) return;
// Initialize an array to hold the data we want to display
$output = [];
// Define prefixes for variables we want to ignore (internal Laravel/Livewire/Blade variables)
$prefixes = ['__', 'app', 'errors', 'attributes', 'slot', 'output', 'prefixes', 'show', 'onlyDebugMode', 'sidebar', 'console', 'consoleJson'];
// Get all variables currently defined in the scope where this component is included
$vars = get_defined_vars();
// Iterate through all defined variables
foreach($vars as $key => $value) {
// Check if the variable key starts with any of the ignored prefixes
$ignore = false;
foreach ($prefixes as $prefix) {
if (str_starts_with($key, $prefix)) {
$ignore = true;
break; // Stop checking prefixes for this key if one matches
}
}
// If the key should not be ignored...
if (!$ignore) {
// Livewire components pass their public properties within the '_instance' variable.
// Check if this is the '_instance' (the Livewire component itself)
if ($key === '_instance') {
// Only add public properties if the component has any (check if encoding is not empty object/array)
if (json_encode($value) !== '{}' && json_encode($value) !== '[]') {
$output['public properties'] = $value; // Store the component instance under 'public properties'
}
} else {
// For any other variable not ignored, add it directly to the output array
$output[$key] = $value;
}
}
}
@endphp
<div class="fixed top-2 right-2 z-[1000]">
<flux:modal.trigger name="livewire-log">
<flux:button icon="bug-ant" size="xs" variant="danger"/>
</flux:modal.trigger>
<flux:modal name="livewire-log" variant="flyout" class="w-[500px] bg-white/75 backdrop-blur-sm">
<flux:heading size="lg">Livewire properties</flux:heading>
<div class="overflow-auto">
@foreach(array_reverse($output) as $key => $value)
<div x-data="{show: false}" x-cloak>
<hr class="my-2 border-slate-500">
<button @click="show = !show" class="w-full flex items-center cursor-pointer gap-2">
<span class="text-xl transition-all block"
:class="show ? 'rotate-45' : ''">+</span>
<p class="text-sm italic cursor-pointer">{{ $key }}</p>
</button>
<pre class="text-sm overflow-x-auto bg-slate-100 dark:bg-slate-600 p-2 rounded" x-show="show"
x-transition>{{ json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) }}</pre>
</div>
@endforeach
</div>
</flux:modal>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
Explanation
This Blade component provides an on-page debugging tool for Livewire components, primarily visible during development.
First, the @php
block checks if the application is in debug mode (config('app.debug')
). If not (and $onlyDebugMode
is true), it stops rendering to prevent exposing debug information in production. It then retrieves all variables available in the current scope using get_defined_vars()
.
The code filters these variables, ignoring internal framework variables identified by $prefixes
(like __
, app
, slot
). It specifically looks for the _instance
variable, which holds the Livewire component's public properties, and adds it to an $output
array under the key 'public properties'. Any other variables passed dynamically to the log component (e.g., <x-itf.livewire-log :users="$users">
) are also added to $output
using their passed name (e.g., ' users').
The HTML section creates a small, fixed button (usually a bug icon) in the corner of the screen. Clicking this button triggers a flyout modal. Inside the modal, the component iterates through the collected $output
data. Using Alpine.js (x-data
, x-show
, @click
), it displays each data key (like 'public properties' or 'users') as a clickable header. Clicking the header reveals the corresponding data, neatly formatted as JSON within a <pre>
tag, making it easy to inspect arrays and objects.
Usage
Now, let's see how to use this <x-itf.livewire-log />
component within one of your Livewire component views.
Consider this example Livewire component (you don't need to create this exact one; it's just for demonstration):
It has:
- Two public properties:
$limit
and$showModal
. - One variable calculated in
render()
:$users
.
php
<?php
namespace App\Livewire;
use App\Models\User;
use Livewire\Component;
use Livewire\Attributes\Layout;
#[Layout('components.layouts.vinylshop', ['title' => 'Log Example', 'description' => 'Log example page',])]
class LogExample extends Component
{
public int $limit = 3; // Public property
public bool $showModal = false; // Public property
public function render()
{
// Variable calculated in render, NOT a public property
$users = User::select(['id', 'name', 'email'])
->orderBy('name')
->take($this->limit)
->get();
// Pass the $users variable to the view
return view('livewire.log-example', compact('users'));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
If you have multiple properties and variables, especially complex ones, it can be helpful to see their current values directly on the page while developing or debugging interactions.
Step 1: Add the basic log component
Simply include the <x-itf.livewire-log />
component at the end of your Livewire component's view.
php
<div>
<x-itf.table cols="w-12, w-[150px], w-[200px]" class="!w-auto">
<thead>
<tr>
<th>id</th>
<th>Name</th>
<th>E-mail</th>
</tr>
</thead>
<tbody>
@foreach($users as $user)
<tr>
<td>{{ $user->id }}</td>
<td>{{ $user->name }}</td>
<td>{{ $user->email }}</td>
</tr>
@endforeach
</tbody>
</x-itf.table>
<x-itf.livewire-log />
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Step 2: Pass specific variables to the log component
To inspect variables that are not public properties (like our $users
variable from the render
method), you need to explicitly pass them to the log component using dynamic attributes (attributes starting with a colon :
).
The attribute name you use (e.g., :users
) becomes the key displayed in the log flyout, and the value you pass (e.g., "$users"
) is the data that will be displayed.
php
<div>
<x-itf.table cols="w-12, w-[150px], w-[200px]" class="!w-auto">
<thead>
<tr>
<th>id</th>
<th>Name</th>
<th>E-mail</th>
</tr>
</thead>
<tbody>
@foreach($users as $user)
<tr>
<td>{{ $user->id }}</td>
<td>{{ $user->name }}</td>
<td>{{ $user->email }}</td>
</tr>
@endforeach
</tbody>
</x-itf.table>
<x-itf.livewire-log :users="$users" />
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Step 3: Pass modified or specific data
You are not limited to passing entire variables. You can pass specific parts of data or even the results of method calls. For instance, if you only want to inspect the first user in the collection:
php
<div>
<x-itf.table cols="w-12, w-[150px], w-[200px]" class="!w-auto">
<thead>
<tr>
<th>id</th>
<th>Name</th>
<th>E-mail</th>
</tr>
</thead>
<tbody>
@foreach($users as $user)
<tr>
<td>{{ $user->id }}</td>
<td>{{ $user->name }}</td>
<td>{{ $user->email }}</td>
</tr>
@endforeach
</tbody>
</x-itf.table>
<x-itf.livewire-log :firstUser="$users->first()" />
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
This custom log component provides a flexible way to visualize precisely the data you need, directly on the page, complementing the capabilities of the Livewire DevTools extension. Remember to remove or conditionally disable ( $onlyDebugMode = true
) this component when deploying to production.