Skip to content

Migration from @react-pdf/renderer

Complete guide for migrating from @react-pdf/renderer to @thermal-print/react.

Why Migrate?

@thermal-print/react is specifically optimized for thermal printers with multiple output options:

Feature@react-pdf/renderer@thermal-print
Primary outputPDF fileESC/POS buffer OR PDF
Thermal printingIndirect (via PDF)Native ESC/POS commands
PDF generationYesYes (@thermal-print/pdf)
Speed~500-2000ms~50-150ms
Bundle size~2.5MB~500KB
Browser previewPDF viewerThermal-style preview
Vector PDFYesYes (jsPDF-based)
Dynamic heightManualAutomatic with wrap prop

Installation

Remove old dependencies

bash
npm uninstall @react-pdf/renderer
# or
pnpm remove @react-pdf/renderer

Install new packages

bash
# Core package (required)
pnpm add @thermal-print/react

# Optional: For PDF browser printing
pnpm add @thermal-print/pdf

Quick Migration Checklist

  • [ ] Update imports from @react-pdf/renderer to @thermal-print/react
  • [ ] Replace PDFViewer/PDFDownloadLink with Preview component
  • [ ] Replace ReactPDF.render() with convertToESCPOS() or convertToHTML()
  • [ ] Update style properties (most are compatible)
  • [ ] Test with thermal printer or Preview component

Component Mapping

@react-pdf/renderer@thermal-printNotes
DocumentDocumentDrop-in replacement
PagePageDrop-in replacement (supports size and wrap props)
ViewViewDrop-in replacement
TextTextDrop-in replacement
ImageImageDrop-in replacement
StyleSheetStyleSheetDrop-in replacement
FontFontNo-op for thermal printers
PDFViewerPreviewDifferent API (thermal-style preview)
PDFDownloadLinkprintNodesToPDF()Returns PDF bytes directly
BlobProviderprintNodesToPDF() or convertToESCPOS()Returns PDF bytes or ESC/POS buffer
usePDFconvertToPrintNodes() + printNodesToPDF()Use conversion functions directly
LinkN/ANot supported (thermal printers don't support links)
NoteN/ANot supported
CanvasN/ANot supported

Code Examples

Before (with @react-pdf/renderer)

tsx
import React from 'react';
import {
  Document,
  Page,
  Text,
  View,
  StyleSheet,
  PDFViewer
} from '@react-pdf/renderer';
import ReactPDF from '@react-pdf/renderer';

const styles = StyleSheet.create({
  page: { padding: 30 },
  header: { fontSize: 20, textAlign: 'center', marginBottom: 10 },
  row: { flexDirection: 'row', justifyContent: 'space-between' },
});

function Receipt() {
  return (
    <Document>
      <Page style={styles.page}>
        <Text style={styles.header}>MY STORE</Text>
        <View style={styles.row}>
          <Text>Coffee</Text>
          <Text>$3.50</Text>
        </View>
      </Page>
    </Document>
  );
}

// Preview in browser
function App() {
  return (
    <PDFViewer>
      <Receipt />
    </PDFViewer>
  );
}

// Generate PDF
ReactPDF.render(<Receipt />, `receipt.pdf`);

After (with @thermal-print/react)

tsx
import React from 'react';
import {
  Document,
  Page,
  Text,
  View,
  StyleSheet,
  Preview,          // Instead of PDFViewer
  convertToESCPOS   // For thermal printing
} from '@thermal-print/react';

const styles = StyleSheet.create({
  page: { padding: 30 },
  header: { fontSize: 20, textAlign: 'center', marginBottom: 10 },
  row: { flexDirection: 'row', justifyContent: 'space-between' },
});

function Receipt() {
  return (
    <Document>
      <Page style={styles.page}>
        <Text style={styles.header}>MY STORE</Text>
        <View style={styles.row}>
          <Text>Coffee</Text>
          <Text>$3.50</Text>
        </View>
      </Page>
    </Document>
  );
}

// Preview in browser (thermal printer style)
function App() {
  return (
    <Preview paperWidth={48} showRuler>
      <Receipt />
    </Preview>
  );
}

// Generate ESC/POS for thermal printer
const buffer = await convertToESCPOS(<Receipt />, {
  paperWidth: 48,
  cut: 'full'
});
await printer.write(buffer);

Style Migration

Supported Properties

Most CSS-like properties from @react-pdf/renderer work in @thermal-print/react:

PropertySupportNotes
fontSizeFullMaps to thermal printer character sizes
fontWeightFull'bold' or >= 700
textAlignFull'left', 'center', 'right'
flexDirectionFull'row', 'column'
justifyContentPartial'space-between', 'center', 'flex-start', 'flex-end'
paddingFullpaddingTop, paddingBottom
marginFullmarginTop, marginBottom
borderTopFullSolid or dashed dividers
borderBottomFullSolid or dashed dividers
widthFullFor column layouts
colorNoThermal printers are monochrome
backgroundColorNoThermal printers are monochrome
fontFamilyNoThermal printers have fixed fonts
positionNoNot applicable to thermal printing
transformNoNot applicable to thermal printing

Font Size Mapping

@react-pdf/renderer uses points (pt), thermal printers use character multipliers:

@react-pdf/renderer@thermal-print/reactResult
fontSize: 8-12fontSize: 8-121x1 (normal)
fontSize: 14-18fontSize: 13-181x2 (double height)
fontSize: 20-24fontSize: 19-242x1 (double width)
fontSize: 28+fontSize: 25+2x2 (double both)

TIP

Font sizes translate almost 1:1, but thermal printers have discrete size steps (1x1, 1x2, 2x1, 2x2).

API Migration

PDFViewer → Preview

Before:

tsx
import { PDFViewer } from '@react-pdf/renderer';

<PDFViewer width="100%" height="600">
  <Receipt />
</PDFViewer>

After:

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

<Preview paperWidth={48} showRuler scale={1.5}>
  <Receipt />
</Preview>

New Props:

  • paperWidth - Characters per line (default: 48 for 80mm paper)
  • showRuler - Show character ruler for debugging
  • scale - Scale factor for preview size

ReactPDF.render() → convertToESCPOS()

Before:

tsx
import ReactPDF from '@react-pdf/renderer';

// Generate PDF file
await ReactPDF.render(<Receipt />, './receipt.pdf');

// Or get blob
const blob = await ReactPDF.renderToStream(<Receipt />);

After:

tsx
import { convertToESCPOS } from '@thermal-print/react';
import fs from 'fs';

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

// Save to file
fs.writeFileSync('./receipt.bin', buffer);

// Or send to printer
await printer.write(buffer);

For PDF downloads, use @thermal-print/pdf which generates vector PDFs directly from PrintNodes:

Before:

tsx
import { PDFDownloadLink } from '@react-pdf/renderer';

<PDFDownloadLink document={<Receipt />} fileName="receipt.pdf">
  {({ loading }) => loading ? 'Loading...' : 'Download PDF'}
</PDFDownloadLink>

After (Option 1 - Vector PDF from PrintNodes):

tsx
import { convertToPrintNodes } from '@thermal-print/react';
import { printNodesToPDF } from '@thermal-print/pdf';

async function handleDownload() {
  // Convert React to PrintNodes
  const printNode = convertToPrintNodes(<Receipt />);

  // Generate vector PDF
  const pdfBytes = await printNodesToPDF(printNode, {
    paperWidth: 227,    // 80mm in points
    paperHeight: 'auto' // Dynamic height
  });

  // Create blob and download
  const blob = new Blob([pdfBytes], { type: 'application/pdf' });
  const url = URL.createObjectURL(blob);

  const a = document.createElement('a');
  a.href = url;
  a.download = 'receipt.pdf';
  a.click();

  URL.revokeObjectURL(url);
}

<button onClick={handleDownload}>Download PDF</button>

After (Option 2 - DOM-based PDF):

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

async function handleDownload() {
  // Render to DOM
  const htmlResult = await convertToHTML(<Receipt />, {
    containerId: 'receipt-download',
    keepInDOM: true
  });

  // Convert DOM to PDF
  const pdfResult = await convertToPDF('receipt-download', {
    paperSize: '80mm',
    filename: 'receipt.pdf' // Auto-download
  });

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

BlobProvider → Direct Buffer/PDF Generation

Before:

tsx
import { BlobProvider } from '@react-pdf/renderer';

<BlobProvider document={<Receipt />}>
  {({ blob, url, loading }) => {
    // Use blob
  }}
</BlobProvider>

After (ESC/POS Buffer):

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

// Get ESC/POS buffer directly
const buffer = await convertToESCPOS(<Receipt />, {
  paperWidth: 48
});

// Convert to blob if needed
const blob = new Blob([buffer], { type: 'application/octet-stream' });
const url = URL.createObjectURL(blob);

After (PDF Blob):

tsx
import { convertToPrintNodes } from '@thermal-print/react';
import { printNodesToPDF } from '@thermal-print/pdf';

// Get PDF bytes
const printNode = convertToPrintNodes(<Receipt />);
const pdfBytes = await printNodesToPDF(printNode);

// Create PDF blob
const blob = new Blob([pdfBytes], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);

// Open in new tab or use as needed
window.open(url);

Image Migration

Both libraries support images, but with different processing:

Before (@react-pdf/renderer):

tsx
<Image src="https://example.com/logo.png" />
<Image src={require('./logo.png')} />

After (@thermal-print/react):

tsx
// Base64 data URI (recommended for thermal printers)
<Image src="data:image/png;base64,iVBORw0KG..." />

// URL (will be fetched and converted)
<Image src="https://example.com/logo.png" />

Key differences:

  • Images are automatically converted to monochrome (1-bit) for thermal printers
  • Images are resized to fit paper width
  • Best results with high-contrast images

Common Migration Issues

Issue: Fonts not rendering

Problem: Font.register() doesn't work

Solution: Thermal printers have fixed fonts. Remove Font.register() calls. The Font API is a no-op for compatibility.

tsx
// Remove this - it's a no-op
Font.register({ family: 'Roboto', fonts: [...] });

// Thermal printers use their built-in font

Issue: Colors not showing

Problem: color and backgroundColor styles don't work

Solution: Thermal printers are monochrome. Remove color styles.

tsx
// Before
<Text style={{ color: '#ff0000', backgroundColor: '#ffff00' }}>
  Red text
</Text>

// After - use bold for emphasis
<Text style={{ fontWeight: 'bold' }}>Important text</Text>

Issue: Complex layouts not rendering

Problem: Advanced flexbox or absolute positioning doesn't work

Solution: Thermal printers support limited layouts:

  • Column layout (default, stacked vertically)
  • Row layout (side-by-side with flexDirection: 'row')
  • Use justifyContent: 'space-between' for two-column layouts
tsx
// Supported
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
  <Text>Left</Text>
  <Text>Right</Text>
</View>

// Not supported - use simpler layouts
<View style={{ position: 'absolute', top: 10, left: 10 }}>
  <Text>Positioned text</Text>
</View>

Issue: Page breaks not working

Problem: break prop doesn't create page breaks

Solution: Thermal printers print continuously. Use spacing instead:

tsx
// Before
<View break />

// After - add spacing
<View style={{ marginTop: 20, marginBottom: 20 }}>
  <Text>Next section</Text>
</View>

Performance Comparison

Operation@react-pdf/renderer@thermal-print/react
Simple receipt (10 lines)~500ms~50ms
Complex receipt (50 lines)~2000ms~150ms
With images~3000ms~800ms
Bundle size impact~2.5MB~500KB

INFO

Timings are approximate and depend on hardware. Thermal printing with @thermal-print/react is typically 5-10x faster.

Migration Checklist

  • [ ] Install @thermal-print/react
  • [ ] Optionally install @thermal-print/pdf for browser printing
  • [ ] Update all imports from @react-pdf/renderer to @thermal-print/react
  • [ ] Replace PDFViewer with Preview
  • [ ] Replace ReactPDF.render() with convertToESCPOS()
  • [ ] Remove Font.register() calls
  • [ ] Remove color-related styles
  • [ ] Test with Preview component
  • [ ] Test with actual thermal printer
  • [ ] Update any custom wrappers or utilities
  • [ ] Remove @react-pdf/renderer dependency

Released under the MIT License.