The Bridge Plugin is an optional enhancement to Vue Print It that enables direct communication with physical printers, bypassing the browser’s print dialog entirely. This is essential for POS systems, kiosks, receipt printers, and automated workflows.
Why Bridge Printing?
Silent Printing Print directly to configured printers without user interaction or print dialogs.
Printer Selection Programmatically choose which printer to use, perfect for multi-printer environments.
Print Job Control Set copies, paper size, and other print options programmatically.
Receipt Printers Direct support for thermal and ESC/POS receipt printers.
Architecture Overview
Bridge printing uses a client-server architecture:
Vue App : Your application with the bridge plugin installed
Bridge Plugin : Client-side code that communicates with the bridge service
Bridge Service : Local HTTP server that interfaces with system printers
System Printers : OS-level printer drivers
Physical Printer : The actual printing hardware
Installation
The bridge plugin is separate from the core Vue Print It plugin:
Install Both Plugins
Custom Configuration
import { createVuePrintIt , createVuePrintItBridge } from 'vue-print-it'
const app = createApp ( App )
// Install core plugin
app . use ( createVuePrintIt ({
windowTitle: 'My App' ,
preserveStyles: true
}))
// Install bridge plugin
app . use ( createVuePrintItBridge ({
baseUrl: 'http://localhost:8765' ,
autoConnect: true ,
autoSelectDefault: true
}))
app . mount ( '#app' )
Bridge Plugin Internals
The bridge plugin (bridge-plugin.ts:29) creates a reactive state management system:
export function createVuePrintItBridge ( options : BridgePluginOptions = {}) {
const bridgeOptions = {
baseUrl: 'http://localhost:8765' ,
autoConnect: false ,
autoSelectDefault: true ,
timeout: 2000 ,
retryAttempts: 3 ,
... options
};
// Create reactive bridge state
const bridgeState = reactive < BridgeState >({
availablePrinters: [],
defaultPrinter: bridgeOptions . defaultPrinter || null ,
isConnected: false ,
lastUpdated: null
});
return {
install ( app : App ) {
const bridgeClient = new BridgeClient ( bridgeOptions . baseUrl );
// Enhanced bridge client with state management
const enhancedBridgeClient = {
... bridgeClient ,
updatePrinters ,
setDefaultPrinter ,
getDefaultPrinter ,
getState : () => ({ ... bridgeState })
};
// Provide bridge client and state globally
app . provide ( 'vuePrintItBridge' , enhancedBridgeClient );
app . provide ( 'vuePrintItBridgeState' , bridgeState );
// Add to global properties
app . config . globalProperties . $printBridge = enhancedBridgeClient ;
app . config . globalProperties . $printBridgeState = bridgeState ;
// Auto-connect if enabled
if ( bridgeOptions . autoConnect ) {
updatePrinters (). catch (() => {
console . debug ( 'Bridge auto-connect failed - will retry on first use' );
});
}
}
};
}
Reactive State
The bridge maintains a reactive state that components can monitor:
interface BridgeState {
availablePrinters : BridgePrinter []; // List of discovered printers
defaultPrinter : string | null ; // Currently selected default
isConnected : boolean ; // Bridge service availability
lastUpdated : Date | null ; // Last printer list refresh
}
Bridge Client
The BridgeClient class (bridge-client.ts:11) handles HTTP communication:
export class BridgeClient {
private baseUrl : string ;
private isAvailable : boolean | null = null ;
constructor ( baseUrl : string = 'http://localhost:8765' ) {
this . baseUrl = baseUrl ;
}
// Check if bridge service is responding
async checkAvailability () : Promise < boolean > {
try {
const response = await fetch ( ` ${ this . baseUrl } /health` , {
method: 'GET' ,
signal: AbortSignal . timeout ( 2000 )
});
if ( response . ok ) {
this . isAvailable = true ;
return true ;
}
} catch ( error ) {
console . debug ( 'Bridge not available:' , error );
}
this . isAvailable = false ;
return false ;
}
// Get list of available printers
async getPrinters () : Promise < BridgePrinter []> {
const response = await fetch ( ` ${ this . baseUrl } /api/printers` );
return response . ok ? await response . json () : [];
}
// Send print job to bridge
async print ( request : BridgePrintRequest ) : Promise < BridgePrintResponse > {
const response = await fetch ( ` ${ this . baseUrl } /api/print` , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ( request )
});
if ( response . ok ) {
return await response . json ();
} else {
const error = await response . text ();
throw new Error ( `Bridge error: ${ error } ` );
}
}
// Convert HTML to Base64 for transmission
htmlToBase64 ( html : string ) : string {
return btoa ( unescape ( encodeURIComponent ( html )));
}
}
Automatic Bridge Detection
The core print function automatically detects and uses the bridge when appropriate:
// From usePrint.ts:334-347
async function print ( element : HTMLElement | string , localOptions : Partial < PrintOptions > = {}) {
const options = { ... defaultOptions , ... globalOptions , ... localOptions };
try {
await options . onBeforePrint ();
// Check if bridge should be used and is available
const bridgeClient = getBridgeClient ();
const shouldUseBridge = options . useBridge &&
bridgeClient &&
await bridgeClient . checkAvailability ();
if ( shouldUseBridge ) {
await printWithBridge ( element , options , globalOptions , bridgeClient );
} else {
await printWithBrowser ( element , options , globalOptions );
}
await options . onAfterPrint ();
} catch ( error ) {
options . onPrintError ( error as Error );
throw error ;
}
}
Bridge Print Flow
When using bridge printing, the complete HTML with styles is sent to the bridge service:
// From usePrint.ts:175-217
async function printWithBridge (
element : HTMLElement | string ,
options : any ,
globalOptions : Partial < GlobalPrintOptions >,
bridgeClient : any
) : Promise < void > {
const targetElement = resolveElement ( element );
const htmlContent = buildPrintHtml ( targetElement , options , globalOptions );
// Convert to Base64
const base64Content = bridgeClient . htmlToBase64 ( htmlContent );
// Determine printer name with fallback logic
let printerName = options . printerName ;
if ( ! printerName ) {
// Try to get default printer from bridge state
printerName = bridgeClient . getDefaultPrinter ? bridgeClient . getDefaultPrinter () : null ;
// If still no printer, refresh and try again
if ( ! printerName && bridgeClient . updatePrinters ) {
await bridgeClient . updatePrinters ();
printerName = bridgeClient . getDefaultPrinter ();
}
}
// Prepare request for bridge
const printRequest : BridgePrintRequest = {
printer_name: printerName ,
content: base64Content ,
content_type: options . contentType || 'html' ,
copies: options . copies || 1
};
// Send to bridge
const result = await bridgeClient . print ( printRequest );
if ( ! result . success ) {
throw new Error ( `Print error: ${ result . message } ` );
}
console . log ( `Print successful. Job ID: ${ result . job_id } ` );
}
Printer Selection Logic
Use explicitly specified printerName if provided
Fall back to bridge’s default printer
If no default, refresh printer list and try again
If still no printer, the request will fail with an error
Using Bridge Printing
Option 1: Automatic Detection
Set useBridge: true in print options:
await print ( 'receipt-content' , {
useBridge: true ,
printerName: 'EPSON TM-T88V' ,
copies: 2 ,
contentType: 'html'
})
Option 2: Direct Bridge Method
Use the printDirect method for full control:
import { usePrint } from 'vue-print-it'
const { printDirect } = usePrint ()
const htmlContent = `
<!DOCTYPE html>
<html>
<head><title>Receipt</title></head>
<body>
<h1>Order #12345</h1>
<p>Total: $99.99</p>
</body>
</html>
`
await printDirect ( htmlContent , {
printer_name: 'Receipt Printer' ,
content_type: 'html' ,
copies: 1
})
Option 3: Using the Composable
The usePrintBridge composable provides reactive state and methods:
< script setup >
import { usePrintBridge } from 'vue-print-it'
const {
bridgeState ,
refreshPrinters ,
setDefaultPrinter ,
getDefaultPrinter ,
checkConnection
} = usePrintBridge ()
// Check connection on mount
onMounted ( async () => {
const connected = await checkConnection ()
if ( connected ) {
await refreshPrinters ()
}
})
// Change default printer
const selectPrinter = ( printerName ) => {
setDefaultPrinter ( printerName )
}
</ script >
< template >
< div >
< h2 > Bridge Status </ h2 >
< p > Connected: {{ bridgeState . isConnected }} </ p >
< p > Default Printer: {{ bridgeState . defaultPrinter }} </ p >
< h3 > Available Printers </ h3 >
< ul >
< li v-for = " printer in bridgeState . availablePrinters " : key = " printer . name " >
{{ printer . name }}
< span v-if = " printer . is_default " > (Default) </ span >
< button @ click = " selectPrinter ( printer . name ) " > Set Default </ button >
</ li >
</ ul >
< button @ click = " refreshPrinters " > Refresh Printers </ button >
</ div >
</ template >
Bridge API Reference
Endpoints
The bridge service exposes these HTTP endpoints:
Check if bridge service is running Response: {
"status" : "ok" ,
"version" : "1.0.0" ,
"uptime" : 3600
}
List all available printers Response: [
{
"name" : "HP LaserJet Pro" ,
"is_default" : true ,
"status" : "idle"
},
{
"name" : "EPSON TM-T88V" ,
"is_default" : false ,
"status" : "idle"
}
]
Send a print job Request Body: {
"printer_name" : "EPSON TM-T88V" ,
"content" : "<base64-encoded-html>" ,
"content_type" : "html" ,
"copies" : 2 ,
"options" : {}
}
Response: {
"success" : true ,
"job_id" : "print-job-12345" ,
"message" : "Print job sent successfully"
}
Configuration Options
baseUrl
string
default: "http://localhost:8765"
URL where the bridge service is running. Can be a remote server.
Automatically connect and discover printers when plugin is installed.
Automatically select the system’s default printer if no printer is specified.
Connection timeout in milliseconds for bridge requests.
Number of times to retry failed connections before giving up.
Explicitly set a default printer by name. Takes precedence over system default.
Enable detailed logging for bridge operations.
When to Use Bridge Printing
Point of Sale (POS) Systems
Print receipts automatically when transactions complete without requiring user interaction. async function printReceipt ( order ) {
await print ( 'receipt-template' , {
useBridge: true ,
printerName: 'Receipt Printer' ,
autoClose: true
})
}
Self-service kiosks need to print tickets, receipts, or labels without showing print dialogs. await printDirect ( ticketHtml , {
printer_name: 'Label Printer' ,
content_type: 'html' ,
copies: 1
})
Multi-Printer Environments
Warehouses or offices with multiple printers where specific printers must be used for different document types. // Labels go to label printer
await print ( 'label' , {
useBridge: true ,
printerName: 'Zebra ZP450'
})
// Documents go to laser printer
await print ( 'document' , {
useBridge: true ,
printerName: 'HP LaserJet'
})
Background processes or scheduled tasks that need to print without user presence. // Nightly report generation
cron . schedule ( '0 0 * * *' , async () => {
const report = await generateReport ()
await printDirect ( report , {
printer_name: 'Office Printer' ,
copies: 3
})
})
Fallback Behavior
If bridge is unavailable, the plugin automatically falls back to browser printing:
const shouldUseBridge = options . useBridge &&
bridgeClient &&
await bridgeClient . checkAvailability ();
if ( shouldUseBridge ) {
await printWithBridge ( element , options , globalOptions , bridgeClient );
} else {
// Fallback to browser print dialog
await printWithBrowser ( element , options , globalOptions );
}
This ensures your application continues to work even if:
Bridge service is down
Network connection is lost
Bridge plugin isn’t installed
User is on a machine without the bridge
Troubleshooting
Symptoms : isConnected always false, print operations failSolutions :
Verify bridge service is running: curl http://localhost:8765/health
Check firewall isn’t blocking port 8765
Confirm correct baseUrl in plugin options
Enable debug logging: debug: true
Symptoms : availablePrinters array is emptySolutions :
Verify printers are installed and online in OS settings
Restart bridge service
Manually call refreshPrinters()
Check bridge service logs for errors
Symptoms : No error but nothing printsSolutions :
Check printer status (paper, ink, errors)
Verify printer name exactly matches: bridgeState.availablePrinters
Test printer with OS print dialog
Check bridge service logs for job details
Symptoms : Fetch errors in browser consoleSolutions :
Bridge service must send proper CORS headers
If bridge is on different domain, configure CORS
Use proxy in development if needed
Security Considerations
The bridge service has direct access to system printers. Always:
Run bridge service on trusted networks only
Use authentication if bridge is exposed
Validate all print content on server side
Never expose bridge to public internet without security
Next Steps
Plugin System Learn how the plugin architecture works
Style Injection Understand how styles are preserved