---
title: "Delayed Display of Isolated PDFs with Fly-replay and Livewire"
---

# Delayed Display of Isolated PDFs with Fly-replay and Livewire

![A space balloon creature with 6 hands. 3 On the left handles a remote, a button, and a droplet. 2 on the right holds a file and sifts through a record of documents separated into two boxes. The background is set in a gradiently pink and dark outerspace.](https://fly.io/laravel-bytes/displaying-fly-replay-livewire/assets/space-robot-cover.webp)

[Annie Ruygt](https://annieruygtillustration.com/)

In this article we’ll tackle file isolation in a multi-region Laravel Fly App, and display PDFs in a deferred manner. Run your Laravel app with Fly.io, [it takes only a few minutes](https://docs.fly.io/laravel)!

Running our Laravel application close to our users reduces geographical latency. What’s more, with [Fly.io](https://fly.io/), we get to easily do global deployments with [just a few commands](https://fly.io/laravel-bytes/taking-laravel-global/#:~:text=scaled%20out%20to%20also%20include%20Frankfurt%20and%20Singapore%3A)!

However, as we’ve established in [Taking Laravel Global](https://fly.io/laravel-bytes/taking-laravel-global/#problem-file-storage), files stored in one region are not automatically available to instances in other regions.

To address this region-isolation dilemma, today we’ll use the `fly-replay` [response header](https://docs.fly.io/networking/dynamic-request-routing#the-fly-replay-response-header) to direct file retrieval requests to the correct regional instance. Alongside which we’ll use [Livewire’s](https://laravel-livewire.com/) `wire:init` [to speed up](https://laravel-livewire.com/docs/2.x/defer-loading#introduction) initial loading of pages displaying our files.

Here’s a [reference repository](https://github.com/KTanAug21/fly.io-livewire-snippets/blob/master/_readme/display_isolated_fly_replay_livewire.md) you can read and a [demo page](https://ktan-app.fly.dev/documents) you can inspect.

## The Problem

Let’s say we have a [Laravel Fly App](https://docs.fly.io/laravel) with instances running in [AMS, FRA, and SIN](https://docs.fly.io/reference/regions#fly-io-regions).

This Laravel application stores PDFs in each instance’s local storage folder and allows users to view these PDFs in the browser window.

If a `first_title.pdf` file was stored in the AMS region of our Laravel Fly Application, by default, this PDF file will only be accessible to users accessing the AMS instance, but not from instances in FRA or SIN.

Furthermore, even if instances from different regions get to access files stored in other regions, cross-regional data transfer can affect the hold up in pages displaying the file.

## Solution

When it comes to accessing regional-specific storage, we simply need to redirect requests and talk to the right instance containing a requested PDF.

We can easily do so by instructing Fly.io to redirect the request to the correct regional instance with its `fly-replay` [response header](https://docs.fly.io/networking/dynamic-request-routing#the-fly-replay-response-header).

Afterwards, to speed up the initial load-up of the page displaying the PDF file, we can use Livewire’s `wire:init` directive to defer loading our PDF file until the page has completed loading.

### Locally stored, DB tracked, Replay Available

In order to make use of `fly-replay`, we need to know the region our PDF file is stored in.

Fly.io provides us a `FLY_REGION` [environment variable](https://docs.fly.io/machines/runtime-environment#fly_region) which we can use to identify the region our instance is deployed in. We can add this in our [config/app.php](https://github.com/KTanAug21/fly.io-livewire-snippets/blob/master/config/app.php#L42):

```
'fly_region' => env( 'FLY_REGION' ),
```

Then keep track of this region in the PDF details we store in our database:

Multi-region instances benefit greatly with talking to databases in close proximity. Check out running your Laravel Fly App with Multi-region [MySQL](https://fly.io/laravel-bytes/multi-region-laravel-with-planetscale/) or [SQLite](https://docs.fly.io/laravel/advanced-guides/global-sqlite-litefs).

```
DB::table('file_records')->insert([
    'full_path' => 'storage/uploads/'.$fileName,
    'region_id' => config('app.fly_region')
]);
```

Storing a `first_title.pdf` file above should give us the following row in our database:

Make sure to persist stored files through the use of [volumes](https://docs.fly.io/volumes/overview). Here’s [a quick guide](https://docs.fly.io/laravel/the-basics/laravel-volume-storage) to persist data on the storage folder.

```
full_path: "storage/uploads/first_title.pdf",
region_id: "ams"
```

With the above setup, we now have a record of our stored PDF, and the region it is stored in. We can start using `fly-replay` to forward retrieval requests to the right instance to get our `first_title.pdf` file!

## Fly.io ❤️ Laravel

Fly your servers close to your users—and marvel at the speed of close proximity. Deploy globally on Fly in minutes!

[Deploy your Laravel app! →](https://docs.fly.io/laravel)

### Talking to the right instance with Fly-replay

To make our `first_title.pdf` file accessible outside of the AMS region, we’ll have to make sure all retrieval requests for the file “talk” to our instance running in the AMS region.

If the current instance’s region is not the same as the `region_id` recorded for `first_title.pdf`, we stop processing the request in the current instance, and instead return a `fly-replay` [response header](https://docs.fly.io/networking/dynamic-request-routing#the-fly-replay-response-header/) containing the region to forward the request to.

For visibility on the whole picture, you can read [this Controller file](https://github.com/KTanAug21/fly.io-livewire-snippets/blob/master/app/Http/Controllers/DocumentController.php#L27) for reference.

```
  // Decide replay
  if( $pdfDetails->region_id != config('app.fly_region') ){     

      // Replay to identified region
      return response('', 200, [
          'fly-replay' => 'region='.$pdfDetails->region_id ,
      ]);

  }else{

      // FileName
      $fileName = explode('/', $pdfDetails->full_path);
      $fileName = $fileName[(count($fileName))-1];

      // Accessible File Path
      $filePath = Storage::path( $pdfDetails->full_path );

      // Respond with File
      return response()->file( $filePath );

  }
```

Once the response header is returned by the application instance, Fly.io’s proxy layer will immediately recognize the `fly-replay` response header. It will then replay the request to the region specified in its `region=<region_id>` value.

### Displaying PDF iframes with wire:init

Now that PDF files stored in different regions are accessible from the rest, we can proceed with displaying our PDF files with the use of iframes.

We’ll ask our iframe to retrieve the file from a route containing the file retrieval logic specified in our section [above](https://fly.io/laravel-bytes/displaying-fly-replay-livewire/#talking-to-the-right-instance-with-fly-replay).

```
<iframe src="{{ URL::to('/files/show/'.$recordId ) }}"></iframe>
```

Placing this iframe tag in a page will hold up page load until our file gets successfully loaded. We can easily avoid this hold up by using Livewire’s `wire:init` [directive](https://laravel-livewire.com/docs/2.x/defer-loading).

Let’s [create a Livewire component](https://laravel-livewire.com/docs/2.x/making-components) to defer loading our iframe tag:

Here’s the whole picture for our [Livewire view](https://github.com/KTanAug21/fly.io-livewire-snippets/blob/master/resources/views/livewire/show-pdf.blade.php) and [Livewire Component](https://github.com/KTanAug21/fly.io-livewire-snippets/blob/master/app/Http/Livewire/ShowPdf.php).

And a bonus reference on [initializing](https://github.com/KTanAug21/fly.io-livewire-snippets/blob/master/resources/views/documents/index.blade.php#L32) and [updating](https://github.com/KTanAug21/fly.io-livewire-snippets/blob/master/resources/views/documents/index.blade.php#L42) our Livewire component with a PDF record to render.

```
# In the Livewire View
<div id="showFileComponent">
   @if( $loadAllowed==true  )
      <iframe src="{{ URL::to('/files/show/'.$recordId ) }}"></iframe>
   @else
      Loading PDF
   @endif
</div>
```

In the case above, we only load the iframe when the public attribute `$loadAllowed` is true.

We’ll initially set our `$loadAllowed` to false, and only change it to true through the method `allowFileLoading`:

```
# In the Livewire Component
class ShowFile extends Component
{
    public $recordId;
    public $loadAllowed = false;

    public function allowFileLoading()
    {
        $this->loadAllowed = true;
    }
```

And finally, let’s add in `wire:init="allowFileLoading"` to tell the Livewire component that once it completes rendering, it needs to call the `allowFileLoading` method.

```
# In the Livewire View
<div id="showFileComponent" wire:init="allowFileLoading">
```

As seen above, calling the `allowFileLoading` method updates the `$loadAllowed` to true. Doing so loads the iframe tag which calls our Laravel route `files/show/{recordId}`.

## Discussion

Running multi-region instances of our Laravel application means an effort from us to empathize on and close geographical latency for our users across the globe.

At the same time, it also means addressing regional-isolation of files and providing file-accessibility to our users.

Today, we addressed file isolation by talking with the right instance using `fly-replay`, and improved loading of pages displaying these cross-regional files through the use of deferred displaying with `wire:init`.

Of course, this article only focuses on accessibility for file-isolation, there are other equally important points for consideration.

One is Database consistency and [performance optimization](https://fly.io/laravel-bytes/taking-laravel-global/#problem-the-database). We have guides to address those with multi-region [MYSQL with PlanetScale](https://fly.io/laravel-bytes/multi-region-laravel-with-planetscale/), and multi-region [SQLite with LiteFS](https://docs.fly.io/laravel/advanced-guides/global-sqlite-litefs).

For a checklist to mull over, further reading is advised on [Taking Laravel Global](https://fly.io/laravel-bytes/taking-laravel-global/).
