Skip to main content
If your QR codes aren’t rendering, saving, or generating as expected, the sections below cover the most common root causes and their fixes. Each issue includes a concise diagnostic step and a working solution you can apply immediately.
If your problem isn’t listed here, open a GitHub issue and include your PHP version, Laravel version, the QR code method you called, and the full error message or stack trace. Those four details dramatically speed up diagnosis.

Common Issues

Symptom: The QR code shows as a literal escaped string like <svg ...> or a blank area instead of the actual image.Cause: Blade’s {{ }} syntax HTML-escapes its output. QR codes returned as SVG or raw binary must not be escaped.Fix — unescaped echo syntax:
{{-- ❌ Escapes the SVG/PNG string — shows garbled text --}}
{{ QrCode::generate('Hello World') }}

{{-- ✅ Outputs raw SVG/binary correctly --}}
{!! QrCode::generate('Hello World') !!}
Alternative — use the Blade component instead:The <x-qr-code> component handles output escaping automatically and is the recommended approach for Blade templates:
<x-qr-code data="Hello World" />

{{-- With options --}}
<x-qr-code
    data="https://example.com"
    size="300"
    color="#ff0000"
    margin="2"
/>
Symptom: An exception is thrown when you call ->format('png')->generate(...) or ->format('webp')->generate(...). SVG generation works fine.Cause: PNG and WebP rasterisation requires either the ext-imagick or ext-gd PHP extension. If neither is available, the renderer has no way to produce a bitmap image.Fix — verify your extensions:Run the following in your project to see which extensions are loaded:
php -m | grep -iE 'imagick|gd'
Or check phpinfo() in a browser request and search for “imagick” or “gd”.
  • Install Imagick (recommended): Follow your system’s package manager instructions, e.g. sudo apt install php-imagick on Ubuntu, then restart PHP-FPM.
  • Install GD: Most PHP distributions include GD by default. Enable it in php.ini by uncommenting extension=gd.
Once at least one extension is active, PNG and WebP generation will work automatically.
Symptom: CannotWriteFileException (or a generic PHP warning) when you pass a $filename argument to generate().Cause: The target directory does not exist, or the web server process does not have write permission to it.Fix — create the directory and confirm permissions:
use Linkxtr\QrCode\Facades\QrCode;

$path = storage_path('app/qrcodes/qr.svg');

// Ensure the directory exists and is writable before generating
if (! is_dir(dirname($path))) {
    mkdir(dirname($path), 0755, true);
}

QrCode::generate('https://example.com', $path);
On a Linux server, also confirm the web server user (typically www-data) owns or has write access to the directory:
sudo chown -R www-data:www-data storage/app/qrcodes
sudo chmod -R 755 storage/app/qrcodes
Symptom: The QR code generates successfully but scanners struggle or refuse to read it. The code looks extremely dense.Cause: QR codes have a finite data capacity. Long strings, high error correction levels, and small physical sizes all reduce scannability.Fix — reduce data and increase size with high error correction:
// ✅ Use a short URL instead of embedding the full payload
QrCode::size(400)
    ->errorCorrection('H')
    ->generate('https://short.url/abc123');
Guidelines:
  • Keep the data payload as short as possible. Encode an ID or short URL, not a full JSON blob.
  • Use errorCorrection('H') (30 % recovery) for codes that will be printed small or on uneven surfaces.
  • Set size() to at least 400 when using H error correction.
  • For extremely long data, consider storing it server-side and encoding only a lookup key.
Symptom: An InvalidArgumentException is thrown when calling a data-type method like WiFi(), PhoneNumber(), Email(), or VCard().Cause: Since v2.0, all data types validate their inputs strictly. Passing malformed data (wrong keys, missing required fields, bad phone format, etc.) throws an exception instead of silently generating a broken QR code.Fix — validate user input before passing it in:
use InvalidArgumentException;
use Linkxtr\QrCode\Facades\QrCode;

try {
    $qr = QrCode::WiFi([
        'ssid'       => $request->validated('ssid'),
        'encryption' => $request->validated('encryption'), // WEP | WPA | WPA2 | NOPASS
        'password'   => $request->validated('password'),
    ]);
} catch (InvalidArgumentException $e) {
    return back()->withErrors(['qr' => 'Invalid WiFi credentials: ' . $e->getMessage()]);
}
Always run $request->validate(...) or equivalent before passing user-supplied values to any data-type method.
Symptom: The package uses Imagick for PNG/WebP rendering, but you need to use GD — for example, because your Imagick build has a known bug, or your deployment environment restricts Imagick.Cause: By default, the package prefers Imagick over GD when both are available.Fix — set the environment variable or config key:In your .env file:
QR_CODE_FORCE_GD=true
Or, if you have published config/qrcode.php, set the key directly:
return [
    // ... other options
    'force_gd' => true,
];
With either setting in place, the package always uses GD for bitmap rendering, even when Imagick is installed.
Symptom: You published config/qrcode.php and changed defaults like size, format, or error_correction, but generated QR codes still use the old values.Cause: Config-driven defaults are injected only when the Generator is resolved through Laravel’s service container — via the QrCode facade, the <x-qr-code> Blade component, or constructor injection. If you instantiate Generator manually with new Generator(), it receives the hardcoded package defaults, not your published config.Fix — use the facade or container injection:
// ❌ Bypasses config/qrcode.php — uses hardcoded defaults
$qr = (new \Linkxtr\QrCode\Generator())->generate('Hello');

// ✅ Reads config/qrcode.php defaults
use Linkxtr\QrCode\Facades\QrCode;
$qr = QrCode::generate('Hello');

// ✅ Also reads config/qrcode.php defaults via the container
public function __construct(private \Linkxtr\QrCode\Generator $qrCode) {}
After changing config/qrcode.php, clear the config cache so Laravel picks up your edits:
php artisan config:clear