Skip to content
omaris

Data Display

QR Code

A QR code, drawn as vector, encoded here.

import { QrCode } from 'omaris'
Learn

Examples

Basic

One attribute. The encoder is in the component, so there is no dependency and no network call, and the output is SVG — sharp at any size and at the printer's resolution rather than the screen's.

Scan me
<script lang="ts">	import { QrCode } from 'omaris';​	let value = $state('https://omaris.dev');</script>​<div class="flex w-full flex-col items-center gap-4">	<QrCode {value} variant="card" size={188} caption="Scan me" />​	<input		bind:value		class="w-full max-w-xs rounded-shape-sm border border-border bg-surface-container-lowest px-3 py-2 text-sm text-foreground outline-none focus-visible:border-primary focus-visible:ring-2 focus-visible:ring-ring"		aria-label="What the code says"	/></div>

Styles

shape draws the modules, eyes the three corners, and gradient paints them. fluid rounds only the corners nothing joins, so a run of modules reads as one stroke. None of it changes what the code says.

square
dot
fluid
<script lang="ts">	import { QrCode } from 'omaris';</script>​<div class="flex w-full flex-wrap items-end justify-center gap-4">	<QrCode value="https://omaris.dev" size={124} variant="card" caption="square" />	<QrCode		value="https://omaris.dev"		shape="dot"		eyes="circle"		size={124}		variant="card"		caption="dot"	/>	<QrCode		value="https://omaris.dev"		shape="fluid"		eyes="leaf"		gradient={{ from: 'var(--color-primary)', to: 'var(--color-tertiary)', angle: 45 }}		size={124}		variant="card"		caption="fluid"	/></div>

Payloads

A QR code is only ever text; what makes a phone offer to join a network rather than run a search is the shape of that text. qrWifi, qrVcard, qrTel, qrEmail, qrGeo, qrSms and qrCalendar build those shapes, escaping included — a missing semicolon in a Wi-Fi payload scans perfectly and does nothing.

Join the Wi-Fi
Save the contact
Open the map
<script lang="ts">	import { QrCode, qrGeo, qrVcard, qrWifi } from 'omaris';</script>​<div class="flex w-full flex-wrap items-end justify-center gap-4">	<QrCode		value={qrWifi({ ssid: 'Omaris Guest', password: 'hunter2' })}		size={132}		variant="card"		caption="Join the Wi-Fi"	/>	<QrCode		value={qrVcard({			name: 'Ada Lovelace',			organisation: 'Analytical Engines',			email: 'ada@example.com',			phone: '+15550100'		})}		size={132}		variant="card"		caption="Save the contact"	/>	<QrCode value={qrGeo(36.19, 44.01)} size={132} variant="card" caption="Open the map" /></div>

Logo and download

A logo is damage: it covers modules, so level rises to H on its own when one is set. Keep logoSize under 0.3 and the code still reads. bind:this gives download(), toDataURL() and toBlob(), with the theme's colours resolved — what is saved is what was on screen.

<script lang="ts">	import { Button, QrCode } from 'omaris';​	let code = $state<QrCode | null>(null);​	const MARK =		'data:image/svg+xml;charset=utf-8,' +		encodeURIComponent(			'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><rect width="48" height="48" rx="12" fill="%230a84ff"/><path d="M14 30l10-14 10 14z" fill="white"/></svg>'		);</script>​<div class="flex w-full flex-col items-center gap-4">	<QrCode		bind:this={code}		value="https://omaris.dev"		logo={MARK}		shape="fluid"		eyes="rounded"		size={196}		variant="card"	/>​	<div class="flex gap-2">		<Button size="sm" variant="tonal" onclick={() => code?.download('omaris', 'image/png')}>			Download PNG		</Button>		<Button size="sm" variant="outlined" onclick={() => code?.download('omaris', 'image/svg+xml')}>			Download SVG		</Button>	</div></div>

Overridden

Every part is reachable: classes names the frame and the caption, class still covers the root, and the code's own colours are props rather than classes so the SVG keeps them when it is exported.

A code that matches the page
<script lang="ts">	import { QrCode } from 'omaris';</script>​<div class="flex w-full justify-center">	<QrCode		value="https://omaris.dev/docs/components/qr-code"		shape="fluid"		eyes="leaf"		size="13rem"		margin={2}		background="transparent"		color="var(--color-primary)"		pupilColor="var(--color-tertiary)"		caption="A code that matches the page"		class="rounded-shape-xl bg-primary-container p-4"		classes={{ caption: 'text-primary-container-foreground' }}	/></div>

When to use it

Use it for

  • A link people reach with a camera: a menu, a form, a ticket, a device's setup page. value is the whole API.
  • Something a phone should act on: join this network, save this contact, call this number, add this event. Build the payload with qrWifi, qrVcard, qrTel, qrEmail, qrSms, qrGeo or qrCalendar, not by hand.
  • A code that will be printed. It is SVG, so it prints at the printer's resolution. download('name', 'image/svg+xml') gives a designer a file.
  • A code that matches the product: shape, eyes, gradient and logo. The correction level rises to H on its own when a logo covers the middle.

Not for

  • A barcode a scanner gun reads (Code 128, EAN, UPC) → nothing here. That is a different symbology.
  • Showing a code someone else generated → an <img>, or Image Viewer if it needs to zoom.
  • Reading a code from a camera → nothing here. This encodes only.
  • More than about 2,900 bytes. No QR code exists past that, and the component says so. Put the payload behind a URL.

Do

  • Leave margin at 4. It is the standard's quiet zone. Trimming it is the most common reason a code will not scan.
  • Keep logoSize under 0.3, and leave level alone when there is a logo. The bump to H pays for the modules the logo covers.
  • Print at least 2cm across for a code held in the hand, more for one read from further away.
  • Test a styled code with a real phone before it ships. dot and low contrast are the two things that stop a code scanning.
  • Give it a label or caption when the value is not readable. The accessible name falls back to the raw payload.

Don't

  • Put a light code on a dark background because the theme is dark. Not every scanner reads inverted codes. Keep color dark and background light in both themes.
  • Raise level to H for its own sake. More correction means a denser code that must print bigger to scan.
  • Encode a long URL when a short one exists. Every character is modules.
  • Use mask or minVersion for looks. They keep a code's module count the same across a reprint.

Quick reference

variant
  • plain (default)
  • card
  • padded

API

QrCode

A QR code, drawn as vector, encoded here.

It is SVG, not a picture of one. One <path> for the data, one for the eyes, one for their pupils — so it is sharp at any size, prints at the printer's resolution rather than the screen's, takes a gradient or a theme colour like anything else on the page, and weighs a few kilobytes at version 40. A canvas QR is a blurry QR the moment anyone zooms, and a <rect>-per-module QR is thirty thousand elements.

The encoder is here too, so there is no dependency and no network call: numeric, alphanumeric and byte modes, versions 1 to 40, all four error-correction levels, and the smallest of the three modes chosen for whatever you pass. Text is UTF-8.

Styling is the point of the rest: shape and eyes change how the modules and the corners are drawn, gradient paints them, and logo puts a mark in the middle — which raises the correction level to H on its own, because a logo is damage and the code has to survive it.

bind:this gives you download(), toDataURL() and toBlob(), with the theme's colours resolved, so what is saved is what was on screen.

import { QrCode } from 'omaris'
<QrCode value="https://omaris.dev" /><QrCode value={qrWifi({ ssid: 'Cafe', password: 'hunter2' })} caption="Guest Wi-Fi" />

Parts

Every element this renders is reachable from outside. Pass Tailwind for one part as classes={{ part: '…' }}; class covers the root and is merged last, so it always wins.

root
No description in the source yet.
frame
The box the code sits in.
svg
No description in the source yet.
logo
The mark in the middle, when there is one.
caption
No description in the source yet.
error
Shown in place of the code when the text will not fit one.

Props

value required
string

What the code says. A URL, a phone number, a Wi-Fi payload, anything.

level
QrLevel

How much damage the code survives: L ~7%, M ~15%, Q ~25%, H ~30%. Defaults to M, or H when there is a logo — more correction means a denser code, so it is not free.

size

Defaults to 176

number | string

Rendered size. A number is px, a string any CSS length.

margin

Defaults to 4

number

The quiet zone, in modules. Four is the standard's minimum and what scanners are built for; less than that and some will not see the code at all.

shape

Defaults to 'square'

QrShape

How each module is drawn.

radius

Defaults to 0.35

number

Corner radius for shape="rounded", in modules — 0 to 0.5.

eyes

Defaults to 'square'

QrEyes

How the three corner markers are drawn.

color

Defaults to 'var(--color-foreground)'

string

The code's colour. Defaults to the theme's foreground.

background

Defaults to 'var(--color-background)'

string

Behind it. transparent leaves whatever is underneath.

eyeColor
string

The corner frames, if they should differ from the modules.

pupilColor
string

The pupils, if they should differ from the frames.

gradient
QrGradient

A gradient over the modules, in place of color.

logo
string

A mark in the middle. Any image URL, a data URI included.

logoSize

Defaults to 0.22

number

How much of the code's width the logo covers. Keep it under 0.3.

logoMargin

Defaults to 1

number

Clear space punched around the logo, in modules.

logoRadius

Defaults to 6

number | string

Rounding on the logo's backing plate — a number is px.

caption
string

Below the code.

variant

Defaults to 'plain'

QrCodeVariant
plain
The code and nothing else.
card
On a surface, with a quiet border — a code meant to be shown.
padded
Same, without the frame drawn: padding only, for printing.
label
string

The code's accessible name. Falls back to caption, then value.

minVersion
number

Force a version, 1–40, when a code has to keep its module count — printed on a template, say. Too small for the data is still refused.

mask
number

Force one of the eight mask patterns. By default the best is chosen.

children
Snippet

Replaces the caption line.

onerror
(error: Error) => void

Told when the value cannot be encoded — too long for a version 40 code.

class
string
classes
QrCodeClasses

Per-part Tailwind overrides. class still covers the root.