Skip to content
Brok UI

Loading…

No results

File Upload Flow

Open source

A server-ready multipart upload workflow with validation and request states.

Preview

resources/views/blocks/file-upload-flow-multipart.blade.php Blade
@props([
    'action' => '#',
    'method' => 'POST',
    'title' => 'Upload files',
    'description' => 'Add files for review and processing.',
    'fieldName' => 'documents',
    'accept' => '.pdf,.png,.jpg,.jpeg',
    'maxSize' => 10,
    'multiple' => true,
    'statusMessage' => null,
    'failureMessage' => null,
    'cancelHref' => null,
    'submitLabel' => 'Upload files',
])

<x-ui.form.guard
    as="section"
    data-slot="file-upload-flow"
    data-surface="admin"
    {{ $attributes->merge(['class' => 'bg-background text-foreground']) }}
>
    <div class="mx-auto max-w-2xl">
        <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
            :action="$action"
            :method="$method"
            :has-files="true"
            :summary="__('Please correct the upload 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>
                    <x-ui.field :name="$fieldName" :label="__('Files')" :hint="__('The server checks file type, size, count, and access again after upload.')" required>
                        <x-ui.dropzone :accept="$accept" :max-size="$maxSize" :multiple="$multiple" />
                    </x-ui.field>
                </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">
                    @if ($cancelHref)
                        <x-ui.button :href="$cancelHref" variant="ghost">{{ __('Cancel') }}</x-ui.button>
                    @endif
                    <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">{{ __('Uploading…') }}</span>
                    </x-ui.button>
                </x-ui.card.footer>
            </x-ui.card>
        </x-ui.form>
    </div>
</x-ui.form.guard>

Installation

terminal
php artisan ui:add blocks/file-upload-flow-multipart

Installs these dependencies automatically:

Registry contract

Install confidence

php artisan ui:add blocks/file-upload-flow-multipart 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.1.4
License
open
Stability
stable
Contract
v2
Foundation
≥ 1.0.0
Type Generated target
blade resources/views/blocks/file-upload-flow-multipart.blade.php

Registry dependencies

form card field dropzone button alert

Package dependencies

composer: jml/brok:^0.2

npm: alpinejs

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.

file-upload-flow-multipart.md
# Brok UI block: File Upload Flow (`file-upload-flow-multipart`)

A server-ready multipart upload flow with Dropzone selection, validation feedback, progress state, unsaved-file protection, and success or failure feedback.

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/file-upload-flow-multipart
```

## Render it

```blade
<x-blocks.file-upload-flow-multipart />
```

## Props

- `action` (string, default `#`) — Multipart upload endpoint.
- `method` (string, default `POST`) — Upload request method.
- `title` (string, default `Upload files`) — Upload card heading.
- `description` (string, default `Add files for review and processing.`) — Upload card description.
- `field-name` (string, default `documents`) — Laravel validation and upload field key.
- `accept` (string|null, default `.pdf,.png,.jpg,.jpeg`) — Client file-picker hint.
- `max-size` (int, default `10`) — Client maximum size in MB per file.
- `multiple` (bool, default `true`) — Allows multiple selected files.
- `status-message` (string|null, default `null`) — Success feedback after redirect.
- `failure-message` (string|null, default `null`) — Safe upload failure feedback.
- `cancel-href` (string|null, default `null`) — Optional cancel destination.
- `submit-label` (string, default `Upload files`) — Upload action label.

## Use when

- Use for one or more files that are submitted to a Laravel endpoint as one form request.

## Avoid when

- Use a resumable direct-upload client for very large files or unreliable network connections.
- Uploading a single file with a simple drop target; use the dropzone primitive alone instead.

## Rules

- Render the installed block with `<x-blocks.file-upload-flow-multipart />` 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/file-upload-flow-multipart
- Registry JSON (files, props, contract): https://brokui.dev/r/open/blocks/file-upload-flow-multipart.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 one or more files that are submitted to a Laravel endpoint as one form request.

Avoid when

  • Use a resumable direct-upload client for very large files or unreliable network connections.
  • Uploading a single file with a simple drop target; use the dropzone primitive alone instead.

Anti-patterns

  • Do not replace semantic props with conflicting utility classes.
  • Do not remove labels, focus styles, or state attributes.
Anatomy
upload heading dropzone field error cancel action submit progress server feedback
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.file-upload-flow-multipart />

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/file-upload-flow-multipart.blade.php Blade
@props([
    'action' => '#',
    'method' => 'POST',
    'title' => 'Upload files',
    'description' => 'Add files for review and processing.',
    'fieldName' => 'documents',
    'accept' => '.pdf,.png,.jpg,.jpeg',
    'maxSize' => 10,
    'multiple' => true,
    'statusMessage' => null,
    'failureMessage' => null,
    'cancelHref' => null,
    'submitLabel' => 'Upload files',
])

<x-ui.form.guard
    as="section"
    data-slot="file-upload-flow"
    data-surface="admin"
    {{ $attributes->merge(['class' => 'bg-background text-foreground']) }}
>
    <div class="mx-auto max-w-2xl">
        <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
            :action="$action"
            :method="$method"
            :has-files="true"
            :summary="__('Please correct the upload 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>
                    <x-ui.field :name="$fieldName" :label="__('Files')" :hint="__('The server checks file type, size, count, and access again after upload.')" required>
                        <x-ui.dropzone :accept="$accept" :max-size="$maxSize" :multiple="$multiple" />
                    </x-ui.field>
                </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">
                    @if ($cancelHref)
                        <x-ui.button :href="$cancelHref" variant="ghost">{{ __('Cancel') }}</x-ui.button>
                    @endif
                    <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">{{ __('Uploading…') }}</span>
                    </x-ui.button>
                </x-ui.card.footer>
            </x-ui.card>
        </x-ui.form>
    </div>
</x-ui.form.guard>

Ownership & lifecycle

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