Reference
Rune documentation.
Getting started
Rune is a framework-agnostic QR code library. Install the adapter for your framework, or the core for a plain SVG string that works anywhere.
pnpm add @kroszborg/rune-reactpnpm add @kroszborg/rune-vuepnpm add @kroszborg/rune-wcpnpm add @kroszborg/rune # core / SSR string APIReact:
import { QRCode } from '@kroszborg/rune-react';
export default function App() {
return (
<QRCode
value="https://example.com"
dots={{ style: 'rounded' }}
corners={{ square: { style: 'extra-rounded' } }}
/>
);
}Anywhere (server, edge, Node, workers):
import { toSVGString } from '@kroszborg/rune';
const svg = toSVGString({ value: 'https://example.com', dots: { style: 'rounded' } });Every package (below) accepts the exact same RuneOptions object.
| Package | What it is |
|---|---|
@kroszborg/rune | Core: encoder, renderer, exports, data builders, presets. Zero runtime deps. |
@kroszborg/rune-react | React <QRCode> (web). Also the base for React Native. |
@kroszborg/rune-vue | Vue 3 <QRCode>. |
@kroszborg/rune-wc | Vanilla renderRune() + a <rune-qr> Web Component. |
@kroszborg/rune-decode | Decoder: read a QR back from a matrix or image. |
@kroszborg/rune-cli | The rune CLI: generate or decode from the terminal. |
RuneOptions
The single options object accepted by every renderer and adapter.
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | - | Data to encode (required). |
size | number | 256 | Output width/height in px. |
margin | number | 4 | Quiet zone in modules. |
dots | { style, color, gradient } | - | Data-module style and fill. |
corners | CornerOptions | - | Finder ring + core styles and fills. |
background | string | BackgroundOptions | '#ffffff' | Color, gradient, or image. 'transparent' allowed. |
logo | LogoOptions | - | Center logo (auto-raises ECL). |
frame | FrameOptions | - | Outer frame + CTA text. |
qr | { errorCorrectionLevel, version, mask } | - | Encoding controls. |
preset | PresetName | - | Named base style; explicit options win. |
ariaLabel | string | 'QR code: {value}' | Accessible label on the <svg>. |
Dot styles
Set with dots.style. Eight styles; rounded and extra-rounded connect adjacent modules for a fluid look.
<QRCode value="..." dots={{ style: 'classy-rounded' }} />Finder styles
The three corner markers. corners.square styles the 7×7 ring; corners.dot styles the inner 3×3 block. Each takes its own color or gradient.
Ring (corners.square.style)
Core (corners.dot.style)
| Field | Type |
|---|---|
corners.square.style | 'square' | 'rounded' | 'extra-rounded' | 'circle' | 'leaf' |
corners.square.color | string |
corners.square.gradient | Gradient |
corners.dot.style | 'square' | 'rounded' | 'dot' |
corners.dot.color / gradient | string | Gradient |
Colors & gradients
Every element (dots, each finder part, background) takes a solid color or a gradient. A gradient is linear or radial with any number of stops.
| Gradient field | Type | Notes |
|---|---|---|
type | 'linear' | 'radial' | |
rotation | number | Degrees, linear only. |
stops | { offset, color }[] | offset is 0-1. |
dots={{ gradient: {
type: 'linear', rotation: 45,
stops: [{ offset: 0, color: '#0f7a5c' }, { offset: 1, color: '#0b6b4f' }],
} }}Background
Pass a color string, or a BackgroundOptions object for a gradient or image. 'transparent' is accepted (useful for overlaying).
| Field | Type | Description |
|---|---|---|
color | string | Solid fill, or 'transparent'. |
gradient | Gradient | Gradient fill behind the modules. |
image | string | Image URL rendered behind the modules. |
background="transparent"
// or
background={{ gradient: { type: 'radial', stops: [...] } }}Logo
A center logo. Rune automatically raises the error-correction level to H when a logo is present (unless you set one) and clamps the logo size to what stays scannable at that level, so you cannot accidentally produce an unreadable code.
| Field | Type | Default | Description |
|---|---|---|---|
src | string | - | https, blob:, data:image/…, or a path. |
size | number | 0.25 | Fraction of width (clamped per ECL). |
margin | number | 1 | Clear space around the logo, in modules. |
hideDots | boolean | true | Clear the modules behind the logo. |
shape | 'square' | 'rounded' | 'circle' | 'square' | Backing plate shape. |
background | string | bg color | Backing plate color. |
<QRCode value="..." logo={{ src: '/logo.png', size: 0.28, shape: 'circle' }} />In React you can pass a live React node instead of an image with logoElement (see Frameworks).
Frame & CTA
An outer frame with an optional call-to-action label.
| Field | Type | Description |
|---|---|---|
style | 'none' | 'square' | 'rounded' | Border shape. 'none' draws text only. |
text | string | CTA label, e.g. "SCAN ME". |
position | 'bottom' | 'top' | Where the text band sits. |
color / textColor | string | Frame and text colors. |
font | string | CSS font-family for the label. |
Error correction
Higher levels recover from more damage but hold less data. Set with qr.errorCorrectionLevel. You can also pin the version (1-40) and mask (0-7); both are chosen automatically by default.
| Level | Recovery | Use when |
|---|---|---|
L | ~7% | Clean environments, minimal data. |
M | ~15% | General purpose (default). |
Q | ~25% | Print, harsher conditions. |
H | ~30% | Codes with a center logo. |
Presets
Named starting styles. Pass preset; any explicit option you also set wins over the preset.
<QRCode value="..." preset="mint" />Data builders
Turn structured input into correctly-escaped QR content. Import with import { data } from '@kroszborg/rune' and pass the result as value.
data.url('example.com')
data.wifi({ ssid: 'Home', password: 'hunter2', encryption: 'WPA' })
data.email({ to: 'a@b.com', subject: 'Hi', body: '...' })
data.sms({ to: '+15551234', body: 'hi' })
data.tel('+15551234')
data.geo({ lat: 26.9124, lng: 75.7873 })
data.vcard({ fullName: 'Ada Lovelace', email: 'ada@x.com', phone: '+1555' })
data.mecard({ fullName: 'Ada Lovelace', phone: '+1555' })
data.event({ title: 'Launch', start: '2026-07-14T09:00Z', end: '2026-07-14T10:00Z' })
data.crypto({ coin: 'bitcoin', address: '1abc', amount: 0.5 })Export
The core exports SVG anywhere; raster and PDF live in @kroszborg/rune/node so the browser bundle stays free of native binaries.
| Function | Returns | Where |
|---|---|---|
toSVGString(o) | string | anywhere (sync) |
renderToParts(o) | SvgParts | anywhere (adapters) |
toDataURL(o, r?) | Promise<string> | browser (Canvas) |
toBuffer(o, r?) | Promise<Uint8Array> | node (/node) |
toPDF(o) | Promise<Uint8Array> | node (/node) |
RasterOptions: format ('png' | 'jpeg' | 'webp'), quality (0-1), background.
import { toBuffer, toPDF } from '@kroszborg/rune/node';
const png = await toBuffer({ value: '...', size: 512 }, { format: 'png' });
const pdf = await toPDF({ value: '...', size: 512 });Decoding
@kroszborg/rune-decode reads a QR back into its data, with full Reed-Solomon error correction.
import { decode, decodeMatrix } from '@kroszborg/rune-decode';
// From raw RGBA pixels (e.g. a canvas ImageData)
const result = decode({ data, width, height });
if (result) console.log(result.text, result.version, result.ecl);
// From a boolean module matrix
const { text } = decodeMatrix(modules); // modules[y][x], true = dark| Function | Input | Returns |
|---|---|---|
decode(img) | { data, width, height } | MatrixDecodeResult | null |
decodeMatrix(m) | boolean[][] | MatrixDecodeResult |
MatrixDecodeResult = { text, version, ecl, mask }.
CLI
@kroszborg/rune-cli generates and decodes from the terminal.
# generate
rune "https://example.com" -o qr.png --dots rounded --preset mint
rune "https://example.com" --gradient "#0f7a5c,#12946e,45" -o qr.pdf
rune "SCAN ME" --frame "SCAN ME" -o cta.svg # no -o prints SVG to stdout
# decode
rune decode qr.png| Flag | Description |
|---|---|
-o, --out | Output file (.svg .png .jpeg .webp .pdf). Stdout SVG if omitted. |
-s, --size / -m, --margin | Size in px / quiet-zone modules. |
--ecl | L | M | Q | H. |
--dots / --dot-color | Dot style / color. |
--gradient "a,b,r" | Linear gradient from,to,rotation. |
--square / --core | Finder ring / core style. |
--bg / --frame / --logo / --preset | Background, CTA frame, logo, preset. |
Frameworks
React
Accepts every RuneOptions prop, plus style, className, and logoElement (a React node overlaid in the center).
import { QRCode } from '@kroszborg/rune-react';
<QRCode value="..." dots={{ style: 'rounded' }} logoElement={<MyLogo />} qr={{ errorCorrectionLevel: 'H' }} />Vue 3
<script setup>
import { QRCode } from '@kroszborg/rune-vue';
</script>
<template>
<QRCode value="..." :dots="{ style: 'rounded' }" />
</template>Web Component (vanilla)
Attributes for simple cases; set the .options property for the full object.
import { register } from '@kroszborg/rune-wc';
register();
// <rune-qr value="..." dot-style="rounded" preset="mint"></rune-qr>
// el.options = { value: '...', dots: { gradient: {...} }, frame: { text: 'SCAN ME' } };React Native
import { SvgXml } from 'react-native-svg';
import { toSVGString } from '@kroszborg/rune';
const svg = toSVGString({ value: '...', dots: { style: 'rounded' } });
export default () => <SvgXml xml={svg} width={256} height={256} />;Svelte, Solid, Astro, Angular, Lit
No dedicated package needed - inject the string from toSVGString, or use the <rune-qr> Web Component.
const svg = toSVGString({ value: '...', dots: { style: 'rounded' } });
{@html svg} // Svelte
<div innerHTML={svg} /> // Solid
<Fragment set:html={svg} /> // Astro
<div [innerHTML]="svg"> // Angular (sanitizer-bypassed)
<rune-qr value="..."></rune-qr> // Lit / Alpine / plain HTML