npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-niimblue-lib

v1.2.1

Published

React Native library for BLE printing with NIIMBOT devices

Downloads

324

Readme

React Native NiimBlue Library

A React Native library for Bluetooth LE printing with NIIMBOT thermal printers. Features automatic printer model detection, rich content rendering (text, QR codes, barcodes, images), and comprehensive print control.

✨ Features

  • 🔍 Auto-detect printer models - Automatically selects the correct print task based on connected printer
  • 📱 BLE Communication - Direct Bluetooth Low Energy connection to NIIMBOT printers
  • 🎨 Rich Content Support:
    • Text rendering with Skia (custom fonts, alignment, scaling)
    • QR codes with error correction levels
    • Barcodes (EAN13, CODE128)
    • Images from buffers or URIs (PNG, JPG, BMP)
    • Lines and custom pixel data
  • 🖼️ Print Preview - Generate base64 PNG previews before printing
  • 📏 Flexible Layout - Pixel-perfect positioning with alignment options
  • 🔄 Multiple Printer Support - B1, B21, D110, D11 and more

📦 Installation

npm install react-native-niimblue-lib

Install Required Peer Dependencies

Always required for Bluetooth connectivity:

npm install react-native-ble-plx

Optional - only needed if you use PrintPage.addText() for text rendering:

npm install @shopify/react-native-skia

Note: If you only print QR codes, barcodes, or images without text, you don't need Skia.

Alternative: Instead of using Skia, you can render text to image/pixel data using any method (Canvas, SVG, native views, etc.) and add it via addPixelData() or addImageFromBuffer().

Important: These dependencies have native code and require proper linking/building. They cannot work as transitive dependencies.

Expo Setup

If using Expo, add the BLE plugin to your app.json:

{
  "expo": {
    "plugins": [
      [
        "react-native-ble-plx",
        {
          "isBackgroundEnabled": true,
          "modes": ["peripheral", "central"],
          "bluetoothAlwaysPermission": "App needs Bluetooth to connect to NIIMBOT printers"
        }
      ]
    ]
  }
}

Then create a development build:

npx expo prebuild
npx expo run:ios
# or
npx expo run:android

Note: If you use PrintPage.addText(), Skia requires a development build and does NOT work in Expo Go. However, QR codes, barcodes, and images work fine without Skia.

iOS Setup

  1. Install CocoaPods dependencies:
cd ios && pod install
  1. Add to Info.plist:
<key>NSBluetoothAlwaysUsageDescription</key>
<string>App needs Bluetooth to connect to NIIMBOT printers</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Required for Bluetooth device scanning</string>
  1. Note: If you installed @shopify/react-native-skia for text rendering, run pod install again after installation.

Android Setup

Add permissions to android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" 
                 android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.INTERNET" />

🚀 Quick Start

Basic Printing

import { NiimbotBluetoothClient, PrintPage } from 'react-native-niimblue-lib';

// 1. Connect to printer
const client = new NiimbotBluetoothClient();
await client.connect(); // Auto-scans and connects to first NIIMBOT device

// 2. Create print task (auto-detects printer model)
client.stopHeartbeat();
client.setPacketInterval(0); // Fast printing
const task = client.createPrintTask({
  totalPages: 1,
  density: 3,
  labelType: 1,
});

if (!task) {
  throw new Error('Printer model not detected');
}

// 3. Build page content
const page = new PrintPage(400, 240); // width x height in pixels

page.addText('Hello NIIMBOT!', {
  x: 192,
  y: 100,
  fontSize: 24,
  align: 'center',
  vAlign: 'middle',
});

// 4. Print
await task.printInit();
await task.printPage(page.toEncodedImage(), 1);
await task.waitForFinished();
client.startHeartbeat();

📖 Usage Examples

Page Orientation

Use orientation parameter to automatically rotate all content for landscape printing:

// Portrait mode (default) - for vertical labels
const portraitPage = new PrintPage(400, 240, 'portrait');
portraitPage.addText('Product Name', { x: 200, y: 120 });

// Landscape mode - for horizontal labels on vertical paper
// Dimensions are AUTO-SWAPPED: 240x400 becomes 400x240 canvas + 90° rotation
const landscapePage = new PrintPage(240, 400, 'landscape');
landscapePage.addText('Product Name', { 
  x: 200, // Same coordinates work!
  y: 120, // Canvas is 400x240 (swapped) + rotated 90°
});

How it works:

  • Physical paper: 240px width × 400px height (vertical)
  • new PrintPage(240, 400, 'landscape'):
    • ✅ Canvas dimensions: 400×240 (auto-swapped)
    • ✅ Content rotated: 90° clockwise
    • ✅ Result: Horizontal content on vertical paper
    • ✅ Same coordinates as new PrintPage(400, 240, 'portrait')

Text Rendering

Option 1: Using Skia (requires @shopify/react-native-skia)

const page = new PrintPage(400, 240);

// Simple text with default font
page.addText('NIIMBOT PRINTER', {
  x: 192,
  y: 50,
  fontSize: 24,
  align: 'center',
  vAlign: 'middle',
});

// Bold text with manual rotation
import { Skia, FontStyle } from '@shopify/react-native-skia';
page.addText('Rotated Text', {
  x: 192,
  y: 100,
  fontSize: 20,
  fontStyle: FontStyle.Bold,
  align: 'center',
  vAlign: 'middle',
  rotate: 45, // Additional rotation (on top of page orientation)
});

// Custom font style
page.addText('Heavy Text', {
  x: 192,
  y: 140,
  fontSize: 18,
  fontStyle: Skia.FontStyle({ weight: 800 }), // Custom weight
  align: 'center',
  vAlign: 'middle',
  rotate: -15, // Rotate 15 degrees counter-clockwise
});

// Custom font (user loads Typeface separately)
const fontData = await Skia.Data.fromURI('file://path/to/font.ttf');
const customTypeface = Skia.Typeface.MakeFreeTypeFaceFromData(fontData);

page.addText('Custom Font Text', {
  x: 100,
  y: 180,
  fontSize: 16,
  typeface: customTypeface, // Optional
  align: 'left',
});

Option 2: Without Skia - Convert text to image first

// Example: Using react-native-view-shot to capture text as image
import { captureRef } from 'react-native-view-shot';

// 1. Render text in a hidden View with ref
const textRef = useRef();

// 2. Capture as base64
const uri = await captureRef(textRef, {
  format: 'png',
  quality: 1,
});

// 3. Convert to buffer and add to page
const response = await fetch(uri);
const arrayBuffer = await response.arrayBuffer();
const buffer = new Uint8Array(arrayBuffer);

page.addImageFromBuffer({
  buffer,
  x: 192,
  y: 100,
  align: 'center',
  vAlign: 'middle',
});

// Or use any other method: Canvas, SVG to PNG, native rendering, etc.

QR Code

page.addQR('https://github.com', {
  x: 192,
  y: 100,
  width: 150,
  height: 150,
  align: 'center',
  vAlign: 'middle',
  ecl: 'M', // Error correction: L, M, Q, H
  rotate: 90, // Optional: rotate 90 degrees
});

Barcode

page.addBarcode('123456789012', {
  encoding: 'EAN13', // or 'CODE128'
  x: 192,
  y: 150,
  width: 200,
  height: 60,
  align: 'center',
  vAlign: 'middle',
  rotate: 180, // Optional: rotate 180 degrees
});

Image from Buffer (PNG/JPG/BMP)

const imageBuffer = await fetch('https://example.com/image.jpg')
  .then(res => res.arrayBuffer())
  .then(buf => new Uint8Array(buf));

page.addImageFromBuffer({
  buffer: imageBuffer,
  x: 192,
  y: 100,
  width: 200,
  height: 150,
  align: 'center',
  vAlign: 'middle',
  threshold: 128, // Grayscale to binary threshold
  rotate: 270, // Optional: rotate 270 degrees
});

Image from URI

await page.addImageFromUri('https://example.com/logo.png', {
  x: 192,
  y: 100,
  width: 150,
  height: 150,
  align: 'center',
  vAlign: 'middle',
  threshold: 128,
});

Custom Pixel Data

const heartPixels = [
  0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,
  0,0,1,1,1,1,1,0,0,1,1,1,1,0,0,0,
  0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
  // ... (1 = black, 0 = white)
];

page.addPixelData({
  data: heartPixels,
  imageWidth: 16,
  imageHeight: 11,
  x: 192,
  y: 100,
  width: 128, // Scaled width
  height: 88,
  align: 'center',
  vAlign: 'middle',
  rotate: 45, // Optional: rotate 45 degrees
});

Line Drawing

page.addLine({
  x: 10,
  y: 100,
  endX: 374,
  endY: 100,
  thickness: 2,
});

Print Preview

const page = new PrintPage(400, 240);
page.addQR('Preview Test', { x: 192, y: 100, align: 'center', vAlign: 'middle' });

// Generate base64 PNG
const base64Uri = await page.toPreviewImage();
// Display in <Image source={{ uri: base64Uri }} />

🔧 API Reference

NiimbotBluetoothClient

Methods

  • connect(device?: Device): Connect to printer (auto-scan or specific device)
  • disconnect(): Disconnect from printer
  • listDevices(scanDurationMs: number): Scan for available printers
  • listConnectedDevices(): List already connected devices
  • createPrintTask(options: PrintOptions): Create print task with auto-detection
  • setPacketInterval(ms: number): Set delay between packets (0 = fastest)
  • startHeartbeat() / stopHeartbeat(): Control heartbeat

PrintPage

Constructor

new PrintPage(width: number, height: number, orientation?: PageOrientation)
  • width: number - Page width in pixels
  • height: number - Page height in pixels
  • orientation?: 'portrait' | 'landscape' - Page orientation (default: 'portrait')
    • 'portrait': Normal orientation, canvas dimensions = (width, height)
    • 'landscape': Auto-swaps dimensions → canvas becomes (height, width) + rotates all content 90° clockwise
      • Perfect for printing horizontal content on vertical paper
      • Example: new PrintPage(240, 400, 'landscape') → 400×240 canvas with 90° rotation
      • Use same coordinates as new PrintPage(400, 240, 'portrait')

Methods

  • addText(text: string, options: TextOptions): void
  • addQR(text: string, options: QROptions): void
  • addBarcode(text: string, options: BarcodeOptions): void
  • addPixelData(options: ImageOptions): void
  • addImageFromBuffer(options: ImageFromBufferOptions): void
  • addImageFromUri(uri: string, options: ImageFromBufferOptions): Promise<void>
  • addLine(options: LineOptions): void
  • toEncodedImage(): EncodedImage - Convert to printer format
  • toPreviewImage(): Promise<string> - Generate base64 PNG preview

Type Definitions

PageOrientation

type PageOrientation = 'portrait' | 'landscape';

PrintOptions

  • totalPages?: number - Number of pages to print
  • density?: number - Print density (1-5, default: 3, higher = darker)
  • labelType?: number - Label type identifier (printer-specific)
  • statusPollIntervalMs?: number - Status polling interval in ms (default: 100)
  • statusTimeoutMs?: number - Status check timeout in ms (default: 8000)
  • pageTimeoutMs?: number - Timeout for printing each page in ms

PrintElementOptions (Base)

All positioning options support:

  • x: number - X coordinate in pixels
  • y: number - Y coordinate in pixels
  • width?: number - Optional width (auto-scales if only one dimension provided)
  • height?: number - Optional height (auto-scales if only one dimension provided)
  • align?: 'left' | 'center' | 'right' - Horizontal alignment relative to x coordinate
  • vAlign?: 'top' | 'middle' | 'bottom' - Vertical alignment relative to y coordinate
  • rotate?: number - Rotation angle in degrees (0-360), rotates around element center. This is additional rotation on top of page orientation.

TextOptions

Extends PrintElementOptions with:

  • fontSize?: number - Font size in pixels (default: 12)
  • typeface?: SkTypeface - Custom Skia typeface (optional, uses system font if not provided)
  • fontStyle?: FontStyle - Font style for weight, width, and slant (optional, uses Normal if not provided)
    • Use Skia.FontStyle() to create custom styles with weight (100-900), width, and slant
    • Common presets: FontStyle.Normal, FontStyle.Bold, FontStyle.Italic, FontStyle.BoldItalic

QROptions

Extends PrintElementOptions with:

  • ecl?: 'L' | 'M' | 'Q' | 'H' - Error correction level (default: 'M')
    • L: Low (~7% correction)
    • M: Medium (~15% correction)
    • Q: Quartile (~25% correction)
    • H: High (~30% correction)

BarcodeOptions

Extends PrintElementOptions with:

  • encoding?: 'EAN13' | 'CODE128' - Barcode encoding format (default: 'EAN13')

ImageOptions

Extends PrintElementOptions with:

  • data: number[] - 1D array of pixel data (1 = black, 0 = white)
  • imageWidth: number - Original image width in pixels
  • imageHeight: number - Original image height in pixels

ImageFromBufferOptions

Extends PrintElementOptions with:

  • buffer: Uint8Array - Image file buffer (supports PNG/JPG/BMP formats)
  • threshold?: number - Grayscale to binary conversion threshold (0-255, default: 128, lower = darker)
  • Plus all PrintElementOptions (x, y, width, height, align, vAlign, rotate)

LineOptions

  • x: number - Start X coordinate in pixels
  • y: number - Start Y coordinate in pixels
  • endX: number - End X coordinate in pixels
  • endY: number - End Y coordinate in pixels
  • thickness?: number - Line thickness in pixels (default: 1)

🎯 Alignment System

The library uses reference point alignment:

// Center text at position (192, 100)
page.addText('Centered', {
  x: 192,    // Reference X
  y: 100,    // Reference Y
  align: 'center',   // Text center aligns to x
  vAlign: 'middle',  // Text middle aligns to y
});

// Right-bottom align at position (350, 180)
page.addText('Corner', {
  x: 350,
  y: 180,
  align: 'right',   // Right edge at x=350
  vAlign: 'bottom', // Bottom edge at y=180
});

🐛 Troubleshooting

Bluetooth Connection Issues

  • Problem: Cannot find or connect to printer
  • Solution:
    • Ensure Bluetooth is enabled on your device
    • Make sure printer is powered on and in pairing mode
    • Check that all required permissions are granted (Bluetooth, Location on Android)
    • Try listDevices() to scan for available printers

Text Not Rendering

  • Problem: Text appears blank or crashes
  • Solution:
    • If using addText(), make sure @shopify/react-native-skia is installed
    • Use Expo development build (not Expo Go) when using Skia
    • Alternatively, use addImageFromBuffer() with pre-rendered text images

Print Quality Issues

  • Problem: Print is too light or too dark
  • Solution: Adjust the density parameter (1-5, default 3) in createPrintTask()

Image Not Printing Correctly

  • Problem: Image appears distorted or incorrect colors
  • Solution:
    • Ensure image dimensions are multiples of 8 pixels for width
    • Adjust threshold parameter (default 128) in addImageFromBuffer()
    • Images are converted to black & white - use high contrast images

Android Crash on Disconnect

  • Problem: App crashes when disconnecting from printer on Android
  • Solution: This is a known issue with react-native-ble-plx. See workaround: https://github.com/dotintent/react-native-ble-plx/issues/1303#issuecomment-3367559459

Build Errors

  • Problem: Native module errors during build
  • Solution:
    • Run pod install in iOS directory after installing dependencies
    • For Android, sync gradle after adding dependencies
    • Clean build: cd ios && rm -rf Pods && pod install or cd android && ./gradlew clean

🔄 Migration from Web Version

Key changes from niimbluelib web version:

  1. No Canvas API - Use PrintPage class instead
  2. Skia for Text - Requires @shopify/react-native-skia and Expo dev build
  3. Auto Print Tasks - Use createPrintTask() instead of manual model selection
  4. Sync Text Rendering - addText() is now synchronous (load fonts externally)
  5. Buffer Polyfill - Uses buffer package for image decoding

📄 License

MIT

🙏 Credits

Based on niimbluelib by MultiMote.