Why Generate PDF Invoices in Laravel 11 Using Dompdf?

             In modern web applications built with Laravel 11, delivering professional-looking invoices to customers is essential for e-commerce platforms, freelance billing systems, SaaS dashboards, and any business that handles orders or payments. Generating PDF invoices ensures consistent formatting across devices, prevents tampering, enables easy printing or emailing, and provides a polished client experience that boosts trust and perceived professionalism.

Dompdf remains one of the most popular and reliable choices for Laravel developers because it seamlessly converts HTML (and Blade templates) into PDFs without requiring external services like cloud APIs. The barryvdh/laravel-dompdf wrapper makes integration effortless — you get full control over styling with CSS, support for images/logos, tables for line items, totals with taxes/discounts, and even custom headers/footers. Unlike heavier alternatives (e.g., Browsershot or mPDF), Dompdf is lightweight, server-side only, requires zero Node.js dependencies, and performs well on shared hosting or VPS environments.

Whether you're building a simple order system or scaling a full invoicing module, this approach saves development time while delivering pixel-perfect, downloadable documents. In this complete step-by-step tutorial, you'll learn exactly how to set up Dompdf in Laravel 11, create dynamic invoice data from your database, design a clean Blade template, and trigger instant PDF downloads — all with production-ready best practices.

By the end, you'll have a reusable invoice generator ready for real-world use, helping you streamline billing workflows and improve user satisfaction.



Generate Professional PDF Invoices in Laravel 11 with Dompdf

Table Of Content

1 Prerequisites

  • PHP 8.2+
  • Composer
  • MySQL (or preferred DB)
  • Basic knowledge of Laravel migrations, Blade, and Eloquent

2 Introduction

Whether you're developing an e-commerce store, freelance billing system, SaaS dashboard, or internal admin tool, this approach is lightweight, runs entirely on the server (no Node.js or external APIs required), and delivers consistent, printable invoices that look great on any device. By the end of this guide, you'll have a production-ready PDF invoice generator you can extend with taxes, discounts, logos, payment details, or even email attachments.

Let’s get started and turn your Laravel 11 app into a powerful invoicing machine!

3 Create / Install a Laravel Project

3.1 Install Laravel Project

Ensure Composer is installed. Run:

composer create-project laravel/laravel laravel11-dompdf-app

Then navigate to your project:

cd laravel11-dompdf-app

3.2 Configure MySql Database

To store payment data, access the .env file to define database credentials:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel11
DB_USERNAME=root
DB_PASSWORD=

4 Install Barryvdh/Laravel-Dompdf

To install dompdf in Laravel, use Composer:

composer require barryvdh/laravel-dompdf

(Optional but recommended): Publish the config for custom settings like paper size or fonts:


php artisan vendor:publish --provider="Barryvdh\DomPDF\ServiceProvider"

5 Create Models & Migrations for Orders

To create a migration for the order table:

    php artisan make:migration create_order_table

Add the following fields:
    
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('order', function (Blueprint $table) {
            $table->id();
            $table->string('order_ref');
            $table->string('order_invoice');
            $table->string('customer_first_name');
            $table->string('customer_last_name');
            $table->string('customer_address');
            $table->string('customer_company');
            $table->string('amount');
            $table->string('order_status');
            $table->timestamps();
        });
        Schema::create('orderitems', function (Blueprint $table) {
            $table->id();
            $table->string('order_id');
            $table->string('product_name');
            $table->string('item_price');
            $table->string('quantity');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('order');
        Schema::dropIfExists('orderitems');
    }
};

Run the migration:

php artisan migrate

Create the Order model in app/Models/Order.php:

php artisan make:model Order

    
<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Order extends Model
{
    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = 'orders';
}


Create the OrderItem model in app/Models/OrderItem.php:

php artisan make:model OrderItem

    
<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class OrderItem extends Model
{
    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = 'orderitems';
}


6 Build the PDF Controller

Create Controller PdfController:

php artisan make:controller PdfController

This Laravel 11 code defines a PdfController that handles displaying orders and generating invoices as PDF files. Let’s go through it step by step.

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Order;
use App\Models\OrderItem;
use PDF;

class PdfController extends Controller
{
    public function index()
    {
       $orders = Order::get();
        return view('index',compact('orders'));
    }

    public function invoice($id)
    {
         $order = Order::find($id);
        $orderitems = OrderItem::where('order_id', $id)->get();
        
        $pdf = PDF::loadView('invoice', compact('order', 'orderitems'))
             ->setPaper('a4', 'portrait');
        return $pdf->download('invoice.pdf');
    }
}
?>

Let’s break this code into pieces and explain what is happening.
  • namespace App\Http\Controllers;: This defines that the PdfController belongs to the App\Http\Controllers namespace.
  • use App\Models\Order;: Imports the Order model to interact with the orders table in the database.
  • use App\Models\Orderitem;: Imports the Orderitem model, which likely represents individual items in an order.
  • use PDF;: Imports Laravel's PDF package (likely barryvdh/laravel-dompdf) for generating PDF documents.
index(): Method
  • Retrieves all records from the orders table using Order::get().
  • Passes the $orders data to the index view using compact('orders'), allowing the view to display the list of orders.
invoice($id): Method
  • Retrieves the order details using Order::find($id).
  • Fetches the items related to that order using Orderitem::where('order_id', $id)->get().
  • Calls PDF::loadView('invoice', compact('order', 'orderitems')); to generate a PDF using the invoice Blade view.
  • Returns the generated PDF for download as invoice.pdf.

7 Define a Route

Add the following routes in routes/web.php:

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PdfController;

Route::get('/', function () {
    return view('welcome');
});

Route::get('orders', [PdfController::class, 'index']);
Route::get('invoicepdf/{id}', [PdfController::class, 'invoice']);

8 Create Blade Views (Order List + Invoice Template)

Design templates for the order list (index.blade.php) and invoices (invoice.blade.php). These templates utilize dynamic data to display and generate PDFs.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>DataTables with Laravel 11</title>
     <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.0.1/css/bootstrap.min.css" rel="stylesheet">
    <link href="https://cdn.datatables.net/1.11.4/css/dataTables.bootstrap5.min.css" rel="stylesheet">
    <script src="https://code.jquery.com/jquery-3.5.1.js"></script>
    <script src="https://cdn.datatables.net/1.10.22/js/jquery.dataTables.min.js"></script>
    <script src="https://cdn.datatables.net/1.10.22/js/dataTables.bootstrap5.min.js"></script>
</head>
<body>
    <div class="container">
        <h1>Order List</h1>
        <table id="orderTable" class="table table-bordered ">
            <thead>
            <tr>
                    <th scope="col">Order Ref</th>
                    <th scope="col" class="text-right">Amount</th>
                    <th scope="col" class="text-right">Order Status</th>
                    <th scope="col" class="text-right">Download Invoice</th>
                </tr>
            </thead>
            <tbody>
                
                 @foreach($orders as $order)
            <tr>
                    <td>{{ $order->order_ref }}</td>
                    <td>{{ $order->amount }}</td>
                    <td>{{ $order->order_status }}</td>
                    <td>
                    <a target="_blank"
                        title="Generate Invoice"
                        href="invoicepdf/{{ $order->id }} ">
                        
                        {{ $order->order_invoice }} 
                </a>
                    </td>
                </tr>
                @endforeach       
               
            </tbody>
        </table>
    </div>

    <script>
        $(document).ready(function() {
            $('#orderTable').DataTable({});
        });
    </script>
</body>
</html>

Invoice Template (invoice.blade.php)
    
    <!DOCTYPE html>
<html>
<head>
    <title>Generate Dynamic PDFs in Laravel with DomPDF - GetSampleCode.com</title>
<style>
.table-bordered {
    border: 1px solid #ccc;
    border-spacing: 0px;
}
.table-bordered td,
.table-bordered th {
    border: 1px solid #ccc;
    padding:5px;
}
@page {

margin: 100px 25px;

}



header {

position: fixed;

top: -60px;

left: 0px;

right: 0px;

height: 50px;

font-size: 20px !important;

background-color: #000;

color: white;

text-align: center;

line-height: 35px;

}



footer {

position: fixed; 

bottom: -60px; 

left: 0px; 

right: 0px;

height: 50px; 

font-size: 20px !important;

background-color: #000;

color: white;

text-align: center;

line-height: 35px;

}
footer:after {
   content:"Page No - " counter(page);
}

    </style>
</head>
<body>
<header>

Welcome to Get Sample Code

</header>



<!-- Define Footer Block -->

<footer>
</footer>
<div style="font-size:24px; color:'#dddddd'; text-align:center "><i>Invoice</i></div>
<hr>
		<table class="table" width="100%">
            <tr>
                <td><b>Invoice:</b> # {{ $order->order_invoice }}</td>
                <td><b>Receiver:</b></td>
            </tr>
            <tr>
                <td><b>Date:</b> {{ \Carbon\Carbon::parse($order->order_at)->format('Y-m-d')  }} </td>
                <td>{{ $order->customer_first_name }} {{ $order->customer_last_name }} <br> {{ $order->customer_company }}</td>
            </tr>
           
            <tr>
            <td></td>
            <td >{{ $order->customer_address }}</td>
            </tr>
        </table>
		<hr>
        <table class="table table-bordered table-striped" width="100%" >
         <thead>
            <tr>
                <th>Item Description</th>
                <th>Price ($)</th>
                <th>Quantity</th>
                <th>Subtotal ($)</th>
            </tr>
        </thead>
    @php($total = 0);
    @foreach($orderitems as $item)
    @php($price = $item->item_price*$item->quantity)
    @php($total += $price)
    <tr> 
        <td>{{ $item->product_name }}</td>
        <td>{{ number_format($item->item_price, 2) }}</td>
        <td>{{ $item->quantity}}</td>
        <td style="text-align:right;">{{ number_format($price, 2) }}</td>
    </tr>
               @endforeach            
    <tr>
        <td colspan="3" style="text-align:right;">Total ($)</td>
        <td style = "text-align:right;">{{ number_format($total, 2) }}</td>
    </tr>
    </table>
    
  
</body>
</html>
    

9 Folder Structure

10 Test & Generate Your First PDF Invoice

Start your server with:

php artisan serve

Accessing Orders List:

  • When visiting /orders (or the route mapped to PdfController@index), it displays all orders.

Generating Invoice PDF:

  • When accessing /invoicepdf/{id}, it retrieves the order and its items and generates a downloadable invoice.pdf.

11 Best Practices & Tips

  • Use inline CSS (Dompdf has limited external CSS support).
  • Test complex layouts – Dompdf is great for simple invoices but consider Spatie/laravel-pdf (with Browsershot) for advanced CSS/JS.
  • For large PDFs: Queue the generation with Laravel Jobs.
  • Add images: Use absolute paths (public_path()).
  • UTF-8 issues: Ensure <meta charset="utf-8"> and font support.

12 Conclusion

You've now built a fully functional PDF invoice generator in Laravel 11 using Dompdf! Extend it with taxes, discounts, payments, or email attachments.
Revathi M - PHP and CodeIgniter Developer

Written by Revathi M

PHP Developer & Technical Writer · 10+ years building web applications with CodeIgniter and Laravel

Revathi specializes in PHP backend development, authentication systems, and REST API design. She writes practical, production-tested tutorials at Get Sample Code to help developers build secure applications faster.

Frequently Asked Questions

The barryvdh/laravel-dompdf package is used to convert HTML Blade views to PDF.

Run 'composer require barryvdh/laravel-dompdf'. The package is auto-discovered in Laravel 11.

Load the Blade view with data using PDF::loadView('invoice', compact('order', 'orderitems')), then return $pdf->download('invoice.pdf').

Use $pdf->stream('invoice.pdf') instead of $pdf->download().

Tables 'orders' (with order_ref, amount, etc.) and 'orderitems' (with product_name, item_price, quantity, etc.) are created via migrations.

Use internal CSS with @page for margins, inline styles, and simple tables. Page numbering can be added with CSS counters.

No, the barryvdh/laravel-dompdf package is auto-discovered in Laravel 11.

PHP 8.2 or higher, Composer, and a configured MySQL database.