← Laracrate 7 / 19

Displaying files

Once a file exists, you render it from a URL. A file has three, and you pick the one the place needs:

Method What it is Expires
$file->url() A direct link. Public file: its public URL. Private file: a link signed straight to the disk (an R2/S3 presigned GET, or a signed local route on a local disk). Encrypted or sensitive file: its streamUrl(), since PHP has to decrypt it or check each access. Private links: after laracrate.urls.signed_ttl (5 min), or when you say with url(expires: ...). S3 and R2 refuse more than 7 days.
$file->streamUrl() The file through PHP, shown in the browser. Behind the session, the view policy is checked on every request; Range is honoured, so video seeks. Never.
$file->downloadUrl() The file through PHP as an attachment, counted in downloads_count. Never.
$file->url();                               // signed for 5 minutes when private
$file->url(expires: now()->addHours(3));    // a video watched for a while
$file->streamUrl();                         // a link stored in a document, or a page left open

Use url() where the page renders and is read right away, and streamUrl() where the link must keep working (an image embedded in a document, a long session). A signed link is only checked when it is made; the stream route checks the policy every time.

The simplest path is to grab the file and read its url():

@php($avatar = $user->file('avatar'))

@if ($avatar)
    <img src="{{ $avatar->url() }}" alt="Avatar">
@endif

For Blade convenience the model exposes accessors so you can skip the method call:

Accessor Equivalent to
$file->link $file->url()
$file->preview_link $file->preview()?->url('image'); for an image without a preview, the image itself; anything else, the image placeholder
<img src="{{ $file->link }}">
<img src="{{ $file->preview_link }}"> {{-- the variant the type names as its preview --}}

preview_link never throws: if the disk is unreachable or the variant is missing, it returns the configured placeholder so one broken file cannot break the page. It is a direct link (url()), so it follows the same expiry; for one that lasts, use $file->preview()?->streamUrl().

Navigating variants

A file can have derived variants (a thumbnail, a small image, a video frame called portada, and so on, created by the pipeline under the names your config gives them). Reach them with variant() using dot notation:

$file->variant('thumbnail')->url();
$file->variant('portada.mini')->url('image');

variant() never returns null. If any link in the chain is missing it falls back to the closest existing ancestor, so $file->variant('portada.mini') returns portada (or the original) when mini was not generated yet. When that silent fallback would be a bug, use variantOrFail() instead, which throws RuntimeException if any segment is missing:

$thumb = $file->variantOrFail('portada.mini'); // throws if not generated

preview() is the variant the type's preview names, or null when it names none or that variant does not exist yet. Unlike variant(), it never falls back to the original, so a video is never handed out as an image:

// 'video' => [..., 'preview' => 'portada.mini']
$video->preview();              // the same as $video->variant('portada.mini')
$video->preview()?->url();

To avoid lazy-loading each variant on access, eager load the tree with the withVariants() scope on the File model:

$files = File::withVariants()->get(); // loads file -> its variants -> theirs

url() and forced types

url() returns the real URL, or null when no valid backend resolves. Pass a type to force a fallback: if the file is not that type, or its URL cannot be built, you get the configured placeholder for that type instead of null. A 360° photo or a strip of frames is type image, so url('image') serves it.

$file->url();          // real URL or null
$file->url('image');   // real URL if it is an image, otherwise the image placeholder

Rendering from the parent model

The trait gives you two helpers that go straight from a model + collection to a renderable result, with placeholder fallback built in. Prefer these when you do not have a File instance in hand.

fileLink() returns a URL string (the file, a variant, or a placeholder), so the <img> always has a src:

<img src="{{ $user->fileLink('avatar') }}">
<img src="{{ $user->fileLink('avatar', 'medium') }}">     {{-- a variant --}}
<img src="{{ $user->fileLink('cover', 'portada.mini') }}">
public function fileLink(string $collection, ?string $variant = null, ?string $forceType = null): ?string

$forceType selects which placeholder to fall back to. You only need it for multi-type collections (for example a gallery that accepts both images and video). When a collection declares a single type in config, the type is inferred for you.

fileRender() returns rendered HTML. With no component configured it emits a plain <img>; with a component configured for the collection it renders that component instead. Extra attributes are forwarded:

{{ $user->fileRender('avatar', 'medium', ['class' => 'w-12 h-12 rounded-full']) }}
public function fileRender(string $collection, ?string $variant = null, array $attrs = []): HtmlString

Per-collection components

For a consistent look per collection (a round avatar with initials fallback, a card for documents), point the collection at a Blade component via the component config key (or the $fileCollections override shown above). fileRender() then renders:

<x-{component} :model="$model" :url="$url" ...attrs />

The component receives $model (the owning model) and $url (the resolved URL, which may be null when there is no file and no placeholder). A minimal component for the avatar example above:

{{-- resources/views/components/user-avatar.blade.php --}}
@props(['model', 'url'])

@if ($url)
    <img src="{{ $url }}" alt="{{ $model->name }}" {{ $attributes->merge(['class' => 'rounded-full']) }}>
@else
    <span {{ $attributes->merge(['class' => 'rounded-full bg-gray-200 grid place-items-center']) }}>
        {{ Str::of($model->name)->substr(0, 1)->upper() }}
    </span>
@endif

With that component registered, {{ $user->fileRender('avatar') }} renders the avatar everywhere, and falls back to the initials badge when the user has no photo.