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

escpos-ticket-builder

v1.0.0

Published

A flexible and type-safe ESC/POS thermal printer receipt builder with support for multiple printer profiles and quirks

Downloads

6

Readme

🧾 ESC/POS Ticket Builder

npm version License: MIT

A flexible and type-safe ESC/POS thermal printer receipt builder for Node.js and TypeScript. Build beautiful receipts with support for multiple printer profiles, quirks handling, and a fluent API.

✨ Features

  • 🎯 Type-Safe: Full TypeScript support with comprehensive type definitions
  • 🔧 Flexible: Works with multiple printer brands (Elgin, Bematech, Daruma, Epson, etc.)
  • 🎨 Rich Formatting: Headers, tables, QR codes, text alignment, and more
  • 🔌 Profile-Based: Pre-configured profiles for popular printers + custom profile support
  • 🐛 Quirks Handling: Built-in workarounds for printer-specific issues
  • 📦 Zero Dependencies: Lightweight and production-ready

📦 Installation

npm install escpos-ticket-builder
yarn add escpos-ticket-builder
pnpm add escpos-ticket-builder

🚀 Quick Start

import { receipt } from "escpos-ticket-builder";

const ticket = receipt()
  .center()
  .h1("MY STORE")
  .text("123 Main St, City")
  .br()
  .hr()
  .left()
  .productLine(2, "Coffee", 5.5)
  .productLine(1, "Croissant", 3.0)
  .hr()
  .money("TOTAL", 14.0)
  .br(2)
  .center()
  .text("Thank you!")
  .cut()
  .build();

// Send to printer
console.log(ticket); // ESC/POS command string

📚 Documentation

Basic Usage

Creating a Receipt

import { receipt } from "escpos-ticket-builder";

const ticket = receipt().text("Hello, World!").build();

Text Alignment

receipt()
  .left()
  .text("Left aligned")
  .center()
  .text("Center aligned")
  .right()
  .text("Right aligned")
  .build();

Headers

receipt()
  .h1("Large Header") // Extra large text
  .h2("Medium Header") // Large text
  .h3("Small Header") // Double width text
  .build();

Text Formatting

receipt()
  .strong()
  .text("Bold text")
  .endStrong()
  .underline("Underlined text")
  .build();

Advanced Features

Product Lines

receipt()
  .productLine(2, "Coffee", 5.5)
  // Output: 2x Coffee....................R$ 5,50

  .productLine(1, "Croissant")
  // Output: 1x Croissant
  .build();

Key-Value Rows

// Single row
receipt().row("Order", "#12345").row("Date", "2024-01-27").build();

// Multiple rows
receipt()
  .rows([
    { label: "Customer", value: "John Doe" },
    { label: "Phone", value: "(11) 98765-4321" },
    { label: "Address", value: "123 Main St" },
  ])
  .build();

Money Formatting

receipt()
  .money("Subtotal", 10.0)
  .money("Tax", 1.5)
  .money("TOTAL", 11.5)
  .build();

// Output:
// Subtotal: R$ 10,00
// Tax: R$ 1,50
// TOTAL: R$ 11,50

Tables

receipt()
  .table(
    [
      { title: "Item", flex: 2 },
      { title: "Qty", width: 5, align: "right" },
      { title: "Price", width: 10, align: "right" },
    ],
    [
      ["Coffee", "2", "R$ 5.50"],
      ["Croissant", "1", "R$ 3.00"],
      ["Orange Juice", "1", "R$ 4.00"],
    ],
  )
  .build();

Horizontal Rules

receipt()
  .hr() // Dashed line (default)
  .hr(40, "solid") // Solid line with custom width
  .hr(40, "double") // Double line
  .build();

Line Breaks and Spacing

receipt()
  .text("Line 1")
  .br() // Single line break
  .text("Line 2")
  .br(3) // Three line breaks
  .text("Line 3")
  .build();

Paper Cutting

receipt()
  .text("Receipt content")
  .cut() // Full cut with default feed
  .build();

receipt()
  .text("Receipt content")
  .cut(false) // Partial cut
  .build();

receipt()
  .text("Receipt content")
  .cut(true, 5) // Full cut with 5 feed lines
  .build();

Beep

receipt()
  .text("Order ready!")
  .beep() // Trigger printer beep/buzzer
  .build();

Printer Profiles

Using Pre-configured Profiles

import { receipt, profiles } from "escpos-ticket-builder";

// Generic ESC/POS (default)
const ticket1 = receipt(profiles.generic).text("Using generic profile").build();

// Elgin i9
const ticket2 = receipt(profiles.elginI9)
  .text("Using Elgin i9 profile")
  .build();

// Bematech MP-4200 TH
const ticket3 = receipt(profiles.bematechMP4200)
  .text("Using Bematech profile")
  .build();

// Daruma DR800
const ticket4 = receipt(profiles.darumaDR800)
  .text("Using Daruma profile")
  .build();

// 58mm printer
const ticket5 = receipt(profiles.printer58mm)
  .text("Using 58mm printer profile")
  .build();

Creating Custom Profiles

import { receipt, PrinterProfile } from "escpos-ticket-builder";

const customProfile: PrinterProfile = {
  name: "My Custom Printer",
  cols: 48,
  capabilities: {
    align: true,
    bold: true,
    underline: true,
    doubleWidth: true,
    doubleHeight: true,
    cut: true,
    partialCut: false,
    qrCode: false,
    beep: true,
  },
  quirks: {
    needsExtraFeedBeforeCut: 5,
    brokenBoldCommand: false,
    elginNeedsCRLF: false,
  },
};

const ticket = receipt(customProfile).text("Using custom profile").build();

Profile Configuration

Capabilities

| Capability | Description | | -------------- | ------------------------------------------------ | | align | Support for text alignment (left, center, right) | | bold | Support for bold text | | underline | Support for underlined text | | doubleWidth | Support for double width text | | doubleHeight | Support for double height text | | cut | Support for paper cutting | | partialCut | Support for partial paper cutting | | qrCode | Support for QR code printing | | beep | Support for beep/buzzer |

Quirks

| Quirk | Description | | --------------------------- | -------------------------------------- | | maxLineLength | Override maximum characters per line | | elginNeedsCRLF | Use CRLF (\r\n) instead of LF (\n) | | brokenBoldCommand | Disable bold if command is broken | | needsInitBeforeEveryPrint | Call initialize() automatically | | needsExtraFeedBeforeCut | Extra feed lines before cutting | | alternativeCutCommand | Custom cut command string |

🎯 Real-World Examples

Restaurant Receipt

import { receipt } from "escpos-ticket-builder";

const restaurantReceipt = receipt()
  .initialize()
  .center()
  .h1("RESTAURANT XYZ")
  .text("Rua Example, 123")
  .text("Tel: (11) 1234-5678")
  .text("CNPJ: 12.345.678/0001-90")
  .br()
  .hr()
  .left()
  .row("Mesa", "15")
  .row("Garcom", "João")
  .row("Data", "27/01/2024 14:30")
  .hr()
  .productLine(2, "X-Burger", 25.0)
  .productLine(1, "Batata Frita", 12.0)
  .productLine(2, "Refrigerante", 8.0)
  .productLine(1, "Sobremesa", 15.0)
  .hr()
  .money("Subtotal", 93.0)
  .money("Taxa Servico (10%)", 9.3)
  .strong()
  .money("TOTAL", 102.3)
  .endStrong()
  .hr()
  .center()
  .text("Obrigado pela preferencia!")
  .text("Volte sempre!")
  .br(2)
  .beep()
  .cut()
  .build();

Retail Receipt

import { receipt } from "escpos-ticket-builder";

const retailReceipt = receipt()
  .center()
  .h2("LOJA ABC")
  .text("Shopping Center - Loja 123")
  .br()
  .hr()
  .left()
  .table(
    [
      { title: "Item", flex: 2 },
      { title: "Qtd", width: 5, align: "center" },
      { title: "Valor", width: 12, align: "right" },
    ],
    [
      ["Camiseta Azul", "2", "R$ 59,90"],
      ["Calça Jeans", "1", "R$ 129,90"],
      ["Meia (Kit 3)", "1", "R$ 19,90"],
    ],
  )
  .hr()
  .right()
  .money("Subtotal", 269.6)
  .money("Desconto", -26.96)
  .strong()
  .money("TOTAL", 242.64)
  .endStrong()
  .br()
  .left()
  .rows([
    { label: "Forma Pagamento", value: "Cartao Credito" },
    { label: "Parcelas", value: "3x R$ 80,88" },
    { label: "CPF", value: "123.456.789-00" },
  ])
  .br()
  .center()
  .text("Troca em ate 30 dias")
  .text("www.lojaabc.com.br")
  .cut()
  .build();

Delivery Order

import { receipt } from "escpos-ticket-builder";

const deliveryOrder = receipt()
  .center()
  .h1("PEDIDO #1234")
  .br()
  .left()
  .strong()
  .text("CLIENTE:")
  .endStrong()
  .text("Maria Silva")
  .text("(11) 98765-4321")
  .br()
  .strong()
  .text("ENDERECO:")
  .endStrong()
  .text("Rua das Flores, 456")
  .text("Apto 302 - Jardim Paulista")
  .text("São Paulo - SP - 01234-567")
  .br()
  .hr()
  .strong()
  .text("PEDIDO:")
  .endStrong()
  .productLine(1, "Pizza Margherita G", 45.0)
  .productLine(2, "Refrigerante 2L", 12.0)
  .productLine(1, "Borda Catupiry", 8.0)
  .hr()
  .money("Subtotal", 77.0)
  .money("Taxa Entrega", 5.0)
  .strong()
  .money("TOTAL", 82.0)
  .endStrong()
  .br()
  .row("Pagamento", "Dinheiro")
  .row("Troco para", "R$ 100,00")
  .br()
  .center()
  .text("Tempo estimado: 40-50 min")
  .cut()
  .build();

🔌 Integration Examples

Sending to USB Printer (Node.js)

import { receipt } from "escpos-ticket-builder";
import { SerialPort } from "serialport";

const ticket = receipt().text("Hello from USB printer!").cut().build();

const port = new SerialPort({ path: "/dev/usb/lp0", baudRate: 9600 });
port.write(ticket, (err) => {
  if (err) {
    console.error("Error:", err);
  } else {
    console.log("Printed successfully!");
  }
});

Sending to Network Printer

import { receipt } from "escpos-ticket-builder";
import * as net from "net";

const ticket = receipt().text("Hello from network printer!").cut().build();

const client = new net.Socket();
client.connect(9100, "192.168.1.100", () => {
  client.write(ticket);
  client.end();
});

Converting to Base64

import { receipt } from "escpos-ticket-builder";

const ticket = receipt().text("Hello, World!").build();

const base64 = Buffer.from(ticket, "utf-8").toString("base64");
console.log(base64);

🛠️ Utility Functions

The library also exports utility functions for advanced use cases:

import { helpers } from "escpos-ticket-builder";

// Remove accents from text
const clean = helpers.removeAccents("São Paulo"); // 'Sao Paulo'

// Wrap text to fit width
const wrapped = helpers.wrapText("This is a long text that needs wrapping", 20);

// Create formatted row
const row = helpers.createRow("Label", "Value", 48);

// Create product line
const product = helpers.createProductLine(2, "Coffee", 5.5, 48);

🧪 Testing

import { receipt, profiles } from "escpos-ticket-builder";

// Test with different profiles
const testProfiles = [
  profiles.generic,
  profiles.elginI9,
  profiles.bematechMP4200,
];

testProfiles.forEach((profile) => {
  const ticket = receipt(profile)
    .center()
    .h1("TEST RECEIPT")
    .text(profile.name)
    .cut()
    .build();

  console.log(`Testing with ${profile.name}:`);
  console.log(ticket);
});

📄 License

MIT © [Your Name]

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

🐛 Issues

If you find a bug or have a feature request, please open an issue on GitHub.

📮 Support

🙏 Acknowledgments

Built with ❤️ for the thermal printer community.


Made with 🧾 by Your Name