Exporting data to PDF is a very common feature. This tutorial will show how to do it in the Filament admin panel: we will export a single record to PDF using Blade View and barryvdh/laravel-dompdf package.
We will use the official Filament demo project for the demo code, adding a PDF download to the OrderResource
. The source code for the original demo it can be found on the GitHub here.
Method 1: Filament Action
First, let's see how we can export into a PDF from Filament action.
app/Filament/Resources/Shop/OrderResource.php:
use Barryvdh\DomPDF\Facade\Pdf;use Illuminate\Support\Facades\Blade; class OrderResource extends Resource{ // ... public static function table(Table $table): Table { return $table ->columns([ // ... ]) ->filters([ // ... ]) ->actions([ Tables\Actions\EditAction::make(), Tables\Actions\Action::make('pdf') ->label('PDF') ->color('success') ->icon('heroicon-s-download') ->action(function (Model $record) { return response()->streamDownload(function () use ($record) { echo Pdf::loadHtml( Blade::render('pdf', ['record' => $record]) )->stream(); }, $record->number . '.pdf'); }), ]) ->bulkActions([ // ... ]); } // ...}
The first import code here is what we return. This response comes from the Livewire where first we pass the content and the second parameter is the filename.
For the PDF content, we use the loadHTML
method from the barryvdh/laravel-dompdf package, and we stream it. To get the HTML we are using the Blade facade to render the Blade file and passing the data.
After this, we have a PDF button in the Orders list.
Now, in the Blade file, you can add any data you need about the record. I called this View pdf.blade.php
.
resources/views/pdf.blade.php:
<div>Number: {{ $record->number }}</div><div>Customer: {{ $record->customer->name }}</div><div>Status: {{ $record->status }}</div><div>Total price: {{ $record->total_price }}</div><div>Order Date: {{ $record->created_at }}</div>
After clicking the PDF button, the PDF will be downloaded with basic information about the order.
Method 2: Using Controller and Route
Here's an alternative approach. First, we need a Controller and a Route.
php artisan make:controller PdfController
routes/web.php:
use App\Http\Livewire\Form;use App\Http\Controllers\PdfController; \Illuminate\Support\Facades\Route::get('form', Form::class); Route::get('pdf/{order}', PdfController::class)->name('pdf');
In the Controller, we can generate the PDF from the View using loadView
method from the barryvdh/laravel-dompdf package and chaining it to download
.
app/Http/Controllers/PdfController.php:
use App\Models\Shop\Order;use Barryvdh\DomPDF\Facade\Pdf; class PdfController extends Controller{ public function __invoke(Order $order) { return Pdf::loadView('pdf', ['record' => $order]) ->download($order->number. '.pdf'); }}
We need to call this Route in the Filament action.
app/Filament/Resources/Shop/OrderResource.php:
class OrderResource extends Resource{ // ... public static function table(Table $table): Table { return $table ->columns([ // ... ]) ->filters([ // ... ]) ->actions([ Tables\Actions\EditAction::make(), Tables\Actions\Action::make('pdf') ->label('PDF') ->color('success') ->icon('heroicon-o-document-download') ->url(fn (Order $record) => route('pdf', $record)) ->openUrlInNewTab(), ]) ->bulkActions([ // ... ]); } // ...}
And that's it. After clicking the PDF button, it will open the URL in a new tab and will download the generated PDF file.
If you want more Filament examples, you can find more real-life projects on our FilamentExamples.com.
Hello, First method deletes the template view for pdf from resource. Why ?
deleteCachedView
isn't neededYeah i know but from "Also, we don't need this cached View, so we are setting it to be deleted." It means cached view. So why it deletes my orginal template from resources/views/pdf.blade.php ?
It was a mistake. Updated text.
This is a very helpful topic. In my case I use method 1: Filament Action. How can I use JavaScript in loaded Blade? I have this simple Blade template:
Test PDF
<script type="text/javascript"> function insertHtml() { document.getElementById("demo").innerHTML = "I have changed!"; } insertHtml() </script>When I execute the code explained in Method 1 in the generated PDF is outputted “Test PDF” instead “I have changed!”. Where is my mistake and how to solve this problem? Thank you.
I guess then you cant? Would would you even want to use it?
I want to use JavaScript to insert dynamically HTML using insertAdjacentHTML() but DomPdf does not take in account the JavaScript contained in the blade Template. And also I don’t know why I can’t paste in the comments the source of the Blade.
If its dompdf thing cant help
OK. Thank you.
How to view pdf in browser without download it?
Change
->download(...)
to->stream(...)
and it should workNice. It work. Tq
Hello and if I would like to open a modal with the pdf file what could you suggest me?
If you want a modal with the PDF, you do need:
https://laraveldaily.com/post/filament-table-row-action-view-modal-infolist
Or in other words, your modal has to be an infolist and there you have to create a custom HTML component (ViewEntry) to load the PDF in an iframe. There is no other way at this point
How can i pass the pdf to the ViewEntry? viewData does not accept a closure?
With filamentphp v3, Laravel 10.34.2, PHP 8.2.0 I get the following error: Unable to prepare route [pdf/{record}] for serialization. Another route has already been assigned name [pdf]. in web.php: Route::get('pdf/{record}', PdfController::class)->name('pdf');
In PdfResourse.php Action::make(('Pdf')) ->icon('heroicon-o-printer') ->url(fn (Invoices $record) => route('pdf', $record)) ->openUrlInNewTab(),**
How do I resolve the routing error? *
if its only name issue, just change route name ->name('another_pdf')
also refactor your code where you use routes by names
I have used method 1. How can I add a custom CSS and JS file to my pdf.blade.php template?
You can't add js and you shouldn't. As for CSS write a custom styles
How do I customize the appearnace of the exported PDF?
For example: I wish to add my company logo and move around some of the locations of each field. A result would be a nice looking customer facing Invoice.
Since this is a
view
based PDF - you have to modify theview
template itself. Anything you put inside of it - will be in your PDF.We don't have a tutorial for this, as it is as simple as creating the HTML content
Does this also work for table header actions, or only for row actions?
I need to export/print a pdf of an admin panel resource table.
This should work on header action too
It would be interesting to see how everyone does this not on a record but on the resultant table in filament. IE if you have a table that's the result of search or filter, how can you STREAM that to a PDF - Thanks for thoughts
You would get selected records from the table component. Plenty of such questions on the filament discord, should check their
Thanks
Using the controller method, how can I make sure that the route is protected with the same authentication as the Filament portal? That seems like something very important that should be implemented.
It's laravel. Add authorization in whate way you want