tumutech / laravel-qr-code
QR code generation for Laravel — PNG, SVG, Blade, storage, and branded styles
Requires
- php: ^8.2
- bacon/bacon-qr-code: ^3.0
- endroid/qr-code: ^5.1
- illuminate/contracts: ^10.0|^11.0|^12.0
- illuminate/filesystem: ^10.0|^11.0|^12.0
- illuminate/http: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
- illuminate/view: ^10.0|^11.0|^12.0
Requires (Dev)
- laravel/pint: ^1.18
- orchestra/testbench: ^8.0|^9.0|^10.0
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^10.5|^11.0
Suggests
- ext-gd: Required to generate PNG and WebP images.
- ext-imagick: Optional; used for styled PNG when available.
README
Branded, scannable QR codes inside Laravel — center logos, style templates, scan-safe checks, HTTP responses, Blade, storage, and data URIs for Inertia.
composer require tumutech/laravel-qr-code
Laravel auto-discovers the package. No extra setup.
Optional defaults:
php artisan vendor:publish --tag=qr-code-config
What it offers
- Center brand logo with a padded punchout so modules do not show through
scanSafe()— contrast, quiet zone, and logo-size checks before you ship- Named templates and frames for consistent product branding
- HTTP responses, Blade embeds, disk storage, and data URIs for Inertia/React
- PNG, SVG, WebP, EPS, PDF, and raw binary
- Wi-Fi, email, SMS, and vCard payloads
Requirements
- PHP 8.2+
- Laravel 10, 11, or 12
- GD extension for PNG / WebP
Usage
use Tumutech\QrCode\Facades\QrCode; return QrCode::size(300)->png('https://example.com');
Center logo
Put your mark in the middle. Error correction is raised to high automatically. By default the logo is ~22% of the QR size, with a rounded background punchout behind it.
return QrCode::size(400) ->logo(public_path('images/brand.png')) ->scanSafe() ->png('https://example.com/invite');
Optional pixel width, or disable the punchout pad:
QrCode::logo(public_path('images/brand.png'), 88); // custom width QrCode::logo(public_path('images/brand.png'), null, false); // no punchout
Keep the logo small enough to scan. scanSafe() rejects logos that cover too much of the code.
Style + brand in one call
QrCode::template('gradient-ocean') ->logo(public_path('images/brand.png')) ->scanSafe() ->png('https://example.com'); QrCode::module('dots', 0.8) ->eye('circle') ->color('#101824') ->backgroundColor('#ffffff') ->logo(public_path('images/brand.png')) ->scanSafe() ->png('https://example.com');
Styled codes support PNG and SVG.
| Option | Values |
|---|---|
module() |
square, dots, rounded |
eye() |
square, circle, pointy |
frame() |
none, badge, card |
gradient() |
start, end, direction: vertical, horizontal, diagonal, inverse_diagonal, radial |
Templates: classic, classic-inverted, dots-circle, dots-square-eyes, dots-pointy, dots-small, dots-large, rounded-soft, rounded-medium, rounded-strong, gradient-ocean, gradient-sunset, gradient-aurora, gradient-forest, gradient-inverse, pointy-brand, circle-eyes-square, badge-brand, card-dots.
Embed in Blade
<x-qr-code data="https://example.com" :size="200" /> <x-qr-code data="https://example.com/invite" :size="240" :logo="public_path('images/brand.png')" template="dots-circle" /> <img src="@qrcode('https://example.com')" alt="QR">
When logo is set, the Blade component turns on scanSafe() for you.
Inertia / React
Generate on Laravel and pass a data URI:
return Inertia::render('Ticket', [ 'qr' => QrCode::size(240) ->logo(public_path('images/brand.png')) ->scanSafe() ->dataUri($ticketUrl), ]);
<img src={qr} width={240} height={240} alt="Ticket QR" />
Save to storage
QrCode::logo(public_path('images/brand.png')) ->scanSafe() ->store('https://example.com', 'qrs/ticket.png'); QrCode::disk('s3')->store('https://example.com', 'qrs/ticket.png');
Wi-Fi, email, SMS, vCard
$qr = QrCode::wifi([ 'ssid' => 'Office', 'password' => 'secret', ]); echo $qr->getDataUri(); QrCode::email('hello@example.com', 'Subject', 'Body'); QrCode::sms('+15551234567', 'Hello'); QrCode::vcard([ 'firstName' => 'Ada', 'lastName' => 'Lovelace', 'organization' => 'Example', 'phones' => ['+15551234567'], 'emails' => ['ada@example.com'], ]);
Everyday methods
| Method | What it does |
|---|---|
logo() |
Center brand mark with optional punchout |
scanSafe() |
Contrast, quiet zone, logo coverage checks |
template() / frame() |
Brand presets and print frames |
module() / eye() / gradient() |
Module shape, finder eyes, fill |
size() / margin() / format() |
Canvas size and output type |
color() / backgroundColor() |
RGB or #hex |
png() / svg() / response() |
HTTP image |
dataUri() / generate() |
Embed or get raw bytes |
store() / disk() |
Save to Laravel storage |
wifi() / email() / sms() / vcard() |
Structured payloads |
Config
Published file: config/qr-code.php
Defaults you can set: format, size, margin, error correction, colors, and storage disk.
Help: enable PHP GD
PNG and WebP need the GD PHP extension. Composer cannot install it for you.
Check:
php -m | grep -i gd
If nothing shows, enable it:
Ubuntu / Debian
sudo apt install php-gd
sudo systemctl restart php8.2-fpm # or your PHP-FPM / Apache service
Windows
In php.ini, uncomment:
extension=gd
Restart Apache, Nginx+PHP-FPM, or php artisan serve.
Laravel Sail / Docker
Install gd in the image (Sail usually includes it). Rebuild if needed:
sail build --no-cache
Then confirm again with php -m | grep -i gd.
SVG output can still work without GD. Styled PNG uses GD when Imagick is not installed.
Credits
This package uses:
- bacon/bacon-qr-code (BSD-2-Clause)
- endroid/qr-code (MIT)
Their license notices ship with Composer under vendor/. Full texts: Bacon LICENSE, Endroid LICENSE.
Contributing
See CONTRIBUTING.md.
License
MIT