Getting Started
Core Architecture
Link Engine
Analytics & Attribution
Partners & Affiliates
Third-Party Integrations
Identity & Security
Automation & Messaging
Developer Tools
The following files were used as context for generating this wiki page:
QR code generation powers high-engagement branding and physical-to-digital distribution across the platform by transforming short links into customizable, scannable matrix graphics. It bridges low-level error correction algorithms and vector rendering pipelines with user-facing interface controls, enabling both automated edge API generation and rich client-side design customization.
Sources: apps/web/lib/qr/codegen.ts:17-31, apps/web/app/api/qr/route.tsx:18-58, apps/web/ui/modals/qr-code-design-fields.tsx:55-79
Low-level QR code generation coordinates version selection, bitstream construction, byte packing, Reed-Solomon error correction, and matrix symbol layout. The engine handles inputs through high-level text or binary factories (QrCode.encodeText(), QrCode.encodeBinary()) and mid-level segment builders (QrCode.encodeSegments()) before instantiating the immutable symbol layout.
Sources: apps/web/lib/qr/codegen.ts:24-55
The encoding pipeline executes a precise multi-step procedure to determine symbol dimensions, pack payloads, and format raw codewords:
QrCode.encodeSegments() iterates through version numbers starting from minVersion (default 1) up to maxVersion (40).dataCapacityBits (QrCode.getNumDataCodewords(version, ecl) * 8) and compares it against QrSegment.getTotalBits(segs, version).boostEcl across error correction levels (LOW, MEDIUM, QUARTILE, HIGH) to upgrade correction strength if space permits without increasing version size.bb.dataCapacityBits with alternating pad bytes (0xec and 0x11).dataCodewords) and invokes the low-level constructor new QrCode(version, ecl, dataCodewords, mask).Sources: apps/web/lib/qr/codegen.ts:68-151
Note
When boostEcl is enabled, the engine actively promotes the error correction level if the data payload fits comfortably within the chosen version at a higher redundancy tier.
Constants and configuration mappings establish error correction lookup keys, canvas scale factors, and rendering defaults used across the QR pipeline.
Sources: apps/web/lib/qr/constants.ts:3-22
Warning
DEFAULT_IMG_SCALE uses a rough area estimate for maximum logo coverage. When integrating images, ensure dimensions do not exceed scannability limits despite fallback thresholds.
Sources: apps/web/lib/qr/constants.ts:18-22
Sources: apps/web/lib/qr/codegen.ts:17-31, apps/web/lib/qr/codegen.ts:68-75, apps/web/lib/qr/codegen.ts:87-101, apps/web/lib/qr/codegen.ts:164-167
The geometry and path construction subsystem transforms raw module arrays generated by the QR engine into optimized SVG paths, customized dot configurations, and structured finder patterns. Instead of rendering individual DOM nodes for every dark module — which scales poorly for high-density symbols like version 40 — the renderer constructs unified path strings using efficient string concatenation and geometric helpers.
Finder patterns (the 7×7 alignment and positioning markers located at the three corners of the matrix) are constructed using dedicated SVG path builders and compound geometries. The finderBorderPath helper generates outer boundary rings and inner cutouts based on the selected borderStyle.
Sources: apps/web/lib/qr/utils.tsx:343-403
Note
Finder border paths use the evenodd fill rule (fillRule="evenodd"), allowing the central ring gap to remain transparent without requiring a separate background-colored blocking rectangle. This prevents unwanted hard-cornered white squares from appearing under rounded or circular marker styles.
The layout engine determines the exact coordinates for the three standard finder patterns relative to the module matrix and margin offset:
cells.length.finderPositions) combining the module dimensions, quiet zone margin, and 7×7 finder dimensions:
{ x: margin, y: margin }{ x: numModules - 7 + margin, y: margin }{ x: margin, y: numModules - 7 + margin }getFinderPatternSVGString (or the FinderPattern component) supplying the computed effectiveMarkerColor, bgColor, borderStyle, and centerStyle.The canvas and SVG rendering pipelines handle the transformation of encoded module matrices into final, renderable visual outputs across raster HTML5 canvases and vector SVG documents. These pipelines manage device pixel ratios, image loading lifecycles, logo embedding, and module excavation to prevent obscured data codewords.
Sources: apps/web/lib/qr/index.tsx:315-399, apps/web/lib/qr/index.tsx:440-498, apps/web/lib/qr/api.tsx:13-85
The raster rendering pipeline builds HTML5 canvas elements and converts them into data representations or raw DOM nodes. The execution proceeds through the following phases:
getQRAsCanvas() extracts parameters from props, resolving defaults for size, level, bgColor, fgColor, margin, dotStyle, and markerColor.qrcodegen.QrCode.encodeText(value, ERROR_LEVEL_MAP[level]).getModules() to retrieve the raw boolean module grid, computes numCells including margins, and calls getImageSettings() to determine logo bounds.Image object with crossOrigin = "anonymous" and invokes waitUntilImageLoaded(image, imageSettings.src). If calculatedImageSettings.excavation is defined, it runs excavatesModules(cells, calculatedImageSettings.excavation).window.devicePixelRatio, sets canvas.height and canvas.width to size * pixelRatio, derives the scaling factor via (size / numCells) * pixelRatio, and applies it using ctx.scale(scale, scale).bgColor and loops through dot styles (rounded, extra-rounded, or default square paths via Path2D) to fill data modules while omitting finder pattern cells via isFinderPatternCell().ctx.drawImage() if haveImageToRender is true, and returns the canvas instance or a data URL depending on the getCanvas flag.Note
During extra-rounded dot rendering, the pipeline inspects adjacent cells in all four cardinal directions (top, right, bottom, left) using an isDark helper. Connected sides dynamically suppress corner rounding to ensure smooth, contiguous shapes across adjacent modules.
Sources: apps/web/lib/qr/index.tsx:507-533
SVG rendering pipelines optimize DOM node counts by consolidating dark modules into single vector paths rather than creating individual <rect> nodes per cell. For instance, Level 1 symbols reduce node counts from 441 to just 2 DOM nodes (background and foreground paths).
Sources: apps/web/lib/qr/index.tsx:315-360, apps/web/lib/qr/utils.tsx:454-531, apps/web/lib/qr/api.tsx:13-58
Warning
When isOGContext is true and image excavation is requested with low error correction levels (L or M), QRCodeSVG automatically upgrades the effective error correction level to Q to compensate for modules removed by logo placement.
Sources: apps/web/lib/qr/utils.tsx:473-476
The public QR code API endpoint runs at the /qr route on the edge runtime, providing dynamic QR image generation with built-in schema validation, rate limiting, and intelligent logo resolution. The endpoint accepts query parameters, executes validation checks, and returns Open Graph-compatible SVG image responses.
When an HTTP GET request hits the /qr route, the execution proceeds through a strict sequence of validation, rate enforcement, logo resolution, and rendering steps:
GET() extracts request URL search parameters and invokes getQRCodeQuerySchema.parse(getSearchParams(req.url)) to validate and coerce query inputs against schema defaults.ratelimitOrThrow(req, "qr") to enforce rate-limiting constraints on the incoming request.getQRCodeLogo({ url, logo, hideLogo }) to determine the correct branding asset for the target link.getShortLinkViaEdge(url.split("?")[0]) queries the edge store for short link data; if no short link matches, it immediately returns DUB_QR_LOGO.getWorkspaceViaEdge({ workspaceId: shortLink.projectId }) retrieves the associated project workspace. If the workspace plan is "free", it returns DUB_QR_LOGO.hideLogo is set to true, it returns null. If a custom logo string is passed in the query parameters, it returns that logo.isDubDomain(shortLink.domain)) and no workspace logo is configured, it falls back to DUB_QR_LOGO. Otherwise, it queries getDomainViaEdge(shortLink.domain) to check for a custom domain logo, falling back sequentially to workspace?.logo and finally DUB_QR_LOGO.GET(), it passes the resolved parameters and qrCodeLogo into QRCodeSVG() and wraps the resulting element in ImageResponse with CORS_HEADERS, setting response dimensions and headers before returning the final image.The query parameters accepted by the QR endpoint are validated using a Zod schema that enforces type coercion, defaults, and descriptive metadata for OpenAPI documentation generation.
Sources: apps/web/lib/zod/schemas/qr.ts:11-67
Warning
Passing custom logo parameters or setting hideLogo requires a paid workspace plan on Dub. Free-tier workspaces ignore custom logo overrides and automatically fall back to the default Dub QR logo (DUB_QR_LOGO).
Sources: apps/web/app/api/qr/route.tsx:76-92
Sources: apps/web/app/api/qr/route.tsx:11-16, apps/web/app/api/qr/route.tsx:106-111, apps/web/lib/zod/schemas/qr.ts:19-25
Dub provides interactive client-side customization modals that allow users to visually configure QR code designs in real time, persist their preferences to local storage, and export finalized graphics in multiple formats. The customization interface spans across link-specific modals (LinkQRModal), partner link modals (PartnerLinkQRModal), and inline link builder components (QRCodePreview), all powered by the shared form body component QRCodeDesignFields.
Sources: apps/web/ui/modals/qr-code-design-fields.tsx:55-79, apps/web/ui/modals/link-qr-modal.tsx:54-107, apps/web/ui/modals/partner-link-qr-modal.tsx:40-81, apps/web/ui/links/link-builder/qr-code-preview.tsx:23-76
The customization interface exposes granular styling controls for dot shapes, finder marker centers, finder marker borders, foreground colors, and logo visibility. Workspace-specific design states are synchronized with local storage using custom persistence hooks prefixed by workspace identifiers.
Sources: apps/web/ui/modals/qr-code-design-fields.tsx:34-51, apps/web/ui/modals/qr-code-design-fields.tsx:165-250, apps/web/ui/modals/link-qr-modal.tsx:79-85
Note
Pro-tier restrictions apply to logo visibility controls. Non-pro workspaces have the logo toggle disabled, forcing the UI to fall back to the default Dub QR logo (DUB_QR_LOGO) without allowing custom hide toggles.
Live preview rendering combines React state management with debounced input handlers and animated transitions. Color inputs use useDebouncedCallback with a 500ms delay to prevent excessive re-rendering and matrix recalculation while users manipulate hex color pickers.
// Call-chain execution for updating QR design state and preview rendering:
// setData() -> state mutation -> previewKey re-calculation -> AnimatePresence transition -> <QRCode /> re-evaluation
const onColorChange = useDebouncedCallback(
(color: string) => setData((d) => ({ ...d, fgColor: color })),
500,
);
const previewKey = `${data.fgColor}-${data.hideLogo}-${data.dotStyle}-${data.markerCenterStyle}-${data.markerBorderStyle}-${data.markerColor ?? ""}`;Sources: apps/web/ui/modals/qr-code-design-fields.tsx:83-94, apps/web/ui/modals/qr-code-design-fields.tsx:130-152
Tip
The preview container wraps the <QRCode /> component inside AnimatePresence with a dynamic previewKey string. Any modification to dot styles, colors, or logo visibility triggers a smooth 100ms fade-and-blur transition.
The preview header features dedicated download and copy action popovers (DownloadPopover and CopyPopover) supplied with precomputed qrData objects. These utilities interface with canvas and SVG export helpers to generate downloadable raster or vector assets as well as clipboard-ready payloads.
Sources: apps/web/ui/modals/qr-code-design-fields.tsx:1-2, apps/web/ui/modals/qr-code-design-fields.tsx:106-123
Sources: apps/web/ui/modals/qr-code-design-fields.tsx:83-91, apps/web/ui/modals/link-qr-modal.tsx:79-82, apps/web/ui/shared/qr-code.tsx:30-54