@acutenetwork/fey
v0.2.1
Published
Generate styled QR codes with custom dots, corners, gradients, and logos. Zero-dependency SVG output for Node.js, plus first-class React, React Native, and browser support.
Downloads
409
Maintainers
Readme
@acutenetwork/fey
Beautiful, customizable QR codes for every platform. Generate styled QR codes with custom shapes, colors, gradients, and embedded logos — in the browser, Node.js, React, and React Native.
Install
pnpm add @acutenetwork/feynpm install @acutenetwork/feyPlatform Entries
| Entry | Import | Environment |
|-------|--------|-------------|
| @acutenetwork/fey | Browser, vanilla JS | Browser (DOM) |
| @acutenetwork/fey/node | Node.js | Server (zero native deps) |
| @acutenetwork/fey/react | React component + hook | Browser (React 18+) |
| @acutenetwork/fey/native | React Native component | iOS / Android |
Browser / Vanilla JS
import { QRCode } from "@acutenetwork/fey";
const qr = new QRCode({
data: "https://example.com",
width: 256,
height: 256,
dotsOptions: {
type: "rounded",
color: "#4267b2",
},
backgroundOptions: {
color: "#ffffff",
},
});
// Render into a DOM element
qr.append(document.getElementById("qr-container")!);
// Export
const svg = await qr.getSVG();
const dataUri = await qr.getDataURI("png");
const blob = await qr.getBlob("png");
// Download to user's device
await qr.download("svg");
await qr.download({ name: "my-qr", extension: "png" });Browser API
class QRCode {
constructor(options?: Partial<Options>);
// Render
append(container: HTMLElement): void;
update(options?: Partial<Options>): void;
// Export
getSVG(): Promise<string>;
getDataURI(format?: FileExtension): Promise<string>;
getBlob(format?: FileExtension): Promise<Blob>;
getBuffer(format?: FileExtension): Promise<Buffer>;
// Download
download(format: FileExtension): Promise<void>;
download(options: { name?: string; extension?: FileExtension }): Promise<void>;
// Extension
applyExtension(fn: (svg: SVGElement, options: Options) => void): void;
deleteExtension(): void;
}Node.js
Zero native dependencies. No jsdom, no canvas — pure SVG generation.
import { QRCode } from "@acutenetwork/fey/node";
const qr = new QRCode({
data: "https://example.com",
width: 256,
height: 256,
dotsOptions: {
type: "rounded",
color: "#4267b2",
},
});
// Get SVG string
const svg = await qr.getSVG();
// Get as Buffer
const buffer = await qr.getBuffer("svg");
// Get as base64 data URI
const dataUri = await qr.getDataURI("svg");
// Write directly to filesystem
await qr.download({ name: "my-qr", extension: "svg" });Node API
class QRCode {
constructor(options?: Partial<Options> & { imageLoader?: ImageLoader });
update(options?: Partial<Options>): void;
getSVG(): Promise<string>;
getDataURI(format?: "svg"): Promise<string>;
getBuffer(format?: "svg"): Promise<Buffer>;
download(format?: FileExtension | { name?: string; extension?: FileExtension }): Promise<void>;
}Embedding images in Node.js
Pass a custom imageLoader to support center logos:
import { QRCode } from "@acutenetwork/fey/node";
import fs from "fs";
const qr = new QRCode({
data: "https://example.com",
image: "logo.png",
imageLoader: async (uri) => {
// Return width, height, and a data URI
const buffer = fs.readFileSync(uri as string);
const base64 = buffer.toString("base64");
return {
width: 100,
height: 100,
dataUri: `data:image/png;base64,${base64}`,
};
},
});
const svg = await qr.getSVG();React
pnpm add @acutenetwork/fey react react-domComponent
import { QRCode } from "@acutenetwork/fey/react";
function App() {
return (
<QRCode
data="https://example.com"
width={256}
height={256}
dotsOptions={{ type: "rounded", color: "#4267b2" }}
className="my-qr"
style={{ border: "1px solid #eee" }}
/>
);
}Hook
For more control (access to the underlying QRCode instance):
import { useQRCode } from "@acutenetwork/fey/react";
function App() {
const { ref, qrCode } = useQRCode({
data: "https://example.com",
width: 256,
height: 256,
dotsOptions: { type: "rounded", color: "#4267b2" },
});
const handleDownload = async () => {
const svg = await qrCode.current?.getSVG();
console.log(svg);
};
return (
<>
<div ref={ref} />
<button onClick={handleDownload}>Download SVG</button>
</>
);
}React API
// Component — props are Options + className + style
function QRCode(props: Partial<Options> & {
className?: string;
style?: React.CSSProperties;
}): JSX.Element;
// Hook — returns a ref and the QRCode instance
function useQRCode(options: Partial<Options>): {
ref: RefObject<HTMLDivElement>;
qrCode: RefObject<QRCodeInstance>;
};React Native
pnpm add @acutenetwork/fey react-native-svgimport { QRCode } from "@acutenetwork/fey/native";
function App() {
return (
<QRCode
data="https://example.com"
width={256}
height={256}
dotsOptions={{ type: "rounded", color: "#4267b2" }}
onReady={({ toDataURL }) => {
toDataURL().then((uri) => console.log(uri));
}}
/>
);
}React Native API
function QRCode(props: Partial<Options> & {
onReady?: (helpers: { toDataURL: () => Promise<string> }) => void;
}): React.ReactElement | null;Supports all styling options. Images can be local assets (require("./logo.png")) or remote URLs.
Options
All platforms share the same Options type:
type Options = {
// Content
data?: string;
image?: string | number;
// Dimensions
width?: number; // default: 300
height?: number; // default: 300
margin?: number; // default: 0
// Rendering
type?: "svg" | "canvas"; // default: "canvas" (browser only)
shape?: "square" | "circle"; // default: "square"
// QR encoding
qrOptions?: {
typeNumber?: 0-40; // 0 = auto, default: 0
mode?: "Numeric" | "Alphanumeric" | "Byte" | "Kanji";
errorCorrectionLevel?: "L" | "M" | "Q" | "H"; // default: "Q"
};
// Dot styling
dotsOptions?: {
type?: "square" | "dots" | "rounded" | "extra-rounded" | "classy" | "classy-rounded";
color?: string; // default: "#000"
gradient?: Gradient;
roundSize?: boolean; // default: true
};
// Corner square styling (the 3 large squares)
cornersSquareOptions?: {
type?: "dot" | "square" | "extra-rounded";
color?: string;
gradient?: Gradient;
};
// Corner dot styling (the inner dot of corner squares)
cornersDotOptions?: {
type?: "dot" | "square";
color?: string;
gradient?: Gradient;
};
// Background
backgroundOptions?: {
color?: string; // default: "#fff"
round?: number; // 0-1, corner roundness
gradient?: Gradient;
};
// Image overlay
imageOptions?: {
hideBackgroundDots?: boolean; // default: true
imageSize?: number; // 0-1, default: 0.4
crossOrigin?: string;
margin?: number; // default: 0
};
};Gradient
type Gradient = {
type: "linear" | "radial";
rotation?: number; // radians, for linear gradients
colorStops: {
offset: number; // 0-1
color: string;
}[];
};Example with gradients
const qr = new QRCode({
data: "https://example.com",
width: 300,
height: 300,
dotsOptions: {
type: "rounded",
gradient: {
type: "linear",
rotation: Math.PI / 4,
colorStops: [
{ offset: 0, color: "#8688B2" },
{ offset: 1, color: "#77779C" },
],
},
},
cornersSquareOptions: {
type: "extra-rounded",
color: "#5D5D8D",
},
cornersDotOptions: {
type: "dot",
color: "#5D5D8D",
},
backgroundOptions: {
color: "#E8E8F0",
},
});Dot Styles
| Type | Description |
|------|-------------|
| "square" | Sharp square dots (default) |
| "dots" | Circular dots |
| "rounded" | Rounded squares that connect to neighbors |
| "extra-rounded" | More aggressively rounded connections |
| "classy" | Rounded on opposite corners |
| "classy-rounded" | Classy with extra-rounded connections |
Corner Square Styles
| Type | Description |
|------|-------------|
| "square" | Sharp square frame |
| "dot" | Circular ring frame |
| "extra-rounded" | Rounded rectangle frame |
Corner Dot Styles
| Type | Description |
|------|-------------|
| "square" | Sharp square inner dot |
| "dot" | Circular inner dot |
Error Correction
| Level | Recovery | Best for |
|-------|----------|----------|
| "L" | ~7% | Maximum data density |
| "M" | ~15% | Balanced |
| "Q" | ~25% | Default, good with logos |
| "H" | ~30% | Large logos, harsh conditions |
Higher error correction allows larger image overlays but increases QR code density.
Exports
Each entry point re-exports all types and constants for convenience:
import {
QRCode,
// Types
type Options,
type FileExtension,
type DotType,
type CornerSquareType,
type CornerDotType,
type Gradient,
type DownloadOptions,
// Constants
dotTypes,
cornerDotTypes,
cornerSquareTypes,
errorCorrectionLevels,
errorCorrectionPercents,
drawTypes,
shapeTypes,
gradientTypes,
modes,
qrTypes,
} from "@acutenetwork/fey"; // or /node, /react, /nativeContributing
Setup
git clone https://github.com/acutenetwork/fey.git
cd fey
pnpm installScripts
| Command | Description |
|---------|-------------|
| pnpm build | Build all entry points (ESM + CJS + DTS) |
| pnpm typecheck | Run TypeScript type checking |
| pnpm lint | Run ESLint |
| pnpm format | Format with Prettier |
| pnpm test | Run tests |
| pnpm dev:vanilla | Start vanilla JS playground |
| pnpm dev:react | Start React playground |
| pnpm dev:node | Run Node.js playground |
Playgrounds
Local playgrounds for each platform live in playgrounds/ (gitignored). To set them up:
cd playgrounds/vanilla && pnpm install # Browser/Vanilla JS
cd playgrounds/react-app && pnpm install # React
cd playgrounds/node-app && pnpm install # Node.js
cd playgrounds/react-native-app && npm install # React Native (Expo)Pull requests
- Fork the repo and create a branch from
main - Make your changes and ensure
pnpm build && pnpm typecheck && pnpm lintall pass - Test your changes in the relevant playground(s)
- Submit a PR with a clear description of what changed and why
License
MIT
