DEV Community

Cover image for Print Setu: Print from Browser to USB Printers Without Installing Drivers
Bhavesh Mishra
Bhavesh Mishra

Posted on • Originally published at printsetu.dev

Print Setu: Print from Browser to USB Printers Without Installing Drivers

Have you ever needed to print directly from a web application to a USB printer without asking users to install drivers? If you're building POS systems, kiosk applications, or any offline-first web app that needs printing capabilities, you know the pain of traditional printing solutions.

I recently built Print Setu to solve exactly this problem. It's a lightweight, browser-compatible SDK that connects to USB printers via WebSocket - no drivers required!

🎯 The Problem

Traditional web printing faces several challenges:

  • Driver Installation Required: Users need to install and configure printer drivers
  • Browser Security Restrictions: Direct USB access from browsers is limited
  • Complex Setup: Setting up printing often requires technical expertise
  • Poor Offline Support: Most solutions require internet connectivity

For applications like retail POS systems, restaurant ordering kiosks, or thermal receipt printers, these limitations are deal-breakers.

πŸ’‘ The Solution: Print Setu

Print Setu provides a simple yet powerful approach:

  1. A lightweight WebSocket server runs locally (via Electron app or standalone service)
  2. Your web application connects using the Print Setu SDK
  3. Send print jobs directly from your browser to any USB printer
  4. Everything works completely offline

Key Features

βœ… No USB Drivers Required - Print directly from browser to USB printers

βœ… Offline Support - Works completely offline via local WebSocket

βœ… Auto-Reconnection - Handles disconnections gracefully

βœ… TypeScript Support - Full type definitions included

βœ… Framework Agnostic - Works with React, Vue, Angular, or vanilla JS

βœ… Event-Driven Architecture - Subscribe to printer events and status updates

πŸš€ Quick Start

Installation

npm install print-setu
Enter fullscreen mode Exit fullscreen mode

Basic Usage

import { PrinterService } from 'print-setu';

// Initialize the service
const printer = new PrinterService({
  url: 'ws://127.0.0.1:8899',
  apiKey: 'your-api-key-here'
});

// Connect to the service
await printer.connect();

// Scan for available printers
await printer.scanPrinters();

// Listen for discovered printers
printer.on('USB_PRINTERS_RESULT', (data) => {
  if (data.payload?.success) {
    console.log('Available printers:', data.payload.data);
  }
});

// Connect to a specific printer
await printer.connectPrinter('printer-id');

// Print data (base64 encoded)
await printer.print('printer-id', base64Data);
Enter fullscreen mode Exit fullscreen mode

That's it! No complex configuration, no driver installation, just simple and intuitive APIs.

🎨 Real-World Example: React Integration

Here's how you can integrate Print Setu into a React application with a custom hook:

// usePrinter.js
import { useState, useEffect, useCallback } from "react";
import { PrinterService } from 'print-setu';

const printer = new PrinterService({ 
  url: 'ws://127.0.0.1:8899', 
  apiKey: "your-api-key" 
});

export function usePrinter() {
  const [printers, setPrinters] = useState([]);
  const [isScanning, setIsScanning] = useState(false);
  const [connectedPrinter, setConnectedPrinter] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    // Connect to WebSocket
    printer.connect();

    // Subscribe to events
    const unsubscribe1 = printer.on("USB_PRINTERS_RESULT", (data) => {
      if (data.payload?.success) {
        setPrinters(data.payload.data || []);
        setIsScanning(false);
      }
    });

    const unsubscribe2 = printer.on("PRINTER_CONNECTED", (data) => {
      if (data.payload?.success) {
        setConnectedPrinter(data.payload.data);
      }
    });

    return () => {
      unsubscribe1();
      unsubscribe2();
    };
  }, []);

  const scanPrinters = useCallback(async () => {
    setIsScanning(true);
    await printer.scanPrinters();
  }, []);

  const connectPrinter = useCallback(async (printerId) => {
    await printer.connectPrinter(printerId);
  }, []);

  const print = useCallback(async (printerId, base64Data) => {
    await printer.print(printerId, base64Data);
  }, []);

  return {
    printers,
    isScanning,
    connectedPrinter,
    error,
    scanPrinters,
    connectPrinter,
    print
  };
}
Enter fullscreen mode Exit fullscreen mode

Using the Hook

import { usePrinter } from './usePrinter';

function PrinterComponent() {
  const {
    printers,
    isScanning,
    connectedPrinter,
    scanPrinters,
    connectPrinter,
    print
  } = usePrinter();

  const handlePrint = async () => {
    if (connectedPrinter) {
      const printData = btoa('Hello, Printer!');
      await print(connectedPrinter.id, printData);
    }
  };

  return (
    <div>
      <button onClick={scanPrinters} disabled={isScanning}>
        {isScanning ? 'Scanning...' : 'Scan Printers'}
      </button>

      <ul>
        {printers.map((printer) => (
          <li key={printer.id}>
            {printer.name}
            <button onClick={() => connectPrinter(printer.id)}>
              Connect
            </button>
          </li>
        ))}
      </ul>

      {connectedPrinter && (
        <div>
          <p>Connected to: {connectedPrinter.name}</p>
          <button onClick={handlePrint}>Print Test</button>
        </div>
      )}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

πŸŽͺ Vue.js Support Too!

Print Setu works seamlessly with Vue.js as well:

<template>
  <div>
    <button @click="scanPrinters" :disabled="isScanning">
      {{ isScanning ? 'Scanning...' : 'Scan Printers' }}
    </button>

    <ul>
      <li v-for="printer in printers" :key="printer.id">
        {{ printer.name }}
        <button @click="connectToPrinter(printer.id)">Connect</button>
      </li>
    </ul>

    <div v-if="connectedPrinter">
      <p>Connected to: {{ connectedPrinter.name }}</p>
      <button @click="handlePrint">Print Test</button>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import { PrinterService } from 'print-setu';

const printers = ref([]);
const isScanning = ref(false);
const connectedPrinter = ref(null);

const printer = new PrinterService({
  url: 'ws://127.0.0.1:8899',
  apiKey: 'your-api-key'
});

onMounted(async () => {
  await printer.connect();

  printer.on('USB_PRINTERS_RESULT', (data) => {
    if (data.payload?.success) {
      printers.value = data.payload.data || [];
      isScanning.value = false;
    }
  });

  printer.on('PRINTER_CONNECTED', (data) => {
    if (data.payload?.success) {
      connectedPrinter.value = data.payload.data;
    }
  });
});

const scanPrinters = async () => {
  isScanning.value = true;
  await printer.scanPrinters();
};

const connectToPrinter = async (printerId) => {
  await printer.connectPrinter(printerId);
};

const handlePrint = async () => {
  if (connectedPrinter.value) {
    const data = btoa('Hello from Vue!');
    await printer.print(connectedPrinter.value.id, data);
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

🎯 Use Cases

Print Setu is perfect for:

  • Retail POS Systems: Print receipts instantly without driver hassles
  • Restaurant Ordering Systems: Send orders directly to kitchen printers
  • Kiosk Applications: Enable self-service printing at events or information centers
  • Warehouse Management: Print labels and shipping information on-demand
  • Healthcare Systems: Print patient wristbands and labels
  • Ticketing Systems: Print tickets at entry points

πŸ—οΈ Architecture

The architecture is straightforward:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         WebSocket          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                 │◄───────(ws://127.0.0.1)────►│                  β”‚
β”‚  Web Browser    β”‚                             β”‚  Local Service   β”‚
β”‚  (Your App)     β”‚         Print Setu SDK      β”‚  (Electron App)  β”‚
β”‚                 β”‚                             β”‚                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                             β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                         β”‚
                                                         β”‚ USB
                                                         β–Ό
                                                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                                  β”‚   Printer   β”‚
                                                  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Enter fullscreen mode Exit fullscreen mode
  1. Your web app uses the Print Setu SDK
  2. SDK communicates with a local WebSocket server (port 8899)
  3. Server has direct USB access to connected printers
  4. Everything runs locally - no internet required!

πŸ“¦ Event-Driven API

Print Setu uses an event-driven architecture with consistent payload structure:

{
  payload?: {
    success: boolean;
    data?: any;
    error?: string;
  };
  error?: string;
}
Enter fullscreen mode Exit fullscreen mode

Available Events

  • USB_PRINTERS_RESULT - Printer scan results
  • PRINTER_DISCOVERED - Individual printer discovered
  • PRINTER_CONNECTED - Printer connection status
  • PRINTER_DISCONNECTED - Printer disconnection status
  • PRINT_RESULT - Print job completion status
  • ERROR - General errors

πŸ”§ Configuration Options

const printer = new PrinterService({
  url: 'ws://127.0.0.1:8899',        // WebSocket URL
  apiKey: 'your-api-key',             // Your API key
  maxReconnectAttempts: 5,            // Max reconnection attempts
  reconnectDelay: 2000,               // Delay between reconnects (ms)
  enableLogging: true                 // Enable console logging
});
Enter fullscreen mode Exit fullscreen mode

🌟 Why Print Setu?

Developer-Friendly

  • Clean, intuitive API
  • Full TypeScript support
  • Comprehensive documentation
  • Framework agnostic

Production-Ready

  • Auto-reconnection handling
  • Event-driven architecture
  • Error handling built-in
  • Offline-first design

Easy Setup

  • No complex configuration
  • No driver installation
  • Works with existing USB printers
  • Quick integration

🚧 Getting Started

  1. Download the Windows Service (Currently Windows only, Mac/Linux coming soon)

  2. Install the NPM Package

   npm install print-setu
Enter fullscreen mode Exit fullscreen mode
  1. Try the Demo

  2. Check the Documentation

πŸ—ΊοΈ Roadmap

  • [x] Windows support
  • [ ] macOS support
  • [ ] Linux support
  • [ ] Network printer support
  • [ ] Printer status monitoring
  • [ ] Print job queue management
  • [ ] Multiple printer support simultaneously

🀝 Contributing

Print Setu is open source! Contributions are welcome:

  • Report bugs via GitHub Issues
  • Submit pull requests
  • Suggest features
  • Improve documentation

πŸ“„ Browser Compatibility

  • Chrome/Edge 88+
  • Firefox 85+
  • Safari 14+
  • Opera 74+

🎬 Conclusion

Print Setu makes USB printing from web applications finally feel native. Whether you're building a POS system, kiosk application, or any web app that needs printing capabilities, Print Setu provides a clean, reliable solution without the driver installation headaches.

The best part? It's completely offline-capable, making it perfect for environments where internet connectivity isn't guaranteed.

Give it a try and let me know what you think! I'd love to hear your feedback and use cases.


Links:

What printing challenges are you facing? Let me know in the comments! πŸ‘‡

Top comments (0)