В январе 2026 года вышел Livewire 4 — самый крупный релиз фреймворка с момента его появления. Главное изменение касается того, как мы вообще пишем компоненты: теперь PHP-логика, Blade-шаблон, CSS и JavaScript живут в одном файле. Это уже не Volt-эксперимент, а поведение по умолчанию. В этой статье разберём однофайловые компоненты (single-file components, SFC) на практике, а заодно посмотрим на islands, новый роутинг и optimistic UI.
Установка и первый однофайловый компонент
Livewire 4 ставится привычной командой Composer и требует Laravel 11+ и PHP 8.2+:
composer require livewire/livewire:^4.0
php artisan optimize:clear
Теперь создадим компонент. Команда make:livewire по умолчанию генерирует именно однофайловый компонент:
php artisan make:livewire counter
В результате появится файл resources/views/components/⚡counter.blade.php. Да, эмодзи-молния в имени — это не опечатка: Livewire помечает ей свои компоненты, чтобы их было видно в дереве файлов с первого взгляда. Если эмодзи мешают, их можно отключить в конфиге (make_command.emoji => false). Сам файл выглядит так:
<?php // resources/views/components/⚡counter.blade.php
use Livewire\Component;
new class extends Component {
public $count = 0;
public function increment()
{
$this->count++;
}
};
?>
<div>
<h1>{{ $count }}</h1>
<button wire:click="increment">+</button>
</div>
Класс объявляется анонимно прямо в шаблоне через new class extends Component — тот же синтаксис, что раньше предлагал Volt. Для крупных компонентов остаётся многофайловый формат (MFC): каталог с отдельными файлами counter.php, counter.blade.php, опциональными counter.css, counter.js и counter.test.php. Создаётся он флагом --mfc, а конвертировать форматы туда-обратно можно командой php artisan livewire:convert.
Роутинг через Route::livewire() и namespaces
Классический способ Route::get('/dashboard', Dashboard::class) всё ещё работает, но для SFC и MFC рекомендуется новый метод Route::livewire(), который ссылается на компонент по имени, а не по классу:
use Illuminate\Support\Facades\Route;
Route::livewire('/dashboard', 'pages::dashboard');
Livewire 4 приносит мнение об устройстве проекта: по умолчанию доступны два namespace — pages:: для страничных компонентов и layouts:: для макетов, а всё остальное лежит в resources/views/components рядом с обычными Blade-компонентами. Для модульных приложений можно регистрировать собственные namespace (например, admin:: или billing::) через ключ component_namespaces в config/livewire.php.
Scoped CSS и JavaScript внутри компонента
В однофайловом компоненте стили и скрипты пишутся прямо в шаблоне. Стили автоматически изолируются — класс .title не «утечёт» в остальную страницу. Если нужны глобальные стили, добавляется атрибут <style global>:
<div>
<h1 class="title">{{ $count }}</h1>
<button wire:click="$js.celebrate">+</button>
</div>
<style>
.title {
color: blue;
font-size: 2rem;
}
</style>
<script>
this.$js.celebrate = () => {
confetti()
}
</script>
Важная деталь производительности: и CSS, и JS отдаются браузеру как отдельные нативные .css/.js-файлы и кешируются. Внутри скрипта this — это алиас для $wire, через который доступен контекст компонента.
Islands — изолированный рендеринг
Islands — флагманская фича четвёртой версии. Они позволяют выделить внутри компонента область, которая обновляется независимо от остального шаблона:
<div>
@island
<div>
Выручка: {{ $this->revenue }}
<button wire:click="$refresh">Обновить</button>
</div>
@endisland
<div>
<!-- Этот блок не перерисуется при обновлении острова -->
Остальной контент...
</div>
</div>
Выигрыш не только в DOM. Если остров завязан на computed-свойство, то при его обновлении выполняются только запросы этого острова — изоляция тянется от базы данных до отрендеренного HTML. Острова поддерживают ленивую загрузку (@island(lazy: true)), именование для адресации (name: 'revenue') и дозагрузку контента для бесконечной прокрутки через wire:island.append.
Optimistic UI и состояния загрузки
Чтобы интерфейс ощущался мгновенным, в Livewire 4 появилась группа директив, обновляющих страницу без обращения к серверу. wire:show переключает видимость через CSS (без удаления из DOM и сетевого запроса), wire:text мгновенно меняет текст, а wire:bind реактивно привязывает любой HTML-атрибут:
<div wire:show="showModal">...</div>
Лайки: <span wire:text="likes"></span>
<input wire:model="message"
wire:bind:class="message.length > 240 && 'text-red-500'">
Отдельно стоит отметить автоматический атрибут data-loading: любой элемент, который инициирует сетевой запрос, получает его на время выполнения. Это позволяет стилизовать загрузку прямо из CSS, в том числе через утилиты Tailwind:
<button wire:click="save" class="data-loading:opacity-50">
Сохранить
</button>
Drag-and-drop без сторонних библиотек
Сортировка перетаскиванием теперь встроена через директиву wire:sort — внешние библиотеки не нужны, анимации обрабатываются автоматически:
<ul wire:sort="reorder">
@foreach ($items as $item)
<li wire:key="{{ $item->id }}" wire:sort:item="{{ $item->id }}">
{{ $item->title }}
</li>
@endforeach
</ul>
public function reorder($item, $position)
{
// $item — это ID, $position — новый индекс
}
Доступны ручки перетаскивания (wire:sort:handle), исключение интерактивных элементов (wire:sort:ignore) и перетаскивание между списками (wire:sort:group).
Миграция с Volt
Поскольку SFC используют тот же синтаксис, что и классовые компоненты Volt, переход почти бесшовный. В компонентах замените импорт Livewire\Volt\Component на Livewire\Component, в маршрутах — Volt::route() на Route::livewire(), в тестах — Volt::test() на Livewire::test(). После этого удалите VoltServiceProvider из bootstrap/providers.php и сам пакет:
composer remove livewire/volt
Существующие классовые Volt-компоненты заработают без изменений, потому что их синтаксис совпадает с нативными SFC.
Итоги
Livewire 4 смещает дефолты в сторону меньшего количества бойлерплейта: один файл на компонент, scoped-стили и скрипты «из коробки», нативный роутинг через Route::livewire() и реально полезные фичи вроде islands, drag-and-drop и optimistic UI. При этом обратная совместимость сохранена — классовые компоненты и старый роутинг продолжают работать, так что обновляться можно постепенно. Если вы уже используете Volt, миграция сводится к нескольким заменам импортов и удалению пакета. А начать знакомство проще всего с одной команды make:livewire — и сразу писать компонент целиком в одном месте.