How to send/write and receive/read (BIDI) USB Device Data from Blazor
Product JSPrintManager for Blazor Published 05/21/2026 Updated 05/21/2026 Author Neodynamic
Overview
JSPrintManager for Blazor supports Bidirectional (BIDI) USB Device Communication from Blazor allowing you to Send/Write & Receive/Read data strings to any USB Device reachable from the client system.
In this walkthrough, you'll learn how to Send/Write & Receive/Read data strings from Blazor through any USB Device reachable from the client machine. This solution works with any popular browser like Chrome, Firefox, IE/Edge & Safari on Windows!
Follow up these steps
- Be sure you install in your dev machine JSPrintManager (JSPM) (Available for Windows, Linux, Raspberry Pi & Mac)
This small app must be installed on each client that will print from your website! - In your Blazor project...
- Add a NuGet reference to the JSPrintManager Razor Component
- Add the JSPrintManager service...
- Add the following statement at the top of your
StartuporProgramfile
using Neodynamic.Blazor;- For Blazor Server
Add the following line in theStartup's ConfigureServicesmethod
services.AddJSPrintManager();For .NET 8+ you must use Interactive Server components and render mode, so add these settings:
builder.Services.AddRazorComponents() .AddInteractiveServerComponents();app.MapRazorComponents<App>() .AddInteractiveServerRenderMode() - For Blazor WebAssembly
Add the following line in theProgram's Mainmethod
builder.Services.AddJSPrintManager();
- For Blazor Server
- Add the following statement at the top of your
- Add the following statement in the
_Imports.razorfile
@using Neodynamic.Blazor - Add a new Razor Page and copy/paste the following code. Please read the source code comments to understand the printing logic!
For .NET 8+ Blazor Server you must use Interactive Server render mode, so add these settings:
@page "/" @rendermode InteractiveServer @inject JSPrintManager JSPrintManager@page "/" @inject JSPrintManager JSPrintManager @using System @using System.Text <div> <strong>JSPM </strong><span>WebSocket Status </span> @if (JSPrintManager.Status == JSPMWSStatus.Open) { <span class="badge badge-success"> <i class="fa fa-check" /> Open </span> } else if (JSPrintManager.Status == JSPMWSStatus.Closed) { <span class="badge badge-danger"> <i class="fa fa-exclamation-circle" /> Closed! </span> <div> <strong>JSPrintManager (JSPM) App</strong> is not installed or not running! <a href="https://neodynamic.com/downloads/jspm" target="_blank">Download JSPM Client App...</a> </div> } else if (JSPrintManager.Status == JSPMWSStatus.Blocked) { <span class="badge badge-warning"> <i class="fa fa-times-circle" /> This Website is Blocked! </span> } else if (JSPrintManager.Status == JSPMWSStatus.WaitingForUserResponse) { <span class="badge badge-warning"> <i class="fa fa-user-circle" /> Waiting for user response... </span> } </div> @if (JSPrintManager.Status == JSPMWSStatus.Open) { <div class="row"> <div class="col-md-12"> <h2 class="text-center"> BIDI USB Device Comm </h2> <hr /> </div> </div> <div class="row"> <div class="col-md-12"> <EditForm Model="@MyUsbComm"> <div class="form-group"> <div class="row"> <div class="col-md-10"> <label>USB Device Path:</label> <InputText @bind-Value="MyUsbComm.DevicePath" class="form-control form-control-sm" /> </div> <div class="col-md-2"> <button class="btn btn-primary" @onclick="OpenPopup">Show USB Devices...</button> </div> </div> <br /><br /> <div class="row"> <div class="col-md-2"> <button class="btn btn-success btn-lg" @onclick="DoUsbBidi"> Start BIDI Comm... </button> </div> <div class="col-md-2"> <button class="btn btn-danger btn-lg" @onclick="StopUsbBidi"> Stop </button> </div> </div> <br /><br /> <div class="row"> <div class="col-md-12"> <br /> <strong>Data to Send:</strong> <div class="input-group mb-3"> <InputText @bind-Value="dataToSend" class="form-control form-control-sm" /> <div class="input-group-append"> <button class="btn btn-info" @onclick="DoSendData"> Send... </button> </div> </div> </div> </div> <br /><br /> <div class="row"> <div class="col-md-12"> <div class="text-center"> USB Comm BIDI Messages<br /> <textarea class="terminal" readOnly @bind="Messages" rows="10" cols="100" /> </div> </div> </div> </div> </EditForm> </div> </div> } @if (IsVisible) { <div class="modal fade show" style="display:block; background-color: rgba(0,0,0,0.5);" tabindex="-1"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title">Available USB Devices</h5> <button type="button" class="btn-close" @onclick="ClosePopup" aria-label="Close"></button> </div> <div class="modal-body"> <p>@UsbDevicesInfo</p> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" @onclick="ClosePopup">Close</button> </div> </div> </div> </div> } @code { protected override void OnAfterRender(bool firstRender) { if (firstRender) { // Handle OnGetUsbDevices event... JSPrintManager.OnGetUsbDevices += () => { var sb = new StringBuilder(); foreach (var usbDev in JSPrintManager.UsbDevices) { sb.Append($"Name: {usbDev.Name}\n DevicePath: {usbDev.DevicePath}\n----------------------"); } UsbDevicesInfo = sb.ToString(); StateHasChanged(); }; JSPrintManager.OnStatusChanged += () => { StateHasChanged(); // Status = Open means that JSPM Client App is up and running! if (JSPrintManager.Status == JSPMWSStatus.Open) { //Try getting USB devices... JSPrintManager.TryGetUsbDevices(); } }; // Start WebSocket comm JSPrintManager.Start(); } base.OnAfterRender(firstRender); } protected override void OnInitialized() { JSPrintManager.OnError += () => { var error = JSPrintManager.LastErrorMessage; }; base.OnInitialized(); } private string Messages { get { return MessagesBuffer.ToString(); } set { MessagesBuffer.Clear(); MessagesBuffer.Append(value); } } private string UsbDevicesInfo = "No USB Devices found."; private StringBuilder MessagesBuffer = new StringBuilder(); private string dataToSend; // The USBComm info obj private USBComm MyUsbComm { get; set; } = new(); private void DoUsbBidi() { // handle USB Comm messages JSPrintManager.OnBTCommEvent += () => { var evt = JSPrintManager.USBCommEventCache[JSPrintManager.USBCommEventCache.Count - 1]; switch (evt.Type) { case USBCommEventType.Close: MessagesBuffer.Append("COMM CLOSE" + Environment.NewLine); break; case USBCommEventType.Error: MessagesBuffer.Append("COMM ERROR: " + evt.Data + Environment.NewLine); break; case USBCommEventType.Open: MessagesBuffer.Append("COMM OPEN" + Environment.NewLine); break; case USBCommEventType.DataReceived: MessagesBuffer.Append("<< " + evt.Data); break; case USBCommEventType.DataSent: MessagesBuffer.Append(">> " + evt.Data); break; } StateHasChanged(); }; // Start BIDI USB Comm... JSPrintManager.OpenUSBComm(MyUsbComm); } private void StopUsbBidi() { // Stop BIDI USB Comm... JSPrintManager.CloseUSBComm(MyUsbComm); } private void DoSendData() { // Send data through BIDI USB Comm... if (string.IsNullOrWhiteSpace(dataToSend) == false) { MyUsbComm.DataToSend = dataToSend; JSPrintManager.SendDataUSBComm(MyUsbComm); } } private bool IsVisible { get; set; } = false; private void OpenPopup() { IsVisible = true; } private void ClosePopup() { IsVisible = false; } }
- That's it! Run your website and play with it.