---
title: "File uploads — Forms for Laravel | Roundly"
description: "file and image fields store uploads as private media on the submission row — image variants, passthrough files and signed URLs."
url: https://roundly-consulting.com/open-source/docs/forms-for-laravel/file-uploads
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Forms for Laravel](https://roundly-consulting.com/open-source/docs/forms-for-laravel.md)

# File uploads

Map a field to the file or image type and the MediaFileResolver attaches the upload as media on the submission row — image variants for images, passthrough for other files, served back via signed or temporary URLs (private by default). Define the field:

```php
Forms::define('kyc', 'Identity check')
    ->public()
    ->group('documents', 'Documents', function (GroupBuilder $g): void {
        $g->field('passport', 'Passport')->file()->rules(['required', 'file', 'max:10240']);
    })
    ->create();
```

The upload input uses the same nested path as any other field — remember the multipart encoding:

```html
<form method="POST" action="{{ route('kyc.store') }}" enctype="multipart/form-data">
    @csrf
    <input type="file" name="kyc[documents][passport]">
    <button type="submit">Upload</button>
</form>
```

## Reading attachments

```php
$g->field('passport', 'Passport')->file();

// the submission row is the media owner:
$row = Forms::find('kyc')->fields->firstWhere('key', 'passport')->submissions->first();
$row->attachments();               // Collection<Media>
$row->attachmentUrl();             // first attachment's URL: signed if private, public if public
$row->attachmentTemporaryUrl();    // always a short-lived signed URL

// or through the resolver:
$resolver = Forms::find('kyc')->fields->firstWhere('key', 'passport')->resolver();
$resolver->fromStorage();          // the stored media UUID
$resolver->url();                  // same as attachmentUrl()
```

The row stores the media UUID as its value, so fromStorage() and the reader return that UUID. More helpers on the row and the resolver:

```php
$row->hasAttachments();            // bool
$row->attachmentUrls();            // list<string> — every attachment
$row->attachmentBucket();          // 'attachment' (config forms.media.bucket)

$resolver->temporaryUrl();         // short-lived signed URL via the resolver
$resolver->url($user);             // scope the lookup to one sender
```

## Upload settings

Everything lives under forms.media — bucket, visibility, disk, mime allowlist, size limit, responsive widths and signed-URL lifetime:

```php
// config/forms.php
'media' => [
    'bucket' => 'attachment',
    'visibility' => 'private',                 // or 'public'
    'disk' => env('FORMS_MEDIA_DISK'),         // null = by visibility (see private_disk)
    'private_disk' => env('FORMS_MEDIA_PRIVATE_DISK', 'local'), // private uploads, never web-served
    'accepted_mime_types' => ['application/pdf', 'image/jpeg', 'image/png'],
    'max_file_size' => 10 * 1024 * 1024,       // bytes
    'responsive_widths' => [480, 960],         // null = the media default ladder
    'temporary_url_lifetime' => 10,            // minutes; null = the media default
],
```

Force-deleting a submission row purges its attachment media, so hard deletes don’t orphan stored files. Soft deletes keep the files.

## Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

[More ways to support, including crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/forms-for-laravel.md)
