Application patterns
A pattern connects UI, server rules, request states, authorization, and navigation. Install the pattern,
then replace its sample routes and data with your application data. Do not treat the preview action
# as a production endpoint.
Note
Approved generator
Use the local generator when an application needs a model-specific interaction flow. It creates the Livewire class, Blade page and partials, and feature test as one conflict-safe transaction.
php artisan ui:generate users.index --dry-run
php artisan ui:generate crud User
php artisan ui:generate pattern settings-page
php artisan ui:generate list
php artisan ui:generate resource:show Project --activity --attachments --related=owner,tasks
php artisan ui:generate resource:crud Project --form=page --filters=status --soft-deletes --bulk-actions --export
php artisan ui:generate pattern onboarding
php artisan ui:generate admin:audit-log --export
php artisan ui:generate flow:import
Configure unusual model fields under ui.generator.models. Add tenant scopes and eager loads in the generated baseQuery(). Add domain validation in rules() and normalization in prepareFormData(). Keep the generated action-level policy checks.
The approved catalog also generates resource show and page-form screens, dashboards, onboarding, wizards, command palettes, notification centers, role management, audit logs, checkout, invitations, approvals, and imports. Each workflow names the application service or query method that must connect it to the domain.
Edit form contract
Resource Form is the shared create and edit pattern. It keeps old input and field errors, shows request progress, prevents repeat submits, warns about unsaved changes, announces the server result, and keeps actions full-width on small screens.
<x-pages.resource-page-edit
:action="route('projects.update', $project)"
:delete-action="route('projects.destroy', $project)"
:detail-href="route('projects.show', $project)"
:index-href="route('projects.index')"
:values="$project->only('name', 'slug', 'status', 'description')"
:status-message="session('status')"
:failure-message="session('failure')"
/>
Server validation and redirects
Use a Form Request for validation and authorization. Redirect after a successful write. This prevents a browser refresh from sending the same write again. Redirect to the detail page after create, back to edit after a saved draft, and to the index after delete.
final class UpdateProjectRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user()->can('update', $this->route('project'));
}
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:120'],
'slug' => ['required', 'alpha_dash', 'max:120'],
'status' => ['required', Rule::in(['draft', 'active', 'archived'])],
'description' => ['nullable', 'string', 'max:5000'],
];
}
}
public function update(UpdateProjectRequest $request, Project $project): RedirectResponse
{
$project->update($request->validated());
return to_route('projects.edit', $project)
->with('status', __('Project saved.'));
}
Laravel sends validation failures back with old input and an error bag. Field, Input, Select, and
Textarea read those values. For a service or network failure, redirect back with input and a
failure message. Log the full exception on the server. Show a safe message
to the user.
try {
$project->update($request->validated());
} catch (Throwable $exception) {
report($exception);
return back()
->withInput()
->with('failure', __('The project could not be saved. Try again.'));
}
Unsaved changes
The pattern enables its browser navigation guard after the first input event. It clears the guard when a
valid form submit starts. Set :dirty-guard="false" only when another router
owns navigation. A client router must also guard its internal links because
beforeunload only covers document navigation.
Data table and bulk actions
Resource Index uses a GET form for filters, stable record IDs for selection, and a separate POST or DELETE form for the bulk write. Allowlist sort fields and page sizes. Re-run authorization for every selected record in one transaction. Report records that the user cannot change; do not silently change them.
Destructive actions
Confirm the exact object and effect. Use a DELETE request with CSRF protection. Authorize on the server, handle dependent records, write an audit event, and redirect to a stable parent page. Do not use optimistic UI for deletion.
File uploads
Dropzone gives early type and size feedback and supports Livewire progress. The server must validate MIME type, size, count, and authorization again. Store with a generated name outside a public executable path. Scan untrusted files before they become available. Return field errors for rejected files and a stable file identifier after success.
$validated = $request->validate([
'document' => ['required', 'file', 'mimes:pdf,png,jpg', 'max:10240'],
]);
$path = $validated['document']->store('pending-documents', 'private');
return back()->with('status', __('Upload received.'));
Settings and onboarding
Give each settings section its own action, validation rules, permission check, request state, and success message. Save onboarding progress on the server after each valid step. A returning user must resume at the first incomplete step. Do not use the displayed step number as proof that earlier steps are complete.
Search, commands, and notifications
The application shell owns the command shortcut and dialog. Commands navigate to authorized routes or send explicit events. Search results must not expose records that the current user cannot read. Notification filters and read actions belong to server routes. Use cursor pagination for long histories and keep unread state authoritative on the server.
Permissions and activity
Permission Matrix sends stable permission keys by role. The server rejects unknown roles and permissions, preserves locked and inherited values, and records the actor and the before and after state. Activity Timeline displays that audit data. It is not the audit store.
Page states
Decide the state on the server when possible. Loading keeps the page shape stable. Empty state explains the next action. Error state gives a retry and a support reference. A stale-data failure can keep the last valid content and show a warning. Use one live region for state changes and move focus only when the user started the change.