Skip to content
Brok UI

Loading…

No results

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

Validate and authorize every write on the server. Client checks improve feedback. They do not create a security boundary.

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.

terminal
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.

resources/views/projects/edit.blade.php Blade
<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.

app/Http/Requests/UpdateProjectRequest.php PHP
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.

app/Http/Controllers/ProjectController.php PHP
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.

app/Http/Controllers/DocumentController.php PHP
$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.