Стартовые наборы Laravel 12–13 и laravel/ui: React, Vue, Livewire

Стартовые наборы Laravel 12–13 и laravel/ui: React, Vue, Livewire
содержание

Введение

Долгое время вход и регистрация в новом Laravel-проекте появлялись одним из трёх способов: командой php artisan ui bootstrap --auth из пакета laravel/ui, через Breeze или через Jetstream. Laravel 12, вышедший 24 февраля 2025 года, поменял этот порядок. На смену Breeze и Jetstream пришли стартовые наборы (starter kits) — готовые шаблоны приложения, по одному на стек: React, Vue и Livewire, а позднее и Svelte. Под капотом у всех Laravel Fortify, а нужный вариант выбирается прямо в установщике laravel new.

При этом старые пакеты никуда не делись. Breeze (v2.4.2) и Jetstream (v5.5.3) совместимы с Laravel 13, но развиваться перестали: им достаются только обновления совместимости, а новые проекты команда Laravel советует начинать со стартовых наборов. Жив и laravel/ui — версия 4.6.3 вышла в марте 2026 года и поддерживает Laravel 9–13. Ниже разобрано, как выбрать набор, что в нём лежит и где laravel/ui по-прежнему оправдан.

Сравнение наборов

Бэкенд у четырёх официальных наборов одинаковый: Fortify, общий набор маршрутов авторизации, двухфакторная аутентификация и страницы настроек профиля. Различаются они фронтендом — именно он и определяет выбор.

Вариант Стек Рендеринг Кому подходит Сложность поддержки
React Inertia 3, React 19, TypeScript, Tailwind 4, shadcn/ui SPA через Inertia, SSR по желанию Командам, которые уже пишут на React; сложным интерфейсам Composer и npm одновременно, типы, сборка
Vue Inertia 3, Vue 3 (Composition API), TypeScript, Tailwind, shadcn-vue SPA через Inertia, SSR по желанию Проектам, где фронтенд на Vue Так же, как у React
Svelte Inertia 3, Svelte 5, TypeScript, Tailwind, shadcn-svelte SPA через Inertia, SSR по желанию Тем, кто остановился на Svelte Так же, как у React, но готовых компонентов меньше
Livewire Livewire 4, Tailwind, Flux UI Blade на сервере, реактивность через Livewire PHP-разработчикам без отдельного фронтендера JavaScript почти не нужен, всё в Blade и PHP
laravel/ui Blade, Bootstrap 5, по желанию Vue 3 или React 18 На сервере Старым проектам на Bootstrap, учебным задачам Контроллеры с трейтами, без Fortify и 2FA
Без набора Laravel как есть Любой API, своя схема авторизации, собственный UI Всё пишется с нуля

Inertia даёт писать страницы на React, Vue или Svelte, не отказываясь от маршрутов и контроллеров Laravel: ни отдельного API, ни клиентского роутера заводить не придётся. Livewire приходит к тому же результату иначе — компоненты остаются на PHP и Blade, JavaScript-фреймворк не нужен.

Практическое правило простое. Когда фронтендера в команде нет, меньше всего хлопот с Livewire-набором. React, Vue или Svelte оправданы, если в интерфейсе много логики на клиенте или фронтенд уже живёт на одном из этих фреймворков — тогда не придётся переучиваться.

Установка стартового набора

Набор ставит установщик Laravel: он по очереди задаёт вопросы и сам загружает подходящий шаблон.

composer global require laravel/installer

laravel new shop
cd shop
npm install && npm run build
composer run dev   # сервер разработки: http://localhost:8000

# те же ответы флагами — для CI и скриптов
laravel new shop --livewire --teams --pest --no-boost

# набор сообщества с Packagist
laravel new shop --using=vendor/starter-kit

В версии установщика 5.32 вопросы идут в таком порядке:

  1. «Do you want to use a starter kit?» — нужен ли стартовый набор вообще.
  2. Стек: React, Svelte, Vue или Livewire.
  3. Кто отвечает за авторизацию: встроенные средства Laravel или WorkOS.
  4. Для Livewire отдельно — однофайловые компоненты или классические (класс + шаблон).
  5. Нужны ли команды (Teams).
  6. Pest или PHPUnit для тестов и ставить ли Laravel Boost.
  7. Какие функции авторизации оставить: подтверждение email, регистрацию, 2FA, passkeys, подтверждение пароля.

Седьмой вопрос задаёт уже не установщик, а сам набор: всё, что не отмечено, вычищается из конфигурации, шаблонов и тестов. Отсюда удобный приём — лишнее лучше отключить сразу, чем потом искать хвосты по проекту. Если же на первый вопрос ответить «нет», а стек всё равно выбрать, получится заготовка с React, Vue, Svelte или Livewire, но без авторизации.

Код набора целиком переносится в приложение, и никакой зависимости от пакета-шаблона не остаётся. Контроллеры, страницы и компоненты дальше живут как обычный код проекта — их можно свободно править.

Fortify и маршруты авторизации

Во всех наборах авторизацией занимается Laravel Fortify — бэкенд, у которого нет своего интерфейса. Он регистрирует маршруты /login, /logout, /register, /forgot-password, /reset-password, /email/verify, /user/confirm-password, /two-factor-challenge и ряд других, а страницы к ним поставляет набор. Посмотреть весь список можно командой php artisan route:list.

Какие функции включены, решает массив features в config/fortify.php:

use Laravel\Fortify\Features;

'features' => [
    Features::registration(),
    Features::resetPasswords(),
    Features::emailVerification(),
    Features::twoFactorAuthentication([
        'confirm' => true,         // 2FA активируется только после ввода кода
        'confirmPassword' => true, // пароль спрашивается перед включением и отключением
    ]),
    Features::passkeys([
        'confirmPassword' => true,
    ]),
],

Регистрация и сброс пароля реализованы классами-действиями (actions) CreateNewUser и ResetUserPassword в app/Actions/Fortify. Правила валидации в последних версиях наборов переехали в трейты app/Concerns/PasswordValidationRules.php и ProfileValidationRules.php, хотя документация пока указывает для PasswordValidationRules старое место — app/Actions/Fortify.

Отключение открытой регистрации

Во внутренней CRM или админке посторонним регистрироваться незачем. Самый короткий путь — не отмечать «Registration» при установке. В уже созданном проекте хватит закомментировать функцию в config/fortify.php:

'features' => [
    // Features::registration(), // регистрация выключена
    Features::resetPasswords(),
    // ...
],

Здесь есть подвох: маршрут register пропадает, а ссылки на него в шаблонах остаются. В Livewire-наборе страница входа ведёт на регистрацию напрямую, поэтому без правки /login начинает отвечать ошибкой 500 «Route [register] not defined». Ссылку в resources/views/pages/auth/login.blade.php нужно убрать или обернуть проверкой — второй вариант удобнее, если регистрацию когда-нибудь вернут:

@if (Route::has('register'))
    <div class="space-x-1 text-sm text-center text-zinc-600 dark:text-zinc-400">
        <span>{{ __('Don\'t have an account?') }}</span>
        <flux:link :href="route('register')" wire:navigate>{{ __('Sign up') }}</flux:link>
    </div>
@endif

В наборах на React, Vue и Svelte та же проблема всплывёт ещё на этапе сборки. Там работает Wayfinder: во время сборки он создаёт типизированные функции для маршрутов Laravel, а страницы их импортируют (import { register } from '@/routes'). Раз маршрута нет, npm run build будет падать, пока импорт и ссылки не удалены из компонентов. В каком-то смысле это даже удобнее: ошибка видна до деплоя, а не у пользователя.

Дополнительное поле в форме регистрации

Допустим, интернет-магазину при регистрации нужен телефон покупателя. Начинаем с миграции:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::table('users', function (Blueprint $table) {
            // nullable — у существующих пользователей телефона нет
            $table->string('phone', 20)->nullable()->after('email');
        });
    }

    public function down(): void
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('phone');
        });
    }
};

Дальше модель. В Laravel 13 User из набора перечисляет поля для массового заполнения не в свойстве $fillable, а атрибутом. Новое поле добавляется туда же — #[Fillable(['name', 'email', 'phone', 'password'])]. Если об этом забыть, User::create() просто пропустит телефон, без ошибок и предупреждений.

Затем правило валидации и сохранение в CreateNewUser. В модель стоит передавать только прошедшие проверку данные — результат validate(), а не весь $input:

public function create(array $input): User
{
    $validated = Validator::make($input, [
        ...$this->profileRules(),
        'phone' => ['required', 'string', 'regex:/^\+?[0-9\s\-()]{10,20}$/'],
        'password' => $this->passwordRules(),
    ])->validate();

    return User::create([
        'name' => $validated['name'],
        'email' => $validated['email'],
        'phone' => $validated['phone'],
        'password' => $validated['password'], // хеширование делает каст 'hashed' в модели
    ]);
}

Последний шаг — само поле в форме. У Livewire-набора она лежит в resources/views/pages/auth/register.blade.php:

<flux:input
    name="phone"
    :label="__('Телефон')"
    :value="old('phone')"
    type="tel"
    required
    autocomplete="tel"
    placeholder="+7 900 123-45-67"
/>

В наборах на Inertia такое же поле добавляется на страницу регистрации в resources/js/pages.

2FA, passkeys, подтверждение email и команды

Двухфакторная аутентификация (2FA) по TOTP работает сразу после установки. Пользователь включает её в настройках, сканирует код любым приложением-аутентификатором и получает резервные коды. Опция confirm не позволяет активировать 2FA без проверочного кода, а confirmPassword запрашивает пароль при включении и отключении.

Весной 2026 года в Fortify появились passkeys — вход по WebAuthn через Face ID, Touch ID, Windows Hello или аппаратный ключ. Их включает Features::passkeys(), а модель User для этого реализует интерфейс PasskeyUser. В свежих версиях наборов passkeys включены изначально, отключить их можно тем же вопросом установщика. До этого войти без пароля в наборах можно было только через WorkOS.

За подтверждение email отвечают интерфейс MustVerifyEmail у модели User и middleware verified на маршрутах. Если функцию не отключать при установке, настраивать ничего не нужно: интерфейс уже подключён, а группа маршрутов с dashboard в routes/web.php защищена ['auth', 'verified'].

Команды (Teams) включаются ответом «да» в установщике или флагом --teams. Каждый пользователь получает личную команду, текущая команда отражается в URL (/{current_team}/dashboard), есть экраны для создания команд, переключения между ними и приглашения участников. Раньше ради этого ставили Jetstream.

Вариант с WorkOS AuthKit отдаёт авторизацию внешнему сервису: вход через Google, Microsoft, GitHub и Apple, Magic Auth и SSO. Понадобятся аккаунт WorkOS и ключи в .env.

Livewire-набор изнутри

Вся фронтенд-часть Livewire-набора сосредоточена в resources/views:

  • components — Blade-компоненты для повторного использования;
  • flux — переопределённые компоненты Flux UI;
  • layouts — шаблоны приложения и страниц входа;
  • pages — Livewire-страницы: auth/* (вход, регистрация, сброс пароля) и settings/* (профиль, внешний вид, безопасность);
  • partials — общие куски разметки, например <head>;
  • dashboard.blade.php и welcome.blade.php.

Страницы подключаются маршрутами вида Route::livewire('settings/profile', 'pages::settings.profile'). Если в старых туториалах встречается Volt, это не ошибка: в Laravel 12 Livewire-набор был построен на нём. Текущая версия обходится без Volt — она использует однофайловые компоненты Livewire 4.

Из коробки приложение открывается с боковой панелью (sidebar). Для горизонтального меню в resources/views/layouts/app.blade.php подключается другой layout, а flux:main получает атрибут container:

<x-layouts::app.header :title="$title ?? null">
    <flux:main container>
        {{ $slot }}
    </flux:main>
</x-layouts::app.header>

Страницы входа бывают трёх видов: simple (по умолчанию), card и split. Нужный задаётся в resources/views/layouts/auth.blade.php:

<x-layouts::auth.split :title="$title ?? null">
    {{ $slot }}
</x-layouts::auth.split>

Если интерфейсу нужно обновляться в реальном времени — уведомления, список заказов без перезагрузки страницы, — к набору подключается Reverb. Об этом отдельная статья: «Real-time в Laravel: Reverb и Echo».

Когда ещё нужен laravel/ui

laravel/ui генерирует каркас на Blade и Bootstrap, а авторизацию реализует контроллерами на трейтах. Новому проекту стартовые наборы дадут больше, но у пакета остаются свои ниши:

  • проект уже сделан на laravel/ui и Bootstrap, и переверстывать его ради смены инструмента бессмысленно;
  • требуется простая авторизация на чистом Blade — без Tailwind, Inertia и Livewire;
  • учебный проект, где полезно видеть привычные контроллеры LoginController и RegisterController.

Установка в Laravel 13:

composer require laravel/ui
php artisan ui bootstrap --auth   # или: ui vue --auth, ui react --auth
npm install && npm run build      # для разработки — npm run dev

Старые инструкции часто советуют npm run watch, но такой команды больше нет: сборку выполняет Vite, а режим разработки запускается через npm run dev. Маршруты авторизации подключает Auth::routes() в routes/web.php, и отдельные группы можно выключить параметрами:

Auth::routes([
    'register' => false, // открытая регистрация выключена
    'verify' => true,    // маршруты подтверждения email
]);

Проверка на чистом Laravel 13 с laravel/ui 4.6.3 выявила две проблемы, о которых документация молчит:

  1. Пресет подменяет vite.config.js и собирает resources/sass/app.scss, а стандартная welcome.blade.php всё так же подключает resources/css/app.css. В итоге после npm run build главная страница отдаёт 500 «Unable to locate file in Vite manifest». Исправляется это правкой подключения в welcome.blade.php или переводом страницы на layout из пресета:

    @vite(['resources/sass/app.scss', 'resources/js/app.js'])
    
  2. Пресет vue ставит @vitejs/plugin-vue версии 5, а она не совместима с Vite 8 из свежего Laravel, поэтому npm install завершается ошибкой ERESOLVE. Выручает обновление плагина:

    npm install -D @vitejs/plugin-vue@^6
    

Tailwind из стандартного package.json пресет не трогает — если проект переходит на Bootstrap, эти пакеты удаляются вручную. Во время сборки Bootstrap через Sass Dart Sass выводит предупреждения об устаревших функциях; на результат они не влияют и ошибками не считаются.

Об ограничениях laravel/ui лучше знать до старта: ни 2FA, ни passkeys, ни команд, нет классов-действий Fortify, вёрстка только на Bootstrap. Пресет React устанавливает React 18. Собственные пресеты можно описать через макросы UiCommand, но в новых проектах это почти не пригождается.

Что делать со старым проектом на Breeze, Jetstream или laravel/ui

Breeze, laravel/ui и стартовые наборы копируют код в приложение, так что «обновлять набор» не нужно — FAQ документации говорит об этом прямо. После перехода на Laravel 13 проект на Breeze или laravel/ui продолжит работать: оба пакета заявляют совместимость с illuminate/* 13.x. Jetstream привязывает приложение к себе сильнее, поскольку часть логики остаётся внутри пакета, но и он получил обновление под Laravel 13.

Инструмента для автоматического переезда с Breeze или Jetstream на новый набор нет. Если переезд всё-таки нужен, надёжнее всего развернуть чистое приложение на выбранном наборе и по частям переносить туда страницы авторизации и настроек, сверяя маршруты и их имена.

Браться за переписывание авторизации стоит, когда понадобились 2FA, passkeys или команды, фронтенд и так переводится на Tailwind, Livewire или Inertia либо проект отказывается от Bootstrap. Сам по себе переход на Laravel 13 поводом для этого не служит.

Итог

  • Новый проект, команда из PHP-разработчиков — Livewire-набор: Blade, Flux UI и никакого отдельного фронтенда.
  • Много логики на клиенте — React, Vue или Svelte на Inertia.
  • Нужны 2FA, passkeys или команды — подойдёт любой из четырёх наборов: всё это уже заложено в Fortify и страницах набора.
  • Старый проект на laravel/ui, Breeze или Jetstream — Laravel обновляется, авторизация остаётся как есть.
  • Простая авторизация на Bootstrap — laravel/ui по-прежнему работает, если учесть две проблемы сборки, описанные выше.

Сразу после установки набора стоит подключить инструменты разработки: Debugbar и IDE Helper.

Похожие