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:
- A lightweight WebSocket server runs locally (via Electron app or standalone service)
- Your web application connects using the Print Setu SDK
- Send print jobs directly from your browser to any USB printer
- 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
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);
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
};
}
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>
);
}
πͺ 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>
π― 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 β
βββββββββββββββ
- Your web app uses the Print Setu SDK
- SDK communicates with a local WebSocket server (port 8899)
- Server has direct USB access to connected printers
- 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;
}
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
});
π 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
-
Download the Windows Service (Currently Windows only, Mac/Linux coming soon)
Install the NPM Package
npm install print-setu
-
Try the Demo
- Visit demo.printsetu.dev
- See it in action with your USB printers
-
Check the Documentation
- Website: printsetu.dev
- GitHub: github.com/mishrabhavesh/print-setu
πΊοΈ 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:
- π Website: printsetu.dev
- π¦ NPM: npmjs.com/package/print-setu
- π» GitHub: github.com/mishrabhavesh/print-setu
- πͺ Demo: demo.printsetu.dev
What printing challenges are you facing? Let me know in the comments! π
Top comments (0)