MQTT Library
@receiptkit/mqtt-client provides real-time MQTT communication with React hooks, server utilities, and a framework-agnostic client.
When should I use this vs. the REST API?
Use the REST API for simple fire-and-forget print jobs. Use this MQTT library when you need real-time features: printer status monitoring, bridge discovery, live connection state, or high-frequency printing with minimal latency.
Installation
npm install @receiptkit/mqtt-clientpnpm add @receiptkit/mqtt-clientPeer dependency: react (optional, only needed for React hooks).
Package Exports
The library has three entry points for different environments:
| Import | Environment | Use Case |
|---|---|---|
| @receiptkit/mqtt-client | Any | Core client, types, topic builders, utilities |
| @receiptkit/mqtt-client/react | Browser | React provider, hooks (useReceiptKit, useBridgeStatus, etc.) |
| @receiptkit/mqtt-client/server | Node.js | Server-side singleton, serverPublish, serverRequestResponse |
Quick Start
Browser (Vanilla JS)
import { ReceiptKitClient } from "@receiptkit/mqtt-client";
// Initialize — creates a global singleton
const client = ReceiptKitClient.init({
apiKey: "rk_pub_your_public_key",
orgId: "your-org-id",
});
// Connect
await client.connect();
// Send a print job
await client.print("00:11:62:32:5A:2A", {
templateId: "your-template-id",
data: {
storeName: "My Store",
total: "$9.99",
},
});
// Check printer status (synchronous — uses cached data)
const status = client.statusCache.getPrinterStatus(
"bridge-id",
"00:11:62:32:5A:2A"
);
console.log(status); // { online: true, coverOpen: false, ... }React
import { ReceiptKitProvider } from "@receiptkit/mqtt-client/react";
function App() {
return (
<ReceiptKitProvider
apiKey="rk_pub_your_public_key"
orgId="your-org-id"
>
<YourApp />
</ReceiptKitProvider>
);
}import {
useReceiptKit,
useBridgeStatus,
usePrinterStatus,
} from "@receiptkit/mqtt-client/react";
function PrintButton() {
const { client, connectionStatus } = useReceiptKit();
const bridge = useBridgeStatus("your-bridge-id");
const printer = usePrinterStatus("your-bridge-id", "00:11:62:32:5A:2A");
const handlePrint = async () => {
await client.print({
printerId: "00:11:62:32:5A:2A",
bridgeId: "your-bridge-id",
templateId: "your-template-id",
data: { storeName: "My Store", total: "$9.99" },
});
};
return (
<div>
<p>MQTT: {connectionStatus}</p>
<p>Bridge: {bridge?.connectionLevel}</p>
<p>Printer: {printer?.statusLevel}</p>
<button onClick={handlePrint} disabled={connectionStatus !== "connected"}>
Print Receipt
</button>
</div>
);
}Server (Node.js / API Routes)
import { getServerClient, serverPublish } from "@receiptkit/mqtt-client/server";
// Option 1: Get the shared server client
const client = await getServerClient({
apiKey: "rk_live_your_secret_key",
orgId: "your-org-id",
});
await client.print({
printerId: "00:11:62:32:5A:2A",
bridgeId: "bridge-abc123",
templateId: "tmpl-id",
data: { total: "$9.99" },
});
// Option 2: One-liner publish (using topic builders)
import { createTopicBuilders } from "@receiptkit/mqtt-client";
const topics = createTopicBuilders({ apiKey: "rk_live_...", orgId: "org-id" });
await serverPublish(
{ apiKey: "rk_live_...", orgId: "org-id" },
topics.print("bridge-abc123"),
{ title: "print-job", printerId: "00:11:62:32:5A:2A", data: { total: "$9.99" } }
);Client Configuration
| Option | Type | Description |
|---|---|---|
| apiKey * | string | API key for MQTT authentication |
| orgId * | string | Organization ID (scopes all topics) |
| endpoint | string | AWS IoT endpoint (has sensible default) |
| environment | "browser" | "server" | Auto-detected. Controls client ID format and connection behavior |
| autoSubscribeStatus | boolean | Auto-subscribe to bridge status on connect (default: true) |
| keepalive | number | MQTT keepalive in seconds (default: 60) |
| reconnectPeriod | number | Reconnect interval in ms (default: 5000) |
| connectTimeout | number | Connection timeout in ms (default: 10000) |
| debug | boolean | Enable debug logging (default: auto-detect based on NODE_ENV) |
React Hooks
useReceiptKit()
Access the MQTT client instance and connection status.
const { client, connectionStatus } = useReceiptKit();
// connectionStatus: "disconnected" | "connecting" | "connected" | "error"useBridgeStatus(bridgeId)
Subscribe to a bridge's real-time status. Returns cached status with live updates.
const bridge = useBridgeStatus("bridge-abc123");
// bridge.connectionLevel: "live" | "stale" | "offline"
// bridge.printers: LivePrinter[]
// bridge.stats: { received, completed, failed }usePrinterStatus(bridgeId, printerMac)
Subscribe to a specific printer's hardware status (paper, cover, errors).
const printer = usePrinterStatus("bridge-abc123", "00:11:62:32:5A:2A");
// printer.statusLevel: "ready" | "paper-low" | "paper-out" | "cover-open" | ...
// printer.online: booleanuseBridgeDiscover()
Discover all bridges in your organization. Broadcasts a discovery request and collects responses.
const { bridges, isDiscovering, discover } = useBridgeDiscover();
// bridges: Map<string, BridgeStatusResponse>
// Call discover() to trigger a new scanTopic Builders
All MQTT topics are org-scoped. Use topic builders to generate correct topic strings instead of constructing them manually:
import { createTopicBuilders } from "@receiptkit/mqtt-client";
const topics = createTopicBuilders({ apiKey: "rk_pub_...", orgId: "your-org-id" });
// ── to-bridge (client/server → bridge) ────────────────────────────────
topics.print("bridge-abc123")
// → "receiptkit/org/{orgId}/to-bridge/bridge-abc123/print"
topics.bridgeStatusPoll("bridge-abc123")
// → "receiptkit/org/{orgId}/to-bridge/bridge-abc123/status-poll"
topics.bridgeDiscover()
// → "receiptkit/org/{orgId}/to-bridge/broadcast/discover"
// ── to-client (bridge → client/dashboard) ─────────────────────────────
topics.bridgeStatus("bridge-abc123")
// → "receiptkit/org/{orgId}/to-client/bridge-abc123/status"
topics.bridgePrintResult("bridge-abc123")
// → "receiptkit/org/{orgId}/to-client/bridge-abc123/print-result"
topics.bridgePresence("bridge-abc123")
// → "receiptkit/org/{orgId}/to-client/bridge-abc123/presence"
// ── Wildcard subscriptions ────────────────────────────────────────────
topics.allToClient()
// → "receiptkit/org/{orgId}/to-client/+/+"
topics.allBridgePresence()
// → "receiptkit/org/{orgId}/to-client/+/presence"Events
The client emits events for connection changes and incoming messages:
const client = ReceiptKitClient.getInstance();
client.on("connectionChange", (status) => {
console.log("MQTT status:", status);
// "disconnected" | "connecting" | "connected" | "error"
});
client.on("bridgeStatus", (bridgeId, status) => {
console.log(`Bridge ${bridgeId}:`, status);
});
client.on("printerStatus", (bridgeId, mac, status) => {
console.log(`Printer ${mac}:`, status);
});
client.on("bridgePresence", (bridgeId, isOnline) => {
console.log(`Bridge ${bridgeId} is ${isOnline ? "online" : "offline"}`);
});Key Concepts
Global Singleton
The client maintains one MQTT connection per page or process. Calling ReceiptKitClient.init() multiple times returns the same instance. This survives React Strict Mode, HMR, and in-app navigation.
StatusCache (Synchronous Reads)
Bridge and printer status is cached in memory. You can read status synchronously via client.statusCache without async MQTT round-trips. The cache is updated automatically when status messages arrive.
Auto-Reconnect
The underlying MQTT.js library handles reconnection automatically with a configurable interval (default: 5 seconds). The client never kills the connection on timeout — it lets MQTT.js keep trying.
Org-Scoped Isolation
All topics are automatically namespaced to the configured orgId. Cross-org messaging is impossible — the MQTT broker (AWS IoT Core) enforces topic-level access control via org-scoped IoT policies.
Related Pages
- Authentication — API key types and when to use rk_pub_ vs rk_live_
- REST API Reference — Simple HTTP-based printing without MQTT
- Getting Started — Set up your first printer and send a test print