Resource Form
A reusable create and edit form with validation, responsive actions, custom field slots, and destructive confirmation.
Preview
@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
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
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.
# 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
- Theming hooks
Usage
Render the block as a component, passing data where useful:
<x-blocks.resource-form-create-edit />
Built from primitives
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.
@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