bacon/bacon-qr-code
BaconQrCode is a pure-PHP QR code generator ported from ZXing's encoder. It renders QR codes to PNG, SVG, EPS or GD images with customizable size, margin, colors, gradients and eye styles, backed by a fast Reed-Solomon codec for quick output. Encoder only.
composer require bacon/bacon-qr-code. There is no Laravel service provider; it is a framework-agnostic PHP library you wire into your app.SvgImageBackEnd — best default for web, email, PDFs, and artifact-free output. Requires ext-xmlwriter.GDLibRenderer — simple PNG output using ext-gd; square modules only, no gradients/curves.ImagickImageBackEnd — styled raster output using ext-imagick; supports more visual features but has known rasterizer artifacts.use BaconQrCode\Renderer\Image\SvgImageBackEnd;
use BaconQrCode\Renderer\ImageRenderer;
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
use BaconQrCode\Writer;
Route::get('/invites/{invite}/qr', function (Invite $invite) {
$writer = new Writer(
new ImageRenderer(new RendererStyle(400), new SvgImageBackEnd())
);
$svg = $writer->writeString($invite->signedUrl());
return response($svg, 200)
->header('Content-Type', 'image/svg+xml');
});
Writer, RendererStyle, and the backend classes. The API surface is small: build a renderer, wrap it in Writer, then call writeString() or writeFile().namespace App\Services;
use BaconQrCode\Renderer\Image\SvgImageBackEnd;
use BaconQrCode\Renderer\ImageRenderer;
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
use BaconQrCode\Writer;
class QrCodeService
{
public function svg(string $payload, int $size = 400): string
{
$writer = new Writer(
new ImageRenderer(
new RendererStyle($size, 4),
new SvgImageBackEnd()
)
);
return $writer->writeString($payload);
}
}
$dataUri = 'data:image/svg+xml;base64,' . base64_encode($qrCodeService->svg($url));
writeString() for HTTP responses and writeFile() for storage. For downloads:return response($png, 200, [
'Content-Type' => 'image/png',
'Content-Disposition' => 'attachment; filename="ticket.png"',
]);
For persisted files:
Storage::disk('public')->put(
"qr/tickets/{$ticket->id}.svg",
$qrCodeService->svg($ticket->verificationUrl())
);
Ensure the parent directory exists before calling writeFile() if you use that method directly.
GDLibRenderer:use BaconQrCode\Renderer\GDLibRenderer;
use BaconQrCode\Writer;
$writer = new Writer(new GDLibRenderer(400));
$png = $writer->writeString($payload);
Do not try to use ImageRenderer with GD; GD support is exposed only through GDLibRenderer.
Cache generated output by payload and style. QR generation is cheap but not free at scale. Use a hash of payload, size, margin, backend, and style options as the cache key. Cache SVG strings for inline rendering and cached files for downloadable assets.
Queue batch generation. For event badges, invoice batches, or ticket exports, generate in a job and write files to Storage. Avoid rendering hundreds of QR codes synchronously in a web request.
Keep payloads short and intentional. QR density grows with payload length. Prefer short signed URLs or compact references over large JSON blobs. For payment QR codes, encode the exact payload string required by the payment provider rather than inventing a format.
Treat QR contents as public. A QR code is just an encoding of its payload; anyone who sees it can read it. Never put secrets, raw API tokens, or sensitive personal data directly in a QR code. Use signed, short-lived URLs or opaque references.
Test generated output structurally. The package is encoder-only, so you cannot decode its own output. Assert that SVG output is valid XML, has expected dimensions/attributes, and that PNG/SVG responses have the correct content type. Use real scanner tests for critical flows.
Prefer the latest 3.x release. Recent releases fix SVG gradient ID collisions, locale-sensitive SVG formatting, Kanji encoding, EC block counts, GD memory handling, and EPS arc rendering. If you generate multiple styled SVGs on one page, upgrade to at least 3.1.x.
Imagick can produce white pixel artifacts. The README documents single white pixels inside filled regions, especially with margin 0, caused by an ImageMagick rasterizer bug. The library cannot reliably work around it. Use SvgImageBackEnd or GDLibRenderer for artifact-free output. Do not attempt to post-process by removing isolated white pixels; that can corrupt legitimate curved modules.
GD is intentionally limited. GDLibRenderer does not support gradients or curved module shapes, so QR codes are always square. If your design requires gradients, rounded modules, or custom eye styles, use SVG or Imagick.
Extension availability causes runtime failures. SvgImageBackEnd needs ext-xmlwriter; GDLibRenderer needs ext-gd; ImagickImageBackEnd needs ext-imagick. Declare the extension you rely on in composer.json platform requirements and verify it in deployment. Add a fallback path if you support multiple hosting environments.
Version 3 requires PHP 8.1+. If you maintain an older Laravel/PHP application, you may need the 2.x line. Check the changelog before upgrading across major versions.
Non-ASCII payloads need attention. For Unicode content, be explicit about encoding and verify with the actual scanners you support. Version 3 added configurable UTF-8 ECI prefix behavior and fixed Kanji byte handling, so older releases are a poor choice for multilingual QR content.
Keep the quiet zone. The default margin exists for a reason. Reducing margin to 0 for design reasons can hurt scan reliability and makes Imagick edge artifacts more visible. Keep at least a few modules of light margin unless you have tested extensively.
High contrast and adequate module size matter more than styling. Dark foreground on light background scans best. Transparent backgrounds can look good in a UI but fail on dark surfaces or in print. If you place a logo over the center, test with multiple phone cameras and scanner apps.
SVG gradient IDs are now unique, but older versions were not. If you render several gradient QR codes inline on the same page, older versions could reuse IDs and cause one QR code’s gradient to affect another. Upgrade to 3.1.x, which uses xxh3-based IDs.
Locale formatting bugs existed in older SVG output. Decimal separators could break SVG transforms under locales that use commas. This was fixed; upgrade rather than patching generated output.
Imagick output format is configurable, but response headers are your responsibility. If you generate PNG, JPEG, or another raster format, set the matching Content-Type and file extension. Do not assume writeString() always returns PNG.
writeFile() does not replace Laravel storage abstractions. For application files, prefer writeString() plus Storage::put(). That keeps paths, disks, permissions, and test fakes under Laravel’s control.
Do not use this package for decoding or validating QR images. It contains the encoder only. If you need to read uploaded QR codes, use a separate decoder or a browser-side scanner library.
How can I help you explore Laravel packages today?