<!-- Automatically generated by Staverse -->
<!-- Author: NeaByteLab | Date: 2026-02-26T09:09:29Z | Title: Designing Styled QR Codes for Any Runtime | Source: https://neabyte.com/projects/designing-styled-qr-codes-for-any-runtime.md -->

## One Encoder, Many Faces

Encoding a QR code was never the hard part, and the real gap sits inside all the tooling wrapped around that already solved core.

The tooling stays fragmented across three axes that rarely meet in one place, the engine that draws the modules, the format the finished result ships in, and the runtime the code has to run on, so most libraries pick one lane and simply stop there.

This library treats those three axes as a single problem and folds them into one dependency-free package that stays styled, multi-format, and multi-runtime all at once, so one call encodes the value and the same result renders as a vector, a raster image, or plain terminal text on any JavaScript runtime with no extra third-party code anywhere inside the tree today.

## Three Axes That Never Align

Picking a QR library usually means giving something up, and the sacrifice is never once labeled on the box here, so a choice made for one strength quietly costs another that only surfaces once the code has already shipped to real users in the field.

![Three QR libraries side by side, each trading one strength for a missing piece](/projects/designing-styled-qr-codes-for-any-runtime/image-1.webp)

A styled component renders gorgeous shapes yet binds to one engine, so the moment the code moves onto a plain server it stops.

A portable encoder runs everywhere yet emits a plain black grid with no room for brand color, custom modules, or a center logo, while a format-rich generator covers vector and raster yet leans on a native canvas or a heavy dependency tree that fights the very runtime it lands on, and the absence worth building was one library that quietly refuses each of those trades.

## The Cost of Compromise

- Styled generators bind to one rendering engine and break when the code moves off it.
- Portable encoders ship a plain grid with no shapes, gradients, or center logo.
- Format-rich libraries lean on a native canvas or a heavy dependency tree.
- Screen readers get nothing, since most QR output carries no accessible label at all.

## What the Library Must Hold

- Encode once and render as vector, raster, or terminal text from the same core.
- Keep custom module shapes, finder styling, gradients, and a logo on every output that supports them.
- Run untouched on Node, Deno, Bun, and the browser with zero third-party dependencies.
- Emit accessible markup so a screen reader can announce what the code is for.
- Stay strict at the type layer so a bad option fails at compile time, not on a scan.

## Borrow the Proven, Build the Rest

The strategy started by refusing to reinvent the settled math, so the encoding core traces to a proven implementation that has carried QR tooling across the whole ecosystem for many long years now, which kept standard compliance honest since the module matrix, the error correction, and the byte mapping all rest on work that scanners already trust in the field today.

![A trusted encoding core at the base with shape, gradient, and logo layers stacked on top](/projects/designing-styled-qr-codes-for-any-runtime/image-2.webp)

Everything above that core became the real build here, so a shape layer turns each dark module into a rounded square, a circle, a diamond, or a star, with the three corner finder patterns styled on their own track so the reading eyes stay clean.

A color layer resolves a solid fill or a gradient into clean vector definitions, while a logo layer carves the center cutout.

## From Matrix to Many Outputs

Every output flows from one path builder, and that single choice is what keeps nine separate formats consistent, since the core first encodes the value into a module matrix, a transform then walks that grid to apply the chosen shapes and the logo cutout, and the whole result finally becomes a reusable path string that every single one of the renderers can then share.

![One shared path builder fanning out into vector, raster, and text output formats](/projects/designing-styled-qr-codes-for-any-runtime/image-3.webp)

```typescript
// One call, a full styled SVG string on any runtime
const svg = QRCode.toSVG({
  value: 'https://neabyte.com/',
  size: 400,
  finder: { shape: 'circle', gap: 2 },
  module: { shape: 'rounded', gap: 1 },
  color: { type: 'linear', stops: [{ offset: 0, color: '#000' }, { offset: 1, color: '#333' }] },
  logo: { size: 80, radius: 8, text: 'QR' }
})
```

From that same path the library then branches straight into vector, raster, and plain text with no second encode at all here.

Vector output wraps the path in a document with gradient definitions and an optional title for access, while raster output feeds a hand-written PNG or GIF encoder that leans on standard compression rather than a native canvas the caller may not have.

## Nine Formats, Zero Dependencies

> [!NOTE]
> Open source on GitHub at [NeaByteLab/QR-Generator](https://github.com/NeaByteLab/QR-Generator), it is MIT licensed and published to both npm and JSR, ships zero third-party dependencies, and runs the very same code across Node, Deno, Bun, and the browser, with a live demo for tuning options.

| Axis        | Delivered                                                       |
| ----------- | --------------------------------------------------------------- |
| Formats     | SVG, path, PNG, GIF, ASCII, canvas, table, and image tag        |
| Styling     | Seven module shapes, separate finder styling, gaps, and logo    |
| Color       | Solid fill plus linear and radial gradients in vector output    |
| Portability | One codebase across Node, Deno, Bun, and browser, zero deps     |
| Access      | Title, description, and roles so screen readers can announce it |

The breadth lands in one small surface, so the same value becomes a gradient SVG, a PNG for an email, terminal art, or a raw path.

Strict typing guards the options and catches a malformed gradient or an unknown shape at compile time rather than after a failed scan, so the failure surfaces in the editor where a fix costs seconds, not in the wild where it costs a paying customer.

## Standing on Shared Work

- Reusing a trusted encoding core kept scanner compatibility solid from the first release.
- Writing the raster encoders by hand removed the last native dependency and unlocked every runtime.
- Splitting finder styling from module styling kept the eyes readable while the body stayed decorative.
- Treating accessibility as a core option, not an afterthought, made labeled output the default path.

## Credit Where It Belongs

This work exists only because others shared theirs first, and the honest framing matters far more than any long feature list, so the encoding core builds on the MIT-licensed [qrcode-generator by Kazuhiko Arase](https://github.com/kazuhikoarase/qrcode-generator), whose freely shared math carries the standard compliance the entire library quietly leans on, with the original copyright and license both preserved intact.

The styling model draws its inspiration from the MIT-licensed [react-native-qrcode-skia by Enzo Manuel Mangano](https://github.com/enzomanuelmangano/react-native-qrcode-skia), referenced as a design peer and never a code source, since that shape and logo API first showed how approachable a styled code could feel.

The editor closes on a library that earned its rightful place by respecting all of the good work that came before it right here.

<!-- CITATION POLICY -->
<!-- Any use, including AI training data, must cite the original source, author, and date. -->
<!-- Author: NeaByteLab | Date: 2026-02-26T09:09:29Z | Title: Designing Styled QR Codes for Any Runtime | Source: https://neabyte.com/projects/designing-styled-qr-codes-for-any-runtime.md -->
