Volt

Are you a visual learner?
Master Livewire with our in-depth screencasts
Watch now
Get comfortable with Livewire first

Before using Volt, we recommend getting familiar with standard, class-based Livewire usage. This will allow you to quickly transfer your knowledge of Livewire into writing components using Volt's functional API.

Volt is an elegantly crafted functional API for Livewire that supports single-file components, allowing a component's PHP logic and Blade templates to coexist in the same file. Behind the scenes, the functional API is compiled to Livewire class components and linked with the template present in the same file.

A simple Volt component looks like the following:

<?php
 
use function Livewire\Volt\{state};
 
state(['count' => 0]);
 
$increment = fn () => $this->count++;
 
?>
 
<div>
<h1>{{ $count }}</h1>
<button wire:click="increment">+</button>
</div>

Installation

To get started, install Volt into your project using the Composer package manager:

composer require livewire/volt

After installing Volt, you may execute the volt:install Artisan command, which will install Volt's service provider file into your application. This service provider specifies the mounted directories in which Volt will search for single file components:

php artisan volt:install

Creating components

You may create a Volt component by placing a file with the .blade.php extension in any of your Volt mounted directories. By default, the VoltServiceProvider mounts the resources/views/livewire and resources/views/pages directories, but you may customize these directories in your Volt service provider's boot method.

For convenience, you may use the make:volt Artisan command to create a new Volt component:

php artisan make:volt counter

By adding the --test directive when generating a component, a corresponding test file will also be generated. If you want the associated test to use Pest, you should use the --pest flag:

php artisan make:volt counter --test --pest

By adding the --class directive it will generate a class-based volt component.

php artisan make:volt counter --class

API style

By utilizing Volt's functional API, we can define a Livewire component's logic through imported Livewire\Volt functions. Volt then transforms and compiles the functional code into a conventional Livewire class, enabling us to leverage the extensive capabilities of Livewire with reduced boilerplate.

Volt's API automatically binds any closure it uses to the underlying component. So, at any time, actions, computed properties, or listeners can refer to the component using the $this variable:

use function Livewire\Volt\{state};
 
state(['count' => 0]);
 
$increment = fn () => $this->count++;
 
// ...

Class-based Volt components

If you would like to enjoy the single-file component capabilities of Volt while still writing class-based components, we've got you covered. To get started, define an anonymous class that extends Livewire\Volt\Component. Within the class, you may utilize all of the features of Livewire using traditional Livewire syntax:

<?php
 
use Livewire\Volt\Component;
 
new class extends Component {
public $count = 0;
 
public function increment()
{
$this->count++;
}
} ?>
 
<div>
<h1>{{ $count }}</h1>
<button wire:click="increment">+</button>
</div>

Class attributes

Just like typical Livewire components, Volt components support class attributes. When utilizing anonymous PHP classes, class attributes should be defined after the new keyword:

<?php
 
use Livewire\Attributes\{Layout, Title};
use Livewire\Volt\Component;
 
new
#[Layout('layouts.guest')]
#[Title('Login')]
class extends Component
{
public string $name = '';
 
// ...

Providing additional view data

When using class-based Volt components, the rendered view is the template present in the same file. If you need to pass additional data to the view each time it is rendered, you may use the with method. This data will be passed to the view in addition to the component's public properties:

<?php
 
use Livewire\WithPagination;
use Livewire\Volt\Component;
use App\Models\Post;
 
new class extends Component {
use WithPagination;
 
public function with(): array
{
return [
'posts' => Post::paginate(10),
];
}
} ?>
 
<div>
<!-- ... -->
</div>

Modifying the view instance

Sometimes, you may wish to interact with the view instance directly, for example, to set the view's title using a translated string. To achieve this, you may define a rendering method on your component:

<?php
 
use Illuminate\View\View;
use Livewire\Volt\Component;
 
new class extends Component {
public function rendering(View $view): void
{
$view->title('Create Post');
 
// ...
}
 
// ...

Rendering and mounting components

Just like a typical Livewire component, Volt components may be rendered using Livewire's tag syntax or the @livewire Blade directive:

<livewire:user-index :users="$users" />

To declare the component's accepted properties, you may use the state function:

use function Livewire\Volt\{state};
 
state('users');
 
// ...

If necessary, you can intercept the properties passed to the component by providing a closure to the state function, allowing you to interact with and modify the given value:

use function Livewire\Volt\{state};
 
state(['count' => fn ($users) => count($users)]);

The mount function may be used to define the "mount" lifecycle hook of the Livewire component. The parameters provided to the component will be injected into this method. Any other parameters required by the mount hook will be resolved by Laravel's service container:

use App\Services\UserCounter;
use function Livewire\Volt\{mount};
 
mount(function (UserCounter $counter, $users) {
$counter->store('userCount', count($users));
 
// ...
});

Full-page components

Optionally, you may render a Volt component as a full page component by defining a Volt route in your application's routes/web.php file:

use Livewire\Volt\Volt;
 
Volt::route('/users', 'user-index');

By default, the component will be rendered using the components.layouts.app layout. You may customize this layout file using the layout function:

use function Livewire\Volt\{layout, state};
 
state('users');
 
layout('components.layouts.admin');
 
// ...

You may also customize the title of the page using the title function:

use function Livewire\Volt\{layout, state, title};
 
state('users');
 
layout('components.layouts.admin');
 
title('Users');
 
// ...

If the title relies on component state or an external dependency, you may pass a closure to the title function instead:

use function Livewire\Volt\{layout, state, title};
 
state('users');
 
layout('components.layouts.admin');
 
title(fn () => 'Users: ' . $this->users->count());

Properties

Volt properties, like Livewire properties, are conveniently accessible in the view and persist between Livewire updates. You can define a property using the state function:

<?php
 
use function Livewire\Volt\{state};
 
state(['count' => 0]);
 
?>
 
<div>
{{ $count }}
</div>

If the initial value of a state property relies on outside dependencies, such as database queries, models, or container services, its resolution should be encapsulated within a closure. This prevents the value from being resolved until it is absolutely necessary:

use App\Models\User;
use function Livewire\Volt\{state};
 
state(['count' => fn () => User::count()]);

If the initial value of a state property is being injected via Laravel Folio's route model binding, it should also be encapsulated within a closure:

use App\Models\User;
use function Livewire\Volt\{state};
 
state(['user' => fn () => $user]);

Of course, properties may also be declared without explicitly specifying their initial value. In such cases, their initial value will be null or will be set based on the properties passed into the component when it is rendered:

use function Livewire\Volt\{mount, state};
 
state(['count']);
 
mount(function ($users) {
$this->count = count($users);
 
//
});

Locked properties

Livewire offers the ability to safeguard properties by enabling you to "lock" them, thereby preventing any modifications from occurring on the client-side. To achieve this using Volt, simply chain the locked method on the state you wish to protect:

state(['id'])->locked();

Reactive properties

When working with nested components, you may find yourself in a situation where you need to pass a property from a parent component to a child component, and have the child component automatically update when the parent component updates the property.

To achieve this using Volt, you may chain the reactive method on the state you wish to be reactive:

state(['todos'])->reactive();

Modelable properties

In cases where you don't want to make use of reactive properties, Livewire provides a modelable feature where you may share state between parent component and child component using wire:model directly on a child component.

To achieve this using Volt, simply chain the modelable method on the state you wish to be modelable:

state(['form'])->modelable();

Computed properties

Livewire also allows you to define computed properties, which can be useful for lazily fetching information needed by your component. Computed property results are "memoized", or cached in memory, for an individual Livewire request lifecycle.

To define a computed property, you may use the computed function. The name of the variable will determine the name of the computed property:

<?php
 
use App\Models\User;
use function Livewire\Volt\{computed};
 
$count = computed(function () {
return User::count();
});
 
?>
 
<div>
{{ $this->count }}
</div>

You may persist the computed property's value in your application's cache by chaining the persist method onto the computed property definition:

$count = computed(function () {
return User::count();
})->persist();

By default, Livewire caches the computed property's value for 3600 seconds. You may customize this value by providing the desired number of seconds to the persist method:

$count = computed(function () {
return User::count();
})->persist(seconds: 10);

Actions

Livewire actions provide a convenient way to listen to page interactions and invoke a corresponding method on your component, resulting in the re-rendering of the component. Often, actions are invoked in response to the user clicking a button.

To define a Livewire action using Volt, you simply need to define a closure. The name of the variable containing the closure will determine the name of the action:

<?php
 
use function Livewire\Volt\{state};
 
state(['count' => 0]);
 
$increment = fn () => $this->count++;
 
?>
 
<div>
<h1>{{ $count }}</h1>
<button wire:click="increment">+</button>
</div>

Within the closure, the $this variable is bound to the underlying Livewire component, giving you the ability to access other methods on the component just as you would in a typical Livewire component:

use function Livewire\Volt\{state};
 
state(['count' => 0]);
 
$increment = function () {
$this->dispatch('count-updated');
 
//
};

Your action may also receive arguments or dependencies from Laravel's service container:

use App\Repositories\PostRepository;
use function Livewire\Volt\{state};
 
state(['postId']);
 
$delete = function (PostRepository $posts) {
$posts->delete($this->postId);
 
// ...
};

Renderless actions

In some cases, your component might declare an action that does not perform any operations that would cause the component's rendered Blade template to change. If that's the case, you can skip the rendering phase of Livewire's lifecycle by encapsulating the action within the action function and chaining the renderless method onto its definition:

use function Livewire\Volt\{action};
 
$incrementViewCount = action(fn () => $this->viewCount++)->renderless();

Protected helpers

By default, all Volt actions are "public" and may be invoked by the client. If you wish to create a function that is only accessible from within your actions, you may use the protect function:

use App\Repositories\PostRepository;
use function Livewire\Volt\{protect, state};
 
state(['postId']);
 
$delete = function (PostRepository $posts) {
$this->ensurePostCanBeDeleted();
 
$posts->delete($this->postId);
 
// ...
};
 
$ensurePostCanBeDeleted = protect(function () {
// ...
});

Forms

Livewire's forms provide a convenient way to deal with form validation and submission within a single class. To use a Livewire form within a Volt component, you may utilize the form function:

<?php
 
use App\Livewire\Forms\PostForm;
use function Livewire\Volt\{form};
 
form(PostForm::class);
 
$save = function () {
$this->form->store();
 
// ...
};
 
?>
 
<form wire:submit="save">
<input type="text" wire:model="form.title">
@error('form.title') <span class="error">{{ $message }}</span> @enderror
 
<button type="submit">Save</button>
</form>

As you can see, the form function accepts the name of a Livewire form class. Once defined, the form can be accessed via the $this->form property within your component.

If you want to use a different property name for your form, you can pass the name as the second argument to the form function:

form(PostForm::class, 'postForm');
 
$save = function () {
$this->postForm->store();
 
// ...
};

Listeners

Livewire's global event system enables communication between components. If two Livewire components exist on a page, they can communicate by utilizing events and listeners. When using Volt, listeners can be defined using the on function:

use function Livewire\Volt\{on};
 
on(['eventName' => function () {
//
}]);

If you need to assign dynamic names to event listeners, such as those based on the authenticated user or data passed to the component, you can pass a closure to the on function. This closure can receive any component parameter, as well as additional dependencies which will be resolved via Laravel's service container:

on(fn ($post) => [
'event-'.$post->id => function () {
//
}),
]);

For convenience, component data may also be referenced when defining listeners using "dot" notation:

on(['event-{post.id}' => function () {
//
}]);

Lifecycle hooks

Livewire has a variety of lifecycle hooks that may be used to execute code at various points in a component's lifecycle. Using Volt's convenient API, you can define these lifecycle hooks using their corresponding functions:

use function Livewire\Volt\{boot, booted, ...};
 
boot(fn () => /* ... */);
booted(fn () => /* ... */);
mount(fn () => /* ... */);
hydrate(fn () => /* ... */);
hydrate(['count' => fn () => /* ... */]);
dehydrate(fn () => /* ... */);
dehydrate(['count' => fn () => /* ... */]);
updating(['count' => fn () => /* ... */]);
updated(['count' => fn () => /* ... */]);

Lazy loading placeholders

When rendering Livewire components, you may pass the lazy parameter to a Livewire component to defer its loading until the initial page is fully loaded. By default, Livewire inserts <div></div> tags into the DOM where the component will be loaded.

If you would like to customize the HTML that is displayed within the component's placeholder while the initial page is loaded, you may use the placeholder function:

use function Livewire\Volt\{placeholder};
 
placeholder('<div>Loading...</div>');

Validation

Livewire offers easy access to Laravel's powerful validation features. Using Volt's API, you may define your component's validation rules using the rules function. Like traditional Livewire components, these rules will be applied to your component data when you invoke the validate method:

<?php
 
use function Livewire\Volt\{rules};
 
rules(['name' => 'required|min:6', 'email' => 'required|email']);
 
$submit = function () {
$this->validate();
 
// ...
};
 
?>
 
<form wire:submit.prevent="submit">
//
</form>

If you need to define rules dynamically, such as rules based on the authenticated user or a information from your database, you can provide a closure to the rules function:

rules(fn () => [
'name' => ['required', 'min:6'],
'email' => ['required', 'email', 'not_in:'.Auth::user()->email]
]);

Error messages and attributes

To modify the validation messages or attributes used during validation, you can chain the messages and attributes methods onto your rules definition:

use function Livewire\Volt\{rules};
 
rules(['name' => 'required|min:6', 'email' => 'required|email'])
->messages([
'email.required' => 'The :attribute may not be empty.',
'email.email' => 'The :attribute format is invalid.',
])->attributes([
'email' => 'email address',
]);

File uploads

When using Volt, uploading and storing files is much easier thanks to Livewire. To include the Livewire\WithFileUploads trait on your functional Volt component, you may use the usesFileUploads function:

use function Livewire\Volt\{state, usesFileUploads};
 
usesFileUploads();
 
state(['photo']);
 
$save = function () {
$this->validate([
'photo' => 'image|max:1024',
]);
 
$this->photo->store('photos');
};

URL query parameters

Sometimes it's useful to update the browser's URL query parameters when your component state changes. In these cases, you can use the url method to instruct Livewire to sync the URL query parameters with a piece of component state:

<?php
 
use App\Models\Post;
use function Livewire\Volt\{computed, state};
 
state(['search'])->url();
 
$posts = computed(function () {
return Post::where('title', 'like', '%'.$this->search.'%')->get();
});
 
?>
 
<div>
<input wire:model.live="search" type="search" placeholder="Search posts by title...">
 
<h1>Search Results:</h1>
 
<ul>
@foreach($this->posts as $post)
<li wire:key="{{ $post->id }}">{{ $post->title }}</li>
@endforeach
</ul>
</div>

Additional URL query parameters options supported by Livewire, such as URL query parameters aliases, may also be provided to the url method:

use App\Models\Post;
use function Livewire\Volt\{state};
 
state(['page' => 1])->url(as: 'p', history: true, keep: true);
 
// ...

Pagination

Livewire and Volt also have complete support for pagination. To include Livewire's Livewire\WithPagination trait on your functional Volt component, you may use the usesPagination function:

<?php
 
use function Livewire\Volt\{with, usesPagination};
 
usesPagination();
 
with(fn () => ['posts' => Post::paginate(10)]);
 
?>
 
<div>
@foreach ($posts as $post)
//
@endforeach
 
{{ $posts->links() }}
</div>

Like Laravel, Livewire's default pagination view uses Tailwind classes for styling. If you use Bootstrap in your application, you can enable the Bootstrap pagination theme by specifying your desired theme when invoking the usesPagination function:

usesPagination(theme: 'bootstrap');

Custom traits and interfaces

To include any arbitrary trait or interface on your functional Volt component, you may use the uses function:

use function Livewire\Volt\{uses};
 
use App\Contracts\Sorting;
use App\Concerns\WithSorting;
 
uses([Sorting::class, WithSorting::class]);

Anonymous components

Sometimes, you may want to convert a small portion of a page into a Volt component without extracting it into a separate file. For example, imagine a Laravel route that returns the following view:

Route::get('/counter', fn () => view('pages/counter.blade.php'));

The view's content is a typical Blade template, including layout definitions and slots. However, by wrapping a portion of the view within the @volt Blade directive, we can convert that piece of the view into a fully-functional Volt component:

<?php
 
use function Livewire\Volt\{state};
 
state(['count' => 0]);
 
$increment = fn () => $this->count++;
 
?>
 
<x-app-layout>
<x-slot name="header">
Counter
</x-slot>
 
@volt('counter')
<div>
<h1>{{ $count }}</h1>
<button wire:click="increment">+</button>
</div>
@endvolt
</x-app-layout>

Passing data to anonymous components

When rendering a view that contains an anonymous component, all of the data given to the view will also be available to the anonymous Volt component:

use App\Models\User;
 
Route::get('/counter', fn () => view('users.counter', [
'count' => User::count(),
]));

Of course, you may declare this data as "state" on your Volt component. When initializing state from data proxied to the component by the view, you only need to declare the name of the state variable. Volt will automatically hydrate the state's default value using the proxied view data:

<?php
 
use function Livewire\Volt\{state};
 
state('count');
 
$increment = function () {
// Store the new count value in the database...
 
$this->count++;
};
 
?>
 
<x-app-layout>
<x-slot name="header">
Initial value: {{ $count }}
</x-slot>
 
@volt('counter')
<div>
<h1>{{ $count }}</h1>
<button wire:click="increment">+</button>
</div>
@endvolt
</x-app-layout>

Testing components

To begin testing a Volt component, you may invoke the Volt::test method, providing the name of the component:

use Livewire\Volt\Volt;
 
it('increments the counter', function () {
Volt::test('counter')
->assertSee('0')
->call('increment')
->assertSee('1');
});

When testing a Volt component, you may utilize all of the methods provided by the standard Livewire testing API.

If your Volt component is nested, you may use "dot" notation to specify the component that you wish to test:

Volt::test('users.stats')

When testing a page that contains an anonymous Volt component, you may use the assertSeeVolt method to assert that the component is rendered:

$this->get('/users')
->assertSeeVolt('stats');