All components
QR Code
Scannable code frames with a centered logo overlay, sized from 2xs to 2xl.
1234567891011121314151617181920import { QrCode } from "#components/ui/base-ui/qr-code.tsx";
export function Example() { return ( // Base UI ships no QR machine — Frame is an <svg> (viewBox 0 0 29 29) // whose children are the module paths: feed it server-generated QR // markup to make it scannable (a decorative grid works for mockups); // modules fill with currentColor and every part composes via `render` <QrCode.Root size="md"> <QrCode.Frame aria-label="QR code"> <path fillRule="evenodd" d="M0 0h7v7H0V0Zm1 1v5h5V1H1Z" /> <path d="M2 2h3v3H2z" /> {/* …finder squares and data modules… */} </QrCode.Frame> <QrCode.Overlay> <Logo /> </QrCode.Overlay> </QrCode.Root> );}Sizes
2xs
xs
sm
md
lg
xl
2xl
Install
1npx stylexblocks add qr-codeSource
components/ui/base-ui/qr-code.tsx
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162import * as stylex from "@stylexjs/stylex";import { mergeProps } from "@base-ui-components/react/merge-props";import { useRender } from "@base-ui-components/react/use-render";import type { StyleOverrideProps } from "#lib/recipe-props.ts";import { semanticColors } from '@stylexblocks/ui/semantic-tokens/colors.stylex.ts'import { semanticRadii } from '@stylexblocks/ui/semantic-tokens/radii.stylex.ts'import { spacing } from '@stylexblocks/ui/tokens/spacing.stylex.ts'
const qrCodeRoot = stylex.create({ base: { position: 'relative', width: 'fit-content', '--qr-code-overlay-size': 'calc(var(--qr-code-size) / 3)', },})
const qrCodeRootSizes = stylex.create({ '2xs': { '--qr-code-size': '40px', }, xs: { '--qr-code-size': '64px', }, sm: { '--qr-code-size': '80px', }, md: { '--qr-code-size': '120px', }, lg: { '--qr-code-size': '160px', }, xl: { '--qr-code-size': '200px', }, '2xl': { '--qr-code-size': '240px', }, full: { '--qr-code-size': '100%', },})
const qrCodeFrame = stylex.create({ base: { width: 'var(--qr-code-size)', height: 'var(--qr-code-size)', fill: 'currentColor', },})
const qrCodeOverlay = stylex.create({ base: { display: 'flex', alignItems: 'center', justifyContent: 'center', width: 'var(--qr-code-overlay-size)', height: 'var(--qr-code-overlay-size)', padding: spacing._1, backgroundColor: semanticColors.bg, borderRadius: semanticRadii.indicatorSm, },})
export type QrCodeSize = keyof typeof qrCodeRootSizes
// The machine-less variant positions the overlay itself (Ark's machine does// this inline); the recipe supplies the box, background, and radius.const qrCodeLocal = stylex.create({ overlay: { position: "absolute", top: "50%", left: "50%", translate: "-50% -50%", },});
/** * QrCode — Base UI ships no QR machine, so the recipe is composed through * Base UI's render primitives (`useRender` + `mergeProps`). Frame renders an * `<svg>` whose children are the module `<path>`s — feed it * server-generated QR markup to make it scannable (a decorative placeholder * grid works for mockups); modules fill with `currentColor`. Only the root * consumes the size axis, so `size` is a plain prop on Root. Every part * composes via `render`. */// recipe maps hold stylex.props-compatible values by construction; one cast// bridges StyleX's variadic genericsfunction sx( styles: ReadonlyArray<unknown>, palette: StyleOverrideProps["palette"], style: StyleOverrideProps["style"],) { return stylex.props( ...([palette, ...styles, style] as unknown as ReadonlyArray<never>), );}
export type QrCodeRootProps = Omit<useRender.ComponentProps<"div">, "style"> & StyleOverrideProps & { size?: QrCodeSize; };
function Root(allProps: QrCodeRootProps) { const { size = "md", render, palette, style, ...rest } = allProps as QrCodeRootProps & Record<string, unknown>; return useRender({ defaultTagName: "div", render: render as useRender.RenderProp, state: { size }, props: mergeProps<"div">( { ...sx([qrCodeRoot.base, qrCodeRootSizes[size]], palette as never, style as never), }, rest, ), });}Root.displayName = "QrCodeRoot";
function Frame(allProps: Omit<useRender.ComponentProps<"svg">, "style"> & StyleOverrideProps) { const { render, palette, style, ...rest } = allProps as Record<string, unknown>; return useRender({ defaultTagName: "svg", render: render as useRender.RenderProp, props: mergeProps<"svg">( { viewBox: "0 0 29 29", role: "img", ...sx([qrCodeFrame.base], palette as never, style as never), }, rest, ), });}Frame.displayName = "QrCodeFrame";
function Overlay(allProps: Omit<useRender.ComponentProps<"div">, "style"> & StyleOverrideProps) { const { render, palette, style, ...rest } = allProps as Record<string, unknown>; return useRender({ defaultTagName: "div", render: render as useRender.RenderProp, props: mergeProps<"div">( { ...sx( [qrCodeOverlay.base, qrCodeLocal.overlay], palette as never, style as never, ), }, rest, ), });}Overlay.displayName = "QrCodeOverlay";
export const QrCode = { Root, Frame, Overlay,};