Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Volt Laravel Package

livewire/volt

Volt adds a functional, single-file component API to Livewire, letting PHP logic and Blade templates live in one file. Write cleaner components with less boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require livewire/volt
php artisan volt:install        # scaffolds resources/views/livewire
php artisan make:volt counter   # functional SFC (default)
php artisan make:volt counter --class  # class-based SFC

Where to look first:

  1. The generated resources/views/livewire/counter.blade.php — it shows the whole file anatomy (PHP block on top, Blade template below, optional @script / @assets).
  2. Livewire\Volt\Volt facade — Volt::route(), Volt::test(), Volt::actingAs().
  3. The upstream docs at livewire.laravel.com/docs/volt (the README is a stub and points there).

The smallest first use case: take an interactive bit of an existing Blade page (a search box, a filter panel, a “load more” list) and replace the surrounding controller/partial with one Volt file, then drop <livewire:search-box /> into the existing view. No route, no controller, no service provider registration.

First full-page use case: a component becomes the route itself.

use Livewire\Volt\Volt;

Volt::route('/posts', 'posts.index')->name('posts.index');
Volt::route('/posts/{post}', 'posts.show')->middleware('auth');

If you already have Livewire in the app, that’s the entire setup — Volt is a compiler that turns SFCs into ordinary Livewire components at runtime, so everything you know about wire: directives, validation, and livewire/update applies unchanged.

Implementation Patterns

Functional API with named actions and computed properties. Names come from the variable you assign, not a string argument:

<?php

use App\Models\Post;
use function Livewire\Volt\{state, computed, action, mount};

state(['search' => ''])->url();

mount(function (Post $post) {
    // route model binding and container-resolved DI both work here
    $this->authorize('view', $post);
});

$results = computed(fn () => Post::where('title', 'like', "%{$this->search}%")->limit(10)->get());

$clear = action(function () {
    $this->search = '';
});

?>

<div>
    <input wire:model.live.debounce.300ms="search" type="search">
    <button wire:click="clear">Clear</button>

    @foreach ($results as $post)
        <div wire:key="post-{{ $post->id }}">{{ $post->title }}</div>
    @endforeach
</div>

Page chrome in the same file. layout('components.layouts.app'), title('Posts') (or title(fn () => $post->title) when it depends on loaded data). No ->layout() on the route, no extra view files.

Query-string state instead of manual plumbing. state(['page' => 1, 'tag' => null])->url() puts the values in the URL; withQueryParams(['tag' => null]) seeds defaults from the request to make bookmarkable filters; ->except([...]) / ->session() control what is persisted where. This replaces most of the #[Url] boilerplate you would write in class components.

Class API inside an SFC when you outgrow functions. Same file format, real class with inheritance, interfaces, and constructor-less DI:

<?php

use Livewire\Volt\Component;
use Livewire\WithPagination;

new class extends Component {
    use WithPagination;

    public function with(): array
    {
        return ['posts' => Post::paginate(15)];
    }
}; ?>

<div>{{ $posts->links() }}</div>

Rule of thumb: functional API for presentation + light state; class SFC the moment you need a trait, an interface, a base class, with() for shareable view data, or heavy type-hinting. uses(WithPagination::class) exists in the functional API too, but the class form is clearer once there are three of them.

Mix modes in one app. Keep heavy/legacy components as class components in app/Livewire and write new interactive leaf components as Volt files. Volt detects class-based projects and resolves names accordingly, so <livewire:old-thing /> and <livewire:new-thing /> coexist. This is the pragmatic migration path: rewrite a page only when you are already editing it.

Reuse existing Blade with @volt. Inline one-off components directly in a Blade view without creating a file — good for prototypes and for feature flags you aren’t ready to promote to a file yet.

Colocate JS. @script / @assets at the bottom of the SFC keep Alpine/Chart.js/Datepicker wiring next to the markup that uses it. Keep the template to a single root element so script placement stays predictable.

Testing. Volt::test('counter')->assertSee(0)->call('increment')->assertSee(1); in Feature/Pest tests, Volt::actingAs($user) for auth-dependent components, and $this->get('/')->assertSeeVolt('counter') / assertDontSeeVolt(...) when asserting on an HTTP response. Named actions from the functional API are callable by their variable name, exactly like class methods.

Gotchas and Tips

  • Helper calls must be top level. state(), computed(), action(), rules(), layout(), title(), uses() are read by the precompiler from the file’s top-level statements. Wrap them in a function or an if and they silently stop being registered — or blow up with a confusing compile error.
  • Component names come from paths. Moving/renaming resources/views/livewire/ files breaks <livewire:... /> references and Volt::route() strings with a ComponentNotFoundException that names the old component. Dotted names (make:volt posts.index) map to nested directories — support for the dot was added in v1.7.2, so don’t rely on it on very old installs.
  • Custom view paths work but are indirect. Volt respects livewire.view_path, not a Volt-specific config key. If components “don’t exist” after you restructure directories, check Livewire’s published config before anything else.
  • Treat every mounted component as reachable. Anything under the Volt directory is resolvable by name and can be rendered through the Livewire endpoint. Authorize in mount() ($this->authorize(...)) rather than assuming “no route means nobody can hit it.”
  • Cache/compile oddities are the historical bug class. Changelog covers route:cache segfaults, Blade::getPath() returning null during view caching, opcache_invalidate missing, and multiple-script/root-element issues. When behavior makes no sense — 404s for a file that clearly exists, stale output after edits — run php artisan optimize:clear and confirm you are on the latest 1.x. Keep Volt and Livewire upgraded together; recent releases are largely compatibility fixes for Livewire 4, PHP 8.5, and Laravel 13.
  • Debug via the compiled output. Stack traces point into storage/framework/views; open the compiled file to see exactly what your closure compiled into. It is the fastest way to understand why $this behaves unexpectedly inside an action.
  • Anonymous components resist unit testing. You cannot new an SFC class yourself — go through Volt::test() / Livewire::test(). That constraint is the main argument for keeping domain logic in services, actions, or plain classes and leaving the Volt file as glue; those are testable in isolation and survive a rewrite to class components.
  • Validation: rules() accepts an array or a closure (useful when rules depend on $this state). Errors surface normally through $errors, and validate() inside an action behaves like Livewire. Don’t hand-roll validation in the Blade template.
  • Watch out when mixing functional and class components in the same directory. Resolution heuristics exist and have been patched more than once. If a component is being interpreted as the wrong type, convert it explicitly to the class form (make:volt --class) rather than fighting the detection.
  • Extension points are Livewire’s, not Volt’s. Volt is thin: it registers routes/tests and compiles files. For anything beyond that — custom component aliases, middleware groups, layout defaults — use Livewire/Blade mechanisms in a service provider, and keep Volt files disposable. That is also the risk profile: it’s first-party and MIT, but it tracks Livewire’s release cadence closely, so pin both and read the changelog before broad upgrades.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo
bernskioldmedia/laravel-harvest
bernskiold/laravel-data-scrubber
bernskiold/laravel-currency-converter
bernskiold/laravel-campaign-monitor
bernskioldmedia/laravel-autotranslate
siberfx/telegram-bot
roy-sandip/laravel-view-extended
gregsphotography/site-builder-core
roy-sandip/laravel-view
anodyne/blade-fluentui-icons
webx-ui/module-catalog-collections