When a learner finishes a course they want a certificate that behaves like a document: something they can print at full quality, attach to a job application and that an employer can check is genuine. In this tutorial you'll build exactly that in Laravel: a Blade-designed certificate rendered as a vector PDF, with a QR code that resolves to a verification page and an email that delivers it automatically. This post originally appeared on Accreditly as is a good base to adapt.
{{-- resources/views/certificates/template.blade.php --}}
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
body { font-family: Georgia, serif; color: #1a2233; margin: 0; }
.certificate { padding: 60px; border: 6px double #b28a2f; margin: 24px; text-align: center; }
.heading { font-size: 15px; letter-spacing: 4px; text-transform: uppercase; color: #b28a2f; }
h1 { font-size: 44px; margin: 24px 0 8px; }
.course { font-size: 22px; margin: 4px 0 28px; }
.issued { font-size: 15px; color: #5a6478; }
.qr { margin-top: 36px; }
.verify-url { font-size: 12px; color: #5a6478; }
</style>
</head>
<body>
<div class="certificate">
<p class="heading">Certificate of Completion</p>
<h1>{{ $certificate->user->name }}</h1>
<p class="course">has completed {{ $certificate->course }}</p>
<p class="issued">Issued {{ $certificate->issued_at->format('j F Y') }}</p>
<div class="qr">
{!! QrCode::size(110)->generate(route('certificates.verify', $certificate)) !!}
</div>
<p class="verify-url">{{ route('certificates.verify', $certificate) }}</p>
</div>
</body>
</html>
Two things are doing quiet work here. The design flows like a document rather than being pinned to fixed pixel dimensions, because the PDF renders onto A4 portrait pages. And the QR code is generated as inline SVG, so the renderer has nothing external to fetch.
Give every certificate a verifiable identity
Verification only needs a stable public identifier and a page that resolves it. A UUID on the certificate row is enough.
Schema::create('certificates', function (Blueprint $table) {
$table->id();
$table->uuid('uuid')->unique();
$table->foreignId('user_id')->constrained();
$table->string('course');
$table->timestamp('issued_at');
$table->timestamps();
});
The verification route binds on the UUID, so guessing sequential ids gets nobody anywhere:
Route::get('/verify/{certificate:uuid}', function (Certificate $certificate) {
return view('certificates.verify', ['certificate' => $certificate]);
})->name('certificates.verify');
The verify view shows the learner's name, the course and the issue date. Anyone scanning the QR code on a printed certificate lands on your domain and sees the record, which is the whole trust story: the paper claims it, your database confirms it.
For the QR code itself, pull in the standard package. The default renderer outputs SVG, which is what you want:
composer require simplesoftwareio/simple-qrcode
Render it as a vector PDF
You have three ways to turn that Blade view into a PDF. DomPDF is pure PHP but supports a narrow slice of CSS, and a certificate is exactly the kind of designed layout it mangles. Browsershot renders correctly because it drives real Chrome, but now you are installing and babysitting Chrome on your server. The third route is an .
In this tutorial you've designed a certificate in Blade, rendered it as a vector PDF, given it a QR verification link and delivered it by email on course completion. The full version, including the fixed-width scaling notes and the Open Badges pairing, lives on Accreditly.
How are you issuing certificates in your Laravel apps at the moment? Share your approach in the comments below.
SOCIAL SHARE CARD GENERATOR