Skip to content
Brok UI

Loading…

No results

Resource Form

Open source

A reusable create and edit form with validation, responsive actions, custom field slots, and destructive confirmation.

Preview

resources/views/blocks/resource-form-create-edit.blade.php Blade
@props([
    'mode'          => 'create',
    'action'        => '#',
    'method'        => null,
    'title'         => null,
    'description'   => null,
    'cancelHref'    => '#',
    'submitLabel'   => null,
    'deleteAction'  => null,
    'deleteLabel'   => 'Delete resource',
    'values'        => null,
    'statusOptions' => null,
    'statusMessage' => null,
    'failureMessage' => null,
    'loadingLabel'  => 'Saving…',
    'dirtyGuard'    => true,
])

@php
    $isEdit = $mode === 'edit';
    $method ??= $isEdit ? 'PATCH' : 'POST';
    $title ??= $isEdit ? 'Edit resource' : 'Create resource';
    $description ??= $isEdit
        ? 'Update the details and save your changes.'
        : 'Add the details for the new resource.';
    $submitLabel ??= $isEdit ? 'Save changes' : 'Create resource';
    $values ??= [
        'name' => $isEdit ? 'Mobile application' : '',
        'slug' => $isEdit ? 'mobile-application' : '',
        'status' => $isEdit ? 'active' : 'draft',
        'description' => $isEdit ? 'Customer-facing mobile application project.' : '',
    ];
    $statusOptions ??= [
        'draft' => 'Draft',
        'active' => 'Active',
        'archived' => 'Archived',
    ];
@endphp

<x-ui.form.guard
    as="section"
    :enabled="$dirtyGuard"
    data-slot="resource-form"
    data-surface="admin"
    data-visual-contracts="VC-004"
    {{ $attributes->merge(['class' => 'bg-background text-foreground']) }}
>
    <div class="mx-auto max-w-3xl">
        <div class="mb-4 space-y-4" aria-live="polite" aria-atomic="true">
            @if ($statusMessage)
                <x-ui.alert variant="success" role="status">{{ __($statusMessage) }}</x-ui.alert>
            @endif

            @if ($failureMessage)
                <x-ui.alert variant="destructive">{{ __($failureMessage) }}</x-ui.alert>
            @endif
        </div>

        <x-ui.form
            :method="$method"
            :action="$action"
            :summary="__('Please correct the errors below.')"
            data-persisted-form
        >
            <x-ui.card>
                <x-ui.card.header>
                    <x-ui.card.title>{{ __($title) }}</x-ui.card.title>
                    <x-ui.card.description>{{ __($description) }}</x-ui.card.description>
                </x-ui.card.header>

                <x-ui.card.content class="space-y-6">
                    @if ($slot->isEmpty())
                        <div class="grid gap-6 sm:grid-cols-2">
                            <x-ui.field name="name" :label="__('Name')" :hint="__('Use a clear name that people can recognize.')" required>
                                <x-ui.input :value="$values['name'] ?? ''" autocomplete="off" required />
                            </x-ui.field>

                            <x-ui.field name="slug" :label="__('Slug')" :hint="__('Used in URLs and system references.')" required>
                                <x-ui.input :value="$values['slug'] ?? ''" autocomplete="off" required />
                            </x-ui.field>
                        </div>

                        <x-ui.field name="status" :label="__('Status')" required>
                            <x-ui.select required>
                                @foreach ($statusOptions as $value => $label)
                                    <option value="{{ $value }}" @selected(old('status', $values['status'] ?? '') === $value)>{{ __($label) }}</option>
                                @endforeach
                            </x-ui.select>
                        </x-ui.field>

                        <x-ui.field name="description" :label="__('Description')" :hint="__('Give collaborators enough context to understand this resource.')">
                            <x-ui.textarea rows="5">{{ $values['description'] ?? '' }}</x-ui.textarea>
                        </x-ui.field>
                    @else
                        {{ $slot }}
                    @endif
                </x-ui.card.content>

                <x-ui.card.footer class="flex flex-col-reverse gap-2 border-t border-border sm:flex-row sm:justify-end">
                    <x-ui.button :href="$cancelHref" variant="ghost">{{ __('Cancel') }}</x-ui.button>
                    <x-ui.button type="submit" x-bind:disabled="submitting" x-bind:aria-busy="submitting">
                        <span x-show="! submitting">{{ __($submitLabel) }}</span>
                        <span x-cloak x-show="submitting" class="inline-flex items-center gap-2">
                            <x-ui.spinner class="size-4" aria-hidden="true" />
                            {{ __($loadingLabel) }}
                        </span>
                    </x-ui.button>
                </x-ui.card.footer>
            </x-ui.card>
        </x-ui.form>

        @if ($isEdit && $deleteAction)
            <x-ui.card class="mt-6 border-destructive/30">
                <x-ui.card.header class="sm:flex-row sm:items-start sm:justify-between sm:gap-6">
                    <div>
                        <x-ui.card.title>{{ __('Danger zone') }}</x-ui.card.title>
                        <x-ui.card.description class="mt-2">
                            {{ __('Deleting this resource is permanent and cannot be undone.') }}
                        </x-ui.card.description>
                    </div>

                    <x-ui.alert-dialog>
                        <x-ui.alert-dialog.trigger class="mt-4 min-h-11 shrink-0 rounded-md border border-destructive/30 bg-destructive-soft px-4 text-sm font-medium text-destructive hover:bg-destructive/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring sm:mt-0">
                            {{ __($deleteLabel) }}
                        </x-ui.alert-dialog.trigger>
                        <x-ui.alert-dialog.content>
                            <x-ui.alert-dialog.header>
                                <x-ui.alert-dialog.title>{{ __('Delete this resource?') }}</x-ui.alert-dialog.title>
                                <x-ui.alert-dialog.description>
                                    {{ __('This action cannot be undone. The resource and its related data will be removed.') }}
                                </x-ui.alert-dialog.description>
                            </x-ui.alert-dialog.header>
                            <x-ui.alert-dialog.footer>
                                <x-ui.alert-dialog.cancel>{{ __('Cancel') }}</x-ui.alert-dialog.cancel>
                                <x-ui.form method="DELETE" :action="$deleteAction" class="!space-y-0">
                                    <x-ui.button type="submit" variant="destructive">{{ __($deleteLabel) }}</x-ui.button>
                                </x-ui.form>
                            </x-ui.alert-dialog.footer>
                        </x-ui.alert-dialog.content>
                    </x-ui.alert-dialog>
                </x-ui.card.header>
            </x-ui.card>
        @endif
    </div>
</x-ui.form.guard>

Installation

terminal
php artisan ui:add blocks/resource-form-create-edit

Registry contract

Install confidence

php artisan ui:add blocks/resource-form-create-edit writes only the generated targets below. The CLI validates each file hash before writing and prompts before replacing local changes unless --force is used.

Version
1.3.3
License
open
Stability
stable
Contract
v2
Foundation
≥ 1.0.0
Type Generated target
blade resources/views/blocks/resource-form-create-edit.blade.php

Registry dependencies

form card field input textarea select button spinner alert-dialog alert

Package dependencies

composer: jml/brok:^0.2

Use with AI

A brief for your coding agent: what the block is, the install command, how to render it, its props and the rules. Copy it, or open a prompt about this block in an assistant.

resource-form-create-edit.md
# Brok UI block: Resource Form (`resource-form-create-edit`)

A reusable create and edit form with Laravel validation, submit progress, unsaved-change protection, outcome feedback, responsive actions, and destructive-action confirmation.

Brok UI is a Laravel Blade registry. `ui:add` copies this block into the app as plain Blade the app owns; it composes installed `<brok:*>` primitives and semantic design tokens.

## Install

```bash
php artisan ui:add blocks/resource-form-create-edit
```

## Render it

```blade
<x-blocks.resource-form-create-edit />
```

## Props

- `mode` (create|edit, default `create`) — Selects create or edit defaults.
- `action` (string, default `#`) — Create or update form action.
- `method` (string|null, default `null`) — Defaults to POST for create and PATCH for edit.
- `title` (string|null, default `null`) — Form card heading.
- `description` (string|null, default `null`) — Form card description.
- `cancel-href` (string, default `#`) — Cancel-action destination.
- `submit-label` (string|null, default `null`) — Submit button label.
- `values` (array|null, default `null`) — Current resource values for the default fields.
- `delete-action` (string|null, default `null`) — Shows the danger zone when set in edit mode.
- `delete-label` (string, default `Delete resource`) — Destructive action label.
- `status-options` (array|null, default `null`) — Associative status value-to-label options.
- `status-message` (string|null, default `null`) — Success message announced after a server redirect.
- `failure-message` (string|null, default `null`) — Non-validation failure message announced as an alert.
- `loading-label` (string, default `Saving…`) — Submit label shown while the browser sends the form.
- `dirty-guard` (bool, default `true`) — Warns before browser navigation after a field changes.

## Use when

- Use for a standard create or edit resource workflow in an authenticated application.

## Avoid when

- Use a small inline form when the task has only one field and no page-level save flow.

## Rules

- Render the installed block with `<x-blocks.resource-form-create-edit />` and pass data through its props; edit the copied file only for structural changes.
- Keep the semantic design tokens (`bg-background`, `text-muted-foreground`); never swap in raw colour utilities.
- Keep the `data-slot` attributes and the logical (start/end) spacing so the markup still mirrors under `dir="rtl"`.

## Links

- Docs: https://brokui.dev/blocks/resource-form-create-edit
- Registry JSON (files, props, contract): https://brokui.dev/r/open/blocks/resource-form-create-edit.json

Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.

Guidance

Use when

  • Use for a standard create or edit resource workflow in an authenticated application.

Avoid when

  • Use a small inline form when the task has only one field and no page-level save flow.

Anti-patterns

  • Do not replace semantic props with conflicting utility classes.
  • Do not remove labels, focus styles, or state attributes.
Anatomy
form heading managed fields cancel and submit actions optional danger zone delete confirmation
Theming hooks
Uses the admin surface and semantic design tokens.

Usage

Render the block as a component, passing data where useful:

resources/views/welcome.blade.php Blade
<x-blocks.resource-form-create-edit />

Built from primitives

Composes <brok:*> Semantic tokens Re-themeable RTL-ready

This block composes installed <brok:*> primitives and semantic design tokens only — it does not reimplement any primitive. Re-theme it (light, dark, admin, customer) by editing CSS variables; flip the preview to RTL to confirm it mirrors.

Source

The exact, editable file ui:add writes into your app. The preview above renders this same source — there are no preview-only blocks.

resources/views/blocks/resource-form-create-edit.blade.php Blade
@props([
    'mode'          => 'create',
    'action'        => '#',
    'method'        => null,
    'title'         => null,
    'description'   => null,
    'cancelHref'    => '#',
    'submitLabel'   => null,
    'deleteAction'  => null,
    'deleteLabel'   => 'Delete resource',
    'values'        => null,
    'statusOptions' => null,
    'statusMessage' => null,
    'failureMessage' => null,
    'loadingLabel'  => 'Saving…',
    'dirtyGuard'    => true,
])

@php
    $isEdit = $mode === 'edit';
    $method ??= $isEdit ? 'PATCH' : 'POST';
    $title ??= $isEdit ? 'Edit resource' : 'Create resource';
    $description ??= $isEdit
        ? 'Update the details and save your changes.'
        : 'Add the details for the new resource.';
    $submitLabel ??= $isEdit ? 'Save changes' : 'Create resource';
    $values ??= [
        'name' => $isEdit ? 'Mobile application' : '',
        'slug' => $isEdit ? 'mobile-application' : '',
        'status' => $isEdit ? 'active' : 'draft',
        'description' => $isEdit ? 'Customer-facing mobile application project.' : '',
    ];
    $statusOptions ??= [
        'draft' => 'Draft',
        'active' => 'Active',
        'archived' => 'Archived',
    ];
@endphp

<x-ui.form.guard
    as="section"
    :enabled="$dirtyGuard"
    data-slot="resource-form"
    data-surface="admin"
    data-visual-contracts="VC-004"
    {{ $attributes->merge(['class' => 'bg-background text-foreground']) }}
>
    <div class="mx-auto max-w-3xl">
        <div class="mb-4 space-y-4" aria-live="polite" aria-atomic="true">
            @if ($statusMessage)
                <x-ui.alert variant="success" role="status">{{ __($statusMessage) }}</x-ui.alert>
            @endif

            @if ($failureMessage)
                <x-ui.alert variant="destructive">{{ __($failureMessage) }}</x-ui.alert>
            @endif
        </div>

        <x-ui.form
            :method="$method"
            :action="$action"
            :summary="__('Please correct the errors below.')"
            data-persisted-form
        >
            <x-ui.card>
                <x-ui.card.header>
                    <x-ui.card.title>{{ __($title) }}</x-ui.card.title>
                    <x-ui.card.description>{{ __($description) }}</x-ui.card.description>
                </x-ui.card.header>

                <x-ui.card.content class="space-y-6">
                    @if ($slot->isEmpty())
                        <div class="grid gap-6 sm:grid-cols-2">
                            <x-ui.field name="name" :label="__('Name')" :hint="__('Use a clear name that people can recognize.')" required>
                                <x-ui.input :value="$values['name'] ?? ''" autocomplete="off" required />
                            </x-ui.field>

                            <x-ui.field name="slug" :label="__('Slug')" :hint="__('Used in URLs and system references.')" required>
                                <x-ui.input :value="$values['slug'] ?? ''" autocomplete="off" required />
                            </x-ui.field>
                        </div>

                        <x-ui.field name="status" :label="__('Status')" required>
                            <x-ui.select required>
                                @foreach ($statusOptions as $value => $label)
                                    <option value="{{ $value }}" @selected(old('status', $values['status'] ?? '') === $value)>{{ __($label) }}</option>
                                @endforeach
                            </x-ui.select>
                        </x-ui.field>

                        <x-ui.field name="description" :label="__('Description')" :hint="__('Give collaborators enough context to understand this resource.')">
                            <x-ui.textarea rows="5">{{ $values['description'] ?? '' }}</x-ui.textarea>
                        </x-ui.field>
                    @else
                        {{ $slot }}
                    @endif
                </x-ui.card.content>

                <x-ui.card.footer class="flex flex-col-reverse gap-2 border-t border-border sm:flex-row sm:justify-end">
                    <x-ui.button :href="$cancelHref" variant="ghost">{{ __('Cancel') }}</x-ui.button>
                    <x-ui.button type="submit" x-bind:disabled="submitting" x-bind:aria-busy="submitting">
                        <span x-show="! submitting">{{ __($submitLabel) }}</span>
                        <span x-cloak x-show="submitting" class="inline-flex items-center gap-2">
                            <x-ui.spinner class="size-4" aria-hidden="true" />
                            {{ __($loadingLabel) }}
                        </span>
                    </x-ui.button>
                </x-ui.card.footer>
            </x-ui.card>
        </x-ui.form>

        @if ($isEdit && $deleteAction)
            <x-ui.card class="mt-6 border-destructive/30">
                <x-ui.card.header class="sm:flex-row sm:items-start sm:justify-between sm:gap-6">
                    <div>
                        <x-ui.card.title>{{ __('Danger zone') }}</x-ui.card.title>
                        <x-ui.card.description class="mt-2">
                            {{ __('Deleting this resource is permanent and cannot be undone.') }}
                        </x-ui.card.description>
                    </div>

                    <x-ui.alert-dialog>
                        <x-ui.alert-dialog.trigger class="mt-4 min-h-11 shrink-0 rounded-md border border-destructive/30 bg-destructive-soft px-4 text-sm font-medium text-destructive hover:bg-destructive/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring sm:mt-0">
                            {{ __($deleteLabel) }}
                        </x-ui.alert-dialog.trigger>
                        <x-ui.alert-dialog.content>
                            <x-ui.alert-dialog.header>
                                <x-ui.alert-dialog.title>{{ __('Delete this resource?') }}</x-ui.alert-dialog.title>
                                <x-ui.alert-dialog.description>
                                    {{ __('This action cannot be undone. The resource and its related data will be removed.') }}
                                </x-ui.alert-dialog.description>
                            </x-ui.alert-dialog.header>
                            <x-ui.alert-dialog.footer>
                                <x-ui.alert-dialog.cancel>{{ __('Cancel') }}</x-ui.alert-dialog.cancel>
                                <x-ui.form method="DELETE" :action="$deleteAction" class="!space-y-0">
                                    <x-ui.button type="submit" variant="destructive">{{ __($deleteLabel) }}</x-ui.button>
                                </x-ui.form>
                            </x-ui.alert-dialog.footer>
                        </x-ui.alert-dialog.content>
                    </x-ui.alert-dialog>
                </x-ui.card.header>
            </x-ui.card>
        @endif
    </div>
</x-ui.form.guard>

Ownership & lifecycle

Owner, release state, review evidence and adoption for this item.
Owner
Platform UI (@JoshJML)
Current version
1.3.3
Status
Stable
License
open
Deprecation
Not deprecated
Contract
v2
Foundation
≥ 1.0.0