react-native-zywell-thermal-printer
v2.0.0
Published
Native bridge for Zywell Thermal printer in React Native applications
Downloads
44
Maintainers
Keywords
Readme
🖨️ react-native-zywell-thermal-printer
Native bridge for Zywell thermal printers — text, image, barcode, QR code, and TSPL label printing over NET, Bluetooth, and USB, on both iOS and Android
🎬 Demo
📑 Contents
- ✨ Features
- 📦 Install
- 📋 Requirements
- 🍎 iOS setup
- 🚀 Quick start
- 🏃 Running the example app
- 📚 API reference
- 🔀 Capability matrix
- 🔄 Migrating from 0.x/1.x
- 🤝 Contributing
✨ Features
- 🖨️ Class-based
ZywellPrinterAPI — one surface for NET, BLUETOOTH, and USB - 🧾 Full print surface on both platforms — text, image, barcode, QR code, and TSPL label printing on both iOS and Android
- 🔍 Device discovery — Bluetooth and USB scanning built in
- ⚡ JSI-backed, type-safe bindings via Nitro Modules (New Architecture only, no legacy bridge)
📦 Install
npm install react-native-zywell-thermal-printer react-native-nitro-modules
cd ios && pod installUpgrading from the old package?
2.0.0is a breaking, New Architecture-only rewrite.npm/yarnnever force-upgrade a pinned dependency, so an existing^1.xinstall keeps working — pin it explicitly (^1.1.1) if you're not ready to move. See the migration guide for what changed.
📋 Requirements
react-native >=0.82.0, New Architecture enabled (this library has no legacy-bridge fallback)react-native-nitro-modulesinstalled alongside this package (peer dependency)
If you can't enable the New Architecture yet, stay on the 1.x line (npm install react-native-zywell-thermal-printer@^1.1.1).
🍎 iOS setup
This library's Swift code depends on CocoaAsyncSocket, which doesn't define Swift modules itself. Add this to your ios/Podfile, inside your app target, before use_native_modules!:
pod 'CocoaAsyncSocket', :modular_headers => trueWithout it, pod install fails with ... depends upon CocoaAsyncSocket, which does not define modules. See example/ios/Podfile for a working reference. (Android needs no extra setup beyond your app's own Bluetooth/network permissions.)
🚀 Quick start
import { ZywellPrinter } from 'react-native-zywell-thermal-printer';
const printer = new ZywellPrinter({ type: 'NET', address: '192.168.0.43' });
await printer.connect();
await printer.printText('Hello from Zywell!', { align: 'center', bold: true });
await printer.cutPaper();
await printer.disconnect();For Bluetooth or USB, change type/address:
new ZywellPrinter({ type: 'BLUETOOTH', address: '00:11:22:33:FF:EE' });
new ZywellPrinter({ type: 'USB', address: usbDevicePath }); // Android only🏃 Running the example app
The example/ app (screenshots above) exercises every connection type and print method.
git clone https://github.com/lehoi2195/react-native-zywell-thermal-printer
cd react-native-zywell-thermal-printer
yarn bootstrap # installs root + example deps, runs `pod install`
yarn example start # Metro, in one terminal
yarn example android # or: yarn example iosYou'll need a real or emulated device with Android/iOS toolchains set up as usual for React Native. To print for real, edit the NET address (or use Scan Bluetooth/Scan USB) to point at your printer.
📚 API reference
new ZywellPrinter(config: PrinterConfig)
Creates a printer instance. Call connect() before using it.
interface PrinterConfig {
type: 'NET' | 'BLUETOOTH' | 'USB';
/** IP (NET) / MAC or peripheral UUID (BLUETOOTH) / device path (USB, Android only) */
address: string;
/** NET only, default 9100. Android hardcodes 9100 regardless of this value (vendor SDK limitation); iOS honors it. */
port?: number;
/** Selects the command-builder for 58mm vs 80mm printers (Android only). 58mm doesn't support printQRCode() or CODE128/CODE93 barcodes. Default 80. */
paperWidthMm?: 58 | 80;
}printer.connect(): Promise<void> / disconnect(): Promise<void> / isConnected(): Promise<boolean>
Open/close the connection and check its state.
printer.printText(text: string, options?: TextPrintOptions): Promise<void>
interface TextPrintOptions {
fontSize?: number; // omit for the printer's native size
align?: 'left' | 'center' | 'right'; // default 'left'
bold?: boolean; // default false
underline?: boolean; // default false
}printer.printImage(base64: string, options?: ImagePrintOptions): Promise<void>
interface ImagePrintOptions {
width?: number; // target width in px, aspect-preserved; omit for native width
threshold?: number; // grayscale threshold 0-255 — accepted but currently unused on both platforms
}printer.printBarcode(content: string, options: BarcodeOptions): Promise<void>
options.type is required. Not supported on 58mm paper for CODE128/CODE93.
type BarcodeType = 'CODE128' | 'CODE39' | 'EAN13' | 'EAN8' | 'UPC_A' | 'UPC_E' | 'CODABAR' | 'ITF' | 'CODE93';
interface BarcodeOptions {
type: BarcodeType;
width?: number; // bar width multiplier — Android 80mm-path only
height?: number; // Android 80mm default 162; iOS uses the SDK default
showText?: boolean; // human-readable text under the barcode — Android 80mm-path only
}printer.printQRCode(content: string, options?: QRCodeOptions): Promise<void>
Not supported on 58mm paper (rejects on Android).
interface QRCodeOptions {
size?: number; // module size, default 3
errorCorrectionLevel?: 'L' | 'M' | 'Q' | 'H'; // default 'M' — higher recovers from more damage
}printer.printLabel(options: LabelOptions): Promise<void>
Prints a TSPL label (distinct from the ESC/POS commands the other print* methods use) from positioned elements.
interface LabelOptions {
widthMm: number;
heightMm: number;
gapMm?: number; // gap-sensor stock spacing, default 2.0
elements: LabelElement[]; // TextLabelElement | BarcodeLabelElement | QRCodeLabelElement | BoxLabelElement | LineLabelElement
}Every element has kind, x, y (dot coordinates, origin top-left). kind is typed as the full union on every variant, so TypeScript won't auto-narrow it — check codeType/content/width to distinguish variants at runtime.
interface TextLabelElement {
kind: LabelElementKind; // 'text'
x: number; y: number;
content: string;
font?: number; // accepted but ignored on both platforms — vendor font is hardcoded
rotation?: number; // degrees, default 0
}
interface BarcodeLabelElement {
kind: LabelElementKind; // 'barcode'
x: number; y: number;
content: string;
codeType: BarcodeType;
height: number;
humanReadable?: boolean; // default true
}
interface QRCodeLabelElement {
kind: LabelElementKind; // 'qrcode'
x: number; y: number;
content: string;
cellWidth?: number; // default 4
eccLevel?: QRErrorCorrectionLevel; // default 'M'
}
interface BoxLabelElement {
kind: LabelElementKind; // 'box'
x: number; y: number;
width: number; height: number;
thickness?: number; // outline thickness in dots, default 1
}
interface LineLabelElement {
kind: LabelElementKind; // 'line'
x: number; y: number;
width: number; height: number;
// filled rectangle (TSPL BAR), not outlined — no separate thickness concept
}await printer.printLabel({
widthMm: 40,
heightMm: 30,
elements: [
{ kind: 'text', x: 10, y: 10, content: 'Sample Label' },
{ kind: 'qrcode', x: 10, y: 40, content: 'https://example.com' },
],
});printer.cutPaper(): Promise<void> / printer.clearBuffer(): Promise<void>
Cut the paper (requires a physical cutter) / clear the print buffer.
ZywellPrinter.scanBluetooth(timeoutMs?: number): Promise<BluetoothDeviceInfo[]>
Static method, default timeout 5000ms. Returns { address, name?, rssi? }[].
ZywellPrinter.scanUSB(): Promise<USBDeviceInfo[]>
Static method, Android only. Returns { address, name? }[].
PrinterNotSupportedOnPlatformError
Thrown when a feature isn't supported on the current platform/connection (e.g. scanUSB() on iOS).
import { PrinterNotSupportedOnPlatformError } from 'react-native-zywell-thermal-printer';
try {
await ZywellPrinter.scanUSB();
} catch (error) {
if (error instanceof PrinterNotSupportedOnPlatformError) console.warn(error.message);
}🔀 Capability matrix
| Feature | NET | BLUETOOTH | USB | | --- | --- | --- | --- | | connect / disconnect / isConnected | ✅ both | ✅ both (Classic on Android, BLE on iOS) | ✅ Android only | | printText / printImage / printBarcode / printQRCode / printLabel | ✅ both | ✅ both | ✅ Android | | cutPaper | Depends on physical printer | Depends on physical printer | Depends on physical printer | | scanBluetooth() | n/a | ✅ both | n/a | | scanUSB() | n/a | n/a | ✅ Android only |
Notes:
- NET
portis Android-inert — the vendor SDK hardcodes port 9100 there; only iOS honors a custom value. - iOS allows one active BLE connection at a time — the vendor BLE manager is a process-wide singleton. NET connections are independent per instance on both platforms.
- A few print options are best-effort per platform (e.g.
BarcodeOptions.width/showTexton iOS,ImagePrintOptions.thresholdon both) — these only affect output fine-tuning, not whether the feature works.
🔄 Migrating from 0.x/1.x
2.0.0 is a full rewrite with a breaking, class-based API — there's no compatibility shim.
| Old (0.1.0 – 1.1.1) | New (2.0.0+) |
| --- | --- |
| import ZywellPrinter from '...' (default export) | import { ZywellPrinter } from '...' (named, class) |
| ZywellPrinter.connectNet(ip) | new ZywellPrinter({ type: 'NET', address: ip }).connect() |
| connectBLE(address) | new ZywellPrinter({ type: 'BLUETOOTH', address }).connect() |
| disconnectNet(ip) / disconnectAddress(...) / disconnectPort(...) | printer.disconnect() |
| printPic(address, imagePath, opts, type) | printer.printImage(base64, options) |
| clearBuffer(address, type) | printer.clearBuffer() |
| Undocumented Android-only native calls for barcode/QR/label | printer.printBarcode() / printQRCode() / printLabel() — documented, on both platforms |
| No discovery API | ZywellPrinter.scanBluetooth(), ZywellPrinter.scanUSB() |
You'll also need to add react-native-nitro-modules and enable the New Architecture — see Requirements.
🤝 Contributing
Contributions, bug reports, and feature requests are welcome — see CONTRIBUTING.md.
📄 License
MIT — made with create-react-native-library
