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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@thermal-print/react

v0.4.0

Published

React components and integration for thermal printers

Downloads

411

Readme

@thermal-print/react

React components and integration for thermal printers with browser printing support.

📦 Installation

pnpm add @thermal-print/react
# or
npm install @thermal-print/react
# or
yarn add @thermal-print/react

🔄 Migrating from @react-pdf/renderer?

If you're currently using @react-pdf/renderer, check out our comprehensive Migration Guide. Most components are drop-in replacements, and migration is usually straightforward!

🎯 Purpose

This package provides React components optimized for thermal printers, along with conversion utilities for both direct thermal printing (ESC/POS) and browser printing (HTML/PDF).

Three conversion paths:

  1. React → ESC/POS - Direct thermal printer output via convertToESCPOS()
  2. React → PrintNode IR - Intermediate representation via convertToPrintNodes()
  3. React → HTML/DOM - Browser rendering via convertToHTML()

🚀 Quick Start

Basic Receipt for Thermal Printer

import React from "react";
import {
  Document,
  Page,
  View,
  Text,
  StyleSheet,
  convertToESCPOS,
} from "@thermal-print/react";

const styles = StyleSheet.create({
  header: {
    fontSize: 20,
    textAlign: "center",
    fontWeight: "bold",
  },
  row: {
    flexDirection: "row",
    justifyContent: "space-between",
  },
});

function Receipt() {
  return (
    <Document>
      <Page>
        <Text style={styles.header}>MY STORE</Text>

        <View style={{ borderBottom: "1px solid black" }} />

        <View style={styles.row}>
          <Text>Coffee</Text>
          <Text>$3.50</Text>
        </View>

        <View style={styles.row}>
          <Text style={{ fontWeight: "bold" }}>Total</Text>
          <Text style={{ fontWeight: "bold" }}>$3.50</Text>
        </View>
      </Page>
    </Document>
  );
}

// Convert to ESC/POS and print
const buffer = await convertToESCPOS(<Receipt />, {
  paperWidth: 48,
  cut: "full",
});

await printer.write(buffer);

Browser Preview

import { Preview } from "@thermal-print/react";

function App() {
  return (
    <Preview id="receipt-preview" paperWidth={48} showRuler>
      <Document>
        <Page>
          <Text>This is how it will print!</Text>
        </Page>
      </Document>
    </Preview>
  );
}

Browser Printing with PDF

import { convertToHTML } from "@thermal-print/react";
import { convertToPDF } from "@thermal-print/pdf";

async function handlePrint() {
  // Step 1: Render to DOM
  const htmlResult = await convertToHTML(
    <Receipt />,
    {
      containerId: "thermal-receipt",
      keepInDOM: true,
    }
  );

  // Step 2: Convert to PDF
  const pdfResult = await convertToPDF("thermal-receipt", {
    paperSize: "80mm",
    scale: 2,
  });

  // Step 3: Open print dialog
  window.open(pdfResult.url);

  // Cleanup
  htmlResult.cleanup();
  pdfResult.cleanup();
}

📖 Components

Document

Root wrapper for thermal printer documents.

<Document>
  <Page>...</Page>
</Document>

Props: None

Page

Semantic page wrapper. Thermal printers print continuously, so this is mainly for logical grouping.

<Page style={{ padding: 20 }}>
  <Text>Content</Text>
</Page>

Props:

  • style?: ViewStyle - Layout styling

View

Layout container with flexbox support.

// Column layout (default)
<View style={{ padding: 10 }}>
  <Text>Item 1</Text>
  <Text>Item 2</Text>
</View>

// Row layout (side-by-side)
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
  <Text>Left</Text>
  <Text>Right</Text>
</View>

// With borders
<View style={{ borderTop: '1px solid black' }}>
  <Text>Content</Text>
</View>

Props:

  • style?: ViewStyle - Layout styling
  • children?: ReactNode - Child elements

Supported styles:

  • flexDirection?: 'row' | 'column'
  • justifyContent?: 'space-between' | 'center' | 'flex-start' | 'flex-end'
  • padding?: number
  • paddingTop?: number
  • paddingBottom?: number
  • margin?: number
  • marginTop?: number
  • marginBottom?: number
  • borderTop?: string - e.g., '1px solid black' or '1px dashed black'
  • borderBottom?: string
  • width?: string | number - For columns, e.g., '50%' or 24

Text

Text content with styling support.

<Text style={{ fontSize: 20, textAlign: "center", fontWeight: "bold" }}>
  Hello World
</Text>

Props:

  • style?: TextStyle - Text styling
  • children?: ReactNode - Text content

Supported styles:

  • fontSize?: number - Maps to thermal printer character sizes
  • fontWeight?: 'bold' | 'normal' | number - Bold emphasis
  • textAlign?: 'left' | 'center' | 'right' - Text alignment

Font size mapping:

  • 8-12px → 1x1 (normal)
  • 13-18px → 1x2 (double height)
  • 19-24px → 2x1 (double width)
  • 25+px → 2x2 (double both)

Image

Display images on thermal printers (converted to monochrome).

<Image
  src="data:image/png;base64,..."
  style={{ textAlign: "center" }}
/>

Props:

  • src: string - Image source (data URI or URL)
  • style?: { textAlign?: 'left' | 'center' | 'right' } - Alignment

Note: Images are automatically:

  • Resized to fit paper width
  • Converted to grayscale
  • Converted to monochrome (1-bit)
  • Printed using ESC/POS raster graphics

Preview

Visual preview component for development and testing.

<Preview
  id="receipt-preview"
  paperWidth={48}
  showRuler
  scale={1.5}
>
  <Document>
    <Page>
      <Text>Preview content</Text>
    </Page>
  </Document>
</Preview>

Props:

  • id?: string - Container ID (useful for PDF conversion)
  • paperWidth?: number - Characters per line (default: 48)
  • showRuler?: boolean - Show character ruler at top
  • scale?: number - Scale factor (default: 1)
  • style?: CSSProperties - Additional CSS styling

StyleSheet

Utility for organizing styles (pass-through, no actual processing).

const styles = StyleSheet.create({
  header: { fontSize: 20, textAlign: 'center' },
  text: { fontSize: 12 },
  bold: { fontWeight: 'bold' }
});

<Text style={styles.header}>Title</Text>

Font

No-op for thermal printers. Reserved for future PDF export compatibility.

Font.register({
  family: "Roboto",
  fonts: [{ src: "https://..." }],
});

📖 API Functions

convertToESCPOS(component, options?)

Converts React component directly to ESC/POS buffer. This is a convenience wrapper that combines convertToPrintNodes() + printNodesToESCPOS().

Parameters:

  • component: ReactElement - React component to convert
  • options?: ConversionOptions - Conversion options

Options:

interface ConversionOptions {
  paperWidth?: number;              // Characters per line (default: 48)
  encoding?: string;                // Character encoding (default: 'utf-8')
  debug?: boolean;                  // Enable debug output
  cut?: boolean | 'full' | 'partial'; // Paper cut (default: 'full')
  feedBeforeCut?: number;           // Lines to feed before cut (default: 3)
  commandAdapter?: 'escpos' | 'escbematech'; // Protocol (default: 'escpos')
  adapter?: ComponentMapping | RendererAdapter; // Custom component mapping
}

Returns: Promise<Buffer> - ESC/POS command buffer

Example:

const buffer = await convertToESCPOS(<Receipt />, {
  paperWidth: 48,
  cut: 'full',
  commandAdapter: 'escpos'
});

convertToPrintNodes(component, adapter?)

Converts React component to PrintNode intermediate representation (IR).

Parameters:

  • component: ReactElement - React component to convert
  • adapter?: RendererAdapter - Optional custom adapter

Returns: PrintNode | null - PrintNode tree

Example:

import { convertToPrintNodes } from '@thermal-print/react';
import { printNodesToESCPOS } from '@thermal-print/escpos';

// Step 1: React → PrintNode
const printNode = convertToPrintNodes(<Receipt />);

// Step 2: Manipulate PrintNode if needed
// ... modify printNode ...

// Step 3: PrintNode → ESC/POS
const buffer = await printNodesToESCPOS(printNode, {
  paperWidth: 48
});

convertToHTML(component, options?)

Converts React component to HTML/DOM using ReactDOM. Useful for browser-based workflows.

Parameters:

  • component: ReactElement - React component to convert
  • options?: ConvertToHTMLOptions - Conversion options

Options:

interface ConvertToHTMLOptions {
  width?: number;                   // Container width in pixels (default: 400)
  applyThermalStyles?: boolean;     // Apply thermal styling (default: true)
  format?: 'html' | 'element';      // Return format (default: 'element')
  containerId?: string;             // Custom container ID
  keepInDOM?: boolean;              // Keep in DOM (default: false)
}

Returns: Promise<ConvertToHTMLResult>

interface ConvertToHTMLResult {
  content: string | HTMLElement;    // Rendered content
  container: HTMLElement;           // Container element
  containerId: string;              // Container ID
  cleanup: () => void;              // Cleanup function
}

Examples:

Return HTMLElement:

const result = await convertToHTML(<Receipt />, {
  format: 'element'
});
const element = result.content as HTMLElement;
result.cleanup();

Return HTML string:

const result = await convertToHTML(<Receipt />, {
  format: 'html'
});
const html = result.content as string;

Keep in DOM with custom ID:

const result = await convertToHTML(<Receipt />, {
  containerId: 'my-receipt',
  keepInDOM: true,
  width: 600
});
// Container stays in DOM, accessible via document.getElementById('my-receipt')

🎨 Styling Guide

Text Alignment

<Text style={{ textAlign: 'center' }}>Centered</Text>
<Text style={{ textAlign: 'left' }}>Left aligned</Text>
<Text style={{ textAlign: 'right' }}>Right aligned</Text>

Font Sizes

<Text style={{ fontSize: 12 }}>Normal (1x1)</Text>
<Text style={{ fontSize: 16 }}>Tall (1x2)</Text>
<Text style={{ fontSize: 20 }}>Wide (2x1)</Text>
<Text style={{ fontSize: 28 }}>Large (2x2)</Text>

Bold Text

<Text style={{ fontWeight: 'bold' }}>Bold text</Text>
<Text style={{ fontWeight: 700 }}>Also bold</Text>

Dividers

<View style={{ borderTop: '1px solid black' }} />
<View style={{ borderBottom: '1px dashed black' }} />

Spacing

<View style={{ marginTop: 10, marginBottom: 10 }}>
  <Text>Content with margin</Text>
</View>

<View style={{ paddingTop: 5, paddingBottom: 5 }}>
  <Text>Content with padding</Text>
</View>

Row Layouts

// Two-column layout
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
  <Text>Item</Text>
  <Text>$10.00</Text>
</View>

// Three-column layout with fixed widths
<View style={{ flexDirection: 'row' }}>
  <Text style={{ width: '40%' }}>Product</Text>
  <Text style={{ width: '30%' }}>Qty</Text>
  <Text style={{ width: '30%' }}>Price</Text>
</View>

🔧 Advanced Usage

Custom Component Adapter

import { createAdapter, convertToESCPOS } from '@thermal-print/react';

const customAdapter = createAdapter({
  Receipt: 'document',
  ReceiptItem: 'text',
  Logo: 'image'
});

const buffer = await convertToESCPOS(<Receipt />, {
  adapter: customAdapter
});

Manipulate PrintNode IR

import { convertToPrintNodes } from '@thermal-print/react';
import { printNodesToESCPOS } from '@thermal-print/escpos';

let printNode = convertToPrintNodes(<Receipt />);

// Add watermark
printNode = {
  ...printNode,
  children: [
    ...printNode.children,
    {
      type: 'text',
      props: { children: 'COPY - NOT ORIGINAL' },
      children: [],
      style: { textAlign: 'center' }
    }
  ]
};

const buffer = await printNodesToESCPOS(printNode);

📄 License

MIT © Gabriel Martinusso

🔗 Related Resources

Packages

Documentation