Skip to content

Add a center image

A center image can brand a QR code, but it intentionally covers encoded modules. Prepare the source before rendering, keep the overlay modest, and test the final artifact with real scanners.

  • Start with a QR code that scans without an overlay.
  • Choose the renderer and prepare the source type it accepts.
  • Use high error correction for logo-style overlays: .errorCorrection('H') or errorCorrectionLevel: 'H'.

Every visual renderer uses the same overlay geometry:

image: {
source: preparedSource,
size: 0.3,
padding: 1,
clearBackground: true,
}
Option Default Accepted value Meaning
source none renderer-specific prepared source Image content; required when image is set
size 0.4 finite number greater than 0 and at most 1 Image box relative to matrix width, excluding the quiet zone
padding 1 non-negative finite number Cleared space around the image, in modules
clearBackground true boolean Replaces covered modules and padding with the light color

The source is centered and contained without cropping. size: 1 is valid input, not a promise that the result will scan. Start around 0.20.3, preserve padding, and increase only after testing.

Output Source type Preparation
Core SVG QRCodeDataImageURL Convert bytes or a Blob to an embedded data:image/... URL
Browser Canvas or PNG-backed Image loaded CanvasImageSource Decode an Image element, canvas, video frame, or ImageBitmap first
Node.js PNG PNG Buffer Read or download valid PNG bytes before rendering
React SVG QRCodeDataImageURL in options.image.source Prepare outside render; memoize options when useful
React Image or Canvas loaded CanvasImageSource in options.image.source Store the decoded source in state before mounting output
Vue SVG QRCodeDataImageURL in options.image.source Prepare before updating a ref or computed options
Vue Image or Canvas loaded CanvasImageSource in options.image.source Store the decoded source in a shallowRef before mounting output
Svelte SVG QRCodeDataImageURL in options.image.source Prepare before updating rune-mode state
Svelte Image or Canvas loaded CanvasImageSource in options.image.source Store the decoded source in $state before mounting output
Angular SVG QRCodeDataImageURL in [options] Prepare before updating the input
Angular Image or Canvas loaded CanvasImageSource in [options] Store the decoded source in a signal before rendering output

QRCodeSDK never reads an image path or fetches a URL on your behalf.

In Node.js, convert already-read bytes to an embedded image URL:

import {readFile} from 'node:fs/promises';
import {type QRCodeDataImageURL, QRCodeSVGRenderer, qrcode} from '@qrcodesdk/core';
const bytes = await readFile('./logo.png');
const source = `data:image/png;base64,${bytes.toString('base64')}` as QRCodeDataImageURL;
const svg = qrcode('https://qrcodesdk.dev')
.errorCorrection('H')
.render(QRCodeSVGRenderer({image: {source, size: 0.3}}));

In a browser, FileReader.readAsDataURL() prepares the same source type from a Blob or uploaded File.

Decode the image before calling the synchronous Canvas or Image renderer:

import {QRCodeCanvasRenderer} from '@qrcodesdk/browser';
import {qrcode} from '@qrcodesdk/core';
const source = new Image();
source.src = '/logo.png';
await source.decode();
const canvas = qrcode('https://qrcodesdk.dev')
.errorCorrection('H')
.render(QRCodeCanvasRenderer({image: {source, size: 0.3}}));

Unloaded or zero-sized sources throw synchronously.

import {readFile} from 'node:fs/promises';
import {qrcode} from '@qrcodesdk/core';
import {QRCodePNGRenderer} from '@qrcodesdk/node';
const source = await readFile('./logo.png');
const png = qrcode('https://qrcodesdk.dev')
.errorCorrection('H')
.render(QRCodePNGRenderer({image: {source, size: 0.3}}));

The Node renderer accepts PNG bytes only. It decodes and alpha-composites the source in memory.

React, Vue, Svelte, and Angular do not load image sources inside their QR components. Prepare browser sources in application state, then pass stable options after decoding.

import {useState} from 'react';
import {QRCodeImage} from '@qrcodesdk/react';
export function QRCodeWithLogo() {
const [source, setSource] = useState<HTMLImageElement>();
async function loadLogo(url: string) {
const image = new Image();
image.src = url;
await image.decode();
setSource(image);
}
return source ? (
<QRCodeImage
data="https://qrcodesdk.dev"
options={{errorCorrectionLevel: 'H', image: {source, size: 0.3}}}
/>
) : (
<button type="button" onClick={() => void loadLogo('/logo.png')}>
Load logo
</button>
);
}
<script setup lang="ts">
import {computed, shallowRef} from 'vue';
import type {QRCodeImageOptions} from '@qrcodesdk/browser';
import {QRCodeImage} from '@qrcodesdk/vue';
const source = shallowRef<HTMLImageElement>();
const options = computed<QRCodeImageOptions | undefined>(() =>
source.value ? {errorCorrectionLevel: 'H', image: {source: source.value, size: 0.3}} : undefined,
);
async function loadLogo(url: string) {
const image = new Image();
image.src = url;
await image.decode();
source.value = image;
}
</script>
<template>
<QRCodeImage v-if="options" data="https://qrcodesdk.dev" :options="options" />
<button v-else type="button" @click="loadLogo('/logo.png')">Load logo</button>
</template>
<script lang="ts">
import type {QRCodeImageOptions} from '@qrcodesdk/browser';
import {QRCodeImage} from '@qrcodesdk/svelte';
let source = $state<HTMLImageElement>();
const options: QRCodeImageOptions | undefined = $derived(
source ? {errorCorrectionLevel: 'H', image: {source, size: 0.3}} : undefined,
);
async function loadLogo(url: string) {
const image = new Image();
image.src = url;
await image.decode();
source = image;
}
</script>
{#if options}
<QRCodeImage data="https://qrcodesdk.dev" {options} />
{:else}
<button type="button" onclick={() => loadLogo('/logo.png')}>Load logo</button>
{/if}
import {Component, signal} from '@angular/core';
import {QRCodeImage} from '@qrcodesdk/angular';
@Component({
selector: 'app-qrcode-with-logo',
imports: [QRCodeImage],
template: `
@if (source(); as image) {
<qrcode-image
[options]="{errorCorrectionLevel: 'H', image: {source: image, size: 0.3}}"
data="https://qrcodesdk.dev" />
}
`,
})
export class QRCodeWithLogo {
readonly source = signal<HTMLImageElement | undefined>(undefined);
async loadLogo(url: string) {
const image = new Image();
image.src = url;
await image.decode();
this.source.set(image);
}
}

Test the rendered or downloaded artifact—not only the preview—at the smallest expected size and after taking screenshots, optimizing images, printing, or compressing. Test several scanner apps and representative devices. If scanning is unreliable, reduce the image, restore padding and the four-module quiet zone, increase module size, simplify curved styling, or shorten the payload.

For exact source constraints, see the SVG, Canvas, PNG-backed Image, or PNG Buffer reference.