Laravel and DomPDF: Generate Simple Invoice PDF with Images and CSS

Generating PDF files is a common task in Laravel projects. In this tutorial, let's see how to create a PDF invoice with a logo and some simple styling.

This is a simple invoice we will be aiming for:

styled pdf

To generate PDF, we will use a barryvdh/laravel-dompdf package, which is just a Laravel wrapper for dompdf/dompdf package.


Generate Invoice From HTML/Blade View

To use this package, we need to install it via composer.

composer require barryvdh/laravel-dompdf

We will generate the PDF from a Blade View file, using the PDF::loadView() method for this. In your Controller, the code would look similar to the below:

Controller:

use Barryvdh\DomPDF\Facade\Pdf;
 
public function download() {
$pdf = Pdf::loadView('pdf');
 
return $pdf->download();
}

This code assumes that the PDF design is in HTML format in the resources/views/pdf.blade.php file.

We can also pass some data array to PDF and generate it. In this case, it's hard-coded, but in real projects it would probably come from the database.

Controller:

public function download() {
$data = [
[
'quantity' => 1,
'description' => '1 Year Subscription',
'price' => '129.00'
]
];
 
$pdf = Pdf::loadView('pdf', ['data' => $data]);
 
return $pdf->download();
}

And the View file should be a typical HTML/Blade template.

resources/views/pdf.blade.php:

<!doctype html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Invoice</title>
</head>
<body>
<table class="w-full">
<tr>
<td class="w-half">
<img src="{{ asset('laraveldaily.png') }}" alt="laravel daily" width="200" />
</td>
<td class="w-half">
<h2>Invoice ID: 834847473</h2>
</td>
</tr>
</table>
 
<div class="margin-top">
<table class="w-full">
<tr>
<td class="w-half">
<div><h4>To:</h4></div>
<div>John Doe</div>
<div>123 Acme Str.</div>
</td>
<td class="w-half">
<div><h4>From:</h4></div>
<div>Laravel Daily</div>
<div>London</div>
</td>
</tr>
</table>
</div>
 
<div class="margin-top">
<table class="products">
<tr>
<th>Qty</th>
<th>Description</th>
<th>Price</th>
</tr>
<tr class="items">
@foreach($data as $item)
<td>
{{ $item['quantity'] }}
</td>
<td>
{{ $item['description'] }}
</td>
<td>
{{ $item['price'] }}
</td>
@endforeach
</tr>
</table>
</div>
 
<div class="total">
Total: $129.00 USD
</div>
 
<div class="footer margin-top">
<div>Thank you</div>
<div>&copy; Laravel Daily</div>
</div>
</body>
</html>

For now, the downloaded PDF would look unstyled, like the image below:

unstyled pdf

Notice: The logo image is placed in the /public directory and loaded in the PDF using the asset() Laravel helper to get the full URL to the image.


Simple Styling for PDF

As you saw in the View file, some CSS class names exist. DomPDF only supports CSS 2.1 and a few CSS3 properties.

This means that almost no Tailwind classes would work. So, writing your own CSS code only for PDFs is better.

The CSS file for PDF is also placed in the /public directory and loaded using the asset() Laravel helper.

resources/view/pdf.blade.php:

<!doctype html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Invoice</title>
 
<link rel="stylesheet" href="{{ asset('pdf.css') }}" type="text/css">
</head>
 
// ...

And simple style for this example.

public/pdf.css:

h4 {
margin: 0;
}
.w-full {
width: 100%;
}
.w-half {
width: 50%;
}
.margin-top {
margin-top: 1.25rem;
}
.footer {
font-size: 0.875rem;
padding: 1rem;
background-color: rgb(241 245 249);
}
table {
width: 100%;
border-spacing: 0;
}
table.products {
font-size: 0.875rem;
}
table.products tr {
background-color: rgb(96 165 250);
}
table.products th {
color: #ffffff;
padding: 0.5rem;
}
table tr.items {
background-color: rgb(241 245 249);
}
table tr.items td {
padding: 0.5rem;
}
.total {
text-align: right;
margin-top: 1rem;
font-size: 0.875rem;
}

Now, after re-downloading the PDF, it will be styled.

styled pdf


Bonus 1: Renaming PDF File

By default, the package will name the PDF document.pdf. You can change the file name by providing a string with a filename and adding a .pdf extension.

For example, if you generate a PDF from an order, you can name it ORDER_NUMBER.pdf.

Controller:

use Barryvdh\DomPDF\Facade\Pdf;
 
public function download() {
$order = Order::where()...
 
$pdf = Pdf::loadView('pdf');
 
return $pdf->download($order->order_number. '.pdf');
}

Bonus 2: Download VS Stream

What if, instead of downloading a PDF, you want to stream it? It's straightforward: just change from download() to stream().

$data = [
[
'quantity' => 1,
'description' => '1 Year Subscription',
'price' => '129.00'
]
];
 
$pdf = Pdf::loadView('pdf', ['data' => $data]);
 
return $pdf->download();
return $pdf->stream();
avatar

greate share, thank you...

avatar

I can't get domPDF working with TailwindCSS. It still download the view, but unstyled :(

avatar

Have you read the full article? DomPDF only supports CSS 2.1 and a few CSS3 properties. Tailwindcss is a modern framework. In you case use packages from spatie browsershot or pdf

avatar

In my project the api in the controller (...app\Http\Controllers\Api\V1\Admin\MyApiController) is not loaded trans ({{ trans('cruds.disconnect.fields.data_disconnect') }} ) in loadView. view is located ...resources\views\pdf\usersdetails.blade.php

avatar

Sorry, but I don't understand what are you saying

avatar

For example, if in file usersdetails.blade.php there is a recording, trans('cruds.disconnect.fields.data_disconnect') which should turn into DataDisconnect she remains as she was 'cruds.disconnect.fields.data_disconnect'. In usersdetails.blade.php does not work trans. Doesn't work only in usersdetails.blade.php. Although everything works in the project. It looks like the method loadView doesn't work with trans. What to do in this case?..

avatar

Just tried it and it works for me. Don't forget as in every blade file trans should go inside curly brackets {{ trans('cruds.disconnect.fields.data_disconnect') }}. Double check if translation is present for you language and correct language is set for the application

avatar

Yes, this also works for me if the controller is not an API. I don't understand why trans doesn't work in the api controller in the method loadView. May be a long path to the controller (...app\Http\Controllers\Api\V1\Admin\MyApiController). I don't understand... A request to the API controller is made from another application via GuzzleHttp\Client.

Like our articles?

Become a Premium Member for $129/year or $29/month
What else you will get:
  • 59 courses (1057 lessons, total 42 h 44 min)
  • 79 long-form tutorials (one new every week)
  • access to project repositories
  • access to private Discord

Recent Premium Tutorials