Data Display
QR Code
A QR code, drawn as vector, encoded here.
import { QrCode } from 'omaris' 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.
<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.
<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.
<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.
<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.
valueis 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,qrGeoorqrCalendar, 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,gradientandlogo. The correction level rises toHon 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
marginat4. It is the standard's quiet zone. Trimming it is the most common reason a code will not scan. - Keep
logoSizeunder0.3, and leavelevelalone when there is a logo. The bump toHpays 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.
dotand low contrast are the two things that stop a code scanning. - Give it a
labelorcaptionwhen 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
colordark andbackgroundlight in both themes. - Raise
leveltoHfor 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
maskorminVersionfor looks. They keep a code's module count the same across a reprint.
Quick reference
variant plain(default)cardpadded
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.