BlazorJS 2.3.0

dotnet add package BlazorJS --version 2.3.0
                    
NuGet\Install-Package BlazorJS -Version 2.3.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="BlazorJS" Version="2.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="BlazorJS" Version="2.3.0" />
                    
Directory.Packages.props
<PackageReference Include="BlazorJS" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add BlazorJS --version 2.3.0
                    
#r "nuget: BlazorJS, 2.3.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package BlazorJS@2.3.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=BlazorJS&version=2.3.0
                    
Install as a Cake Addin
#tool nuget:?package=BlazorJS&version=2.3.0
                    
Install as a Cake Tool

<p align="center"> <img src="https://raw.githubusercontent.com/fgilde/BlazorJS/master/logo.png" alt="BlazorJS" width="200" /> </p>

<h1 align="center">BlazorJS</h1>

<p align="center">Better JavaScript interaction for Blazor.</p>

<p align="center"> <a href="https://quickrun.org/run?repo=fgilde/BlazorJS"><img src="https://quickrun.org/badge.svg" alt="Run the sample with QuickRun" /></a> <a href="https://www.nuget.org/packages/BlazorJS"><img src="https://img.shields.io/nuget/v/BlazorJS?style=flat-square&color=8b5cf6&label=nuget" alt="NuGet" /></a> <a href="https://www.nuget.org/packages/BlazorJS"><img src="https://img.shields.io/nuget/dt/BlazorJS?style=flat-square&color=22d3ee&label=downloads" alt="Downloads" /></a> <img src="https://img.shields.io/badge/net6%20%E2%80%93%20net10-6366f1?style=flat-square" alt="Target frameworks" /> <img src="https://img.shields.io/badge/license-MIT-8b5cf6?style=flat-square" alt="MIT" /> </p>

<p align="center"> <b><a href="https://fgilde.github.io/BlazorJS/">📖 Documentation</a></b>  ·  <a href="https://www.nuget.org/packages/BlazorJS">NuGet</a>  ·  <a href="https://quickrun.org/run?repo=fgilde/BlazorJS">Run the sample</a> </p>


Run the sample

The repository ships a sample app with a live page for every feature. With QuickRun installed it is one click:

QuickRun

Or the manual way:

dotnet run --project BlazorJSSample

What it does

  1. A Scripts component to load JavaScript and stylesheet files per page or component, unloaded again on dispose.
  2. IJSRuntime extensions for dynamic invocation that remove the need to write JS wrapper functions for everything.
  3. Event interop to hook any browser event, plus ResizeObserver and IntersectionObserver, onto a Blazor component.
  4. Saving files through the File System Access API, streamed, with a download fallback.
  5. Clipboard, dialog and DOM helpers with the browser quirks already handled.
  6. A base component to import a module and create a JS object reference from it.

Target frameworks: net10.0, net9.0, net8.0, net7.0, net6.0 and netstandard2.1.

Installation

dotnet add package BlazorJS

Open _Imports.razor and add the usings:

@using BlazorJS
@using BlazorJS.Attributes
@using BlazorJS.JsInterop

The browser side registers itself through a Blazor JS initializer, so there is no service registration and no script tag to add.


<ins>Scripts Component</ins>

The scripts component allows you to include every javascript file easily to your pages or components. For example open any page like the index.razor and add

<Scripts src="js/myjsfile.js"></Scripts>

This component can also load stylesheet files

<Scripts src="js/myjsfile.js,css/mystyle.css"></Scripts>
Include multiple javascript files

Multiple js files can be loaded with a comma seperator ,

<Scripts src="js/myjsfile.js, js/myjsfile2.js"></Scripts>
Parameters
Parameter Default Description
Src One or more files, comma separated
UnloadOnDispose true Removes the elements again when the component is disposed
SourceLoadBehaviour OnAfterRender OnInitialized, OnInitializedAsync, OnAfterRender or OnAfterRenderAsync
SourceLoaded EventCallback<string> raised per file once it finished loading

The same from code:

await jsRuntime.LoadFilesAsync("js/chart.js", "css/chart.css");
await jsRuntime.UnloadFilesAsync("js/chart.js");

<ins>Extended Dynamic JS Invocation</ins>

The Dynamic Invocation extension for IJSRuntime allows for dynamic invocation of JavaScript functions from C#. This extension provides a method DInvokeVoidAsync, which takes in a function to be invoked and an array of objects to be passed as arguments to that function.

Simple Call
await jsRuntime.DInvokeVoidAsync(window => window.alert("test"));
Passing Parameters

Only the source text of the lambda is transferred, so variables from your component do not exist in the browser. Something like this is not enough:

// DONT COPY THIS!! SAMPLE FOR NOT WORKING
await jsRuntime.DInvokeVoidAsync(window => window.alert(currentCount));

Pass the parameters instead:

await jsRuntime.DInvokeVoidAsync((window, c) => window.alert(c), currentCount);
await jsRuntime.DInvokeVoidAsync((window, c, p2, p3) => window.alert(c), currentCount, param2, param3);
await jsRuntime.DInvokeVoidAsync((window, c, x) =>
        {
            window.alert(c);
            window.console.log(x);
        }, currentCount, "Flo");

Or add parameters with the class JSArgument, which keeps the original variable names:

var date = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
var name = "John";
await jsRuntime.DInvokeVoidAsync(window => window.alert(currentCount + " - " + date + name),
                                 JSArgument.For(currentCount).And(date).And(name));
Using return values

All samples above are also callable with a generic argument to use return values.

var res = await jsRuntime.DInvokeAsync<string>(window => window.prompt());
Console.WriteLine(res);

Results can be reused as parameters:

var hash = await jsRuntime.DInvokeAsync<string>(window =>
{
    window.alert(currentCount);
    return window.location.hash + "_" + currentCount;
}, new[] { JSArgument.For(currentCount) });
await jsRuntime.DInvokeVoidAsync(document => document.location.hash = hash, new[] { JSArgument.For(hash) });
// After alerting the currentCount we update the url in browser like this #1_2_3_4...

<ins>Clipboard</ins>

navigator.clipboard only exists in a secure context. CopyToClipboardAsync uses it when available and falls back to the legacy execCommand path otherwise, so it also works on plain http during development.

var copied = await jsRuntime.CopyToClipboardAsync("Copied with BlazorJS");
if (!copied)
    await jsRuntime.AlertAsync("The browser refused the copy.");

// reading always needs a secure context and a user permission, returns null when denied
var text = await jsRuntime.ReadClipboardAsync();

<ins>Saving files</ins>

Blazor can read files with <InputFile>, but writing one back out is still a pile of JavaScript. SaveFileAsync uses the File System Access API when the browser has it, so the user gets a real save dialog and picks the location, and falls back to a plain download otherwise.

// a string, the mime type defaults to text/plain
await jsRuntime.SaveFileAsync("notes.txt", "Written by BlazorJS");

// bytes
await jsRuntime.SaveFileAsync("report.pdf", pdfBytes, "application/pdf");

// or a stream, nothing is buffered in memory twice
await using var stream = File.OpenRead(path);
var saved = await jsRuntime.SaveFileAsync("export.csv", stream, "text/csv");

if (!saved)
    Console.WriteLine("The user closed the save dialog.");

The content is streamed with a DotNetStreamReference, so large files also work in Blazor Server, where a single SignalR message is capped at 32 KB. Call it from a user interaction, browsers reject a save dialog that no click asked for.

<ins>Simple event interop helper</ins>

A simple possibility to hook events, with an easy OnBlur extension for the click-outside case.

private BlazorJSEventInterop<PointerEventArgs> _jsEvent;

_jsEvent = new BlazorJSEventInterop<PointerEventArgs>(_jsRuntime);
await _jsEvent.OnBlur(OnFocusLeft, ".element-selector");

private Task OnFocusLeft(PointerEventArgs arg)
{
    return Task.CompletedTask;
}

You can also use it manually with any event you want to.

private BlazorJSEventInterop<PointerEventArgs> _jsEvent;

_jsEvent = new BlazorJSEventInterop<PointerEventArgs>(_jsRuntime);
await _jsEvent.AddEventListener("NameOfEvent", async args => { await YourCallBack(); }, ".element-selector");

If the selector does not exist yet, a MutationObserver waits for it and attaches the listener as soon as Blazor rendered the element.

<ins>Resize and visibility observers</ins>

The same interop class also exposes a ResizeObserver and an IntersectionObserver. Both are disconnected when the interop instance is disposed.

private BlazorJSEventInterop<ElementSizeArgs> _resize;
private BlazorJSEventInterop<ElementVisibilityArgs> _visibility;

_resize = new BlazorJSEventInterop<ElementSizeArgs>(_jsRuntime);
await _resize.OnResize(OnResized, ".my-chart");   // without a selector: the whole viewport

_visibility = new BlazorJSEventInterop<ElementVisibilityArgs>(_jsRuntime);
await _visibility.OnVisibilityChanged(LoadMore, "#load-more-marker", threshold: 0.5);

private Task OnResized(ElementSizeArgs args)              // Width, Height, Top, Left
    => InvokeAsync(() => RedrawChart(args.Width, args.Height));

private async Task LoadMore(ElementVisibilityArgs args)   // IsVisible, Ratio
{
    if (args.IsVisible)
        await LoadNextPage();
}

Typical use cases: redrawing a canvas or chart when its container changes, and infinite scrolling or lazy loading with a marker element at the end of a list.

<ins>Dialogs and small helpers</ins>

await jsRuntime.AlertAsync("Saved");
var ok   = await jsRuntime.ConfirmAsync("Delete this item?");
var name = await jsRuntime.PromptAsync("Your name?", "Flo");

await jsRuntime.AddCss(".demo { color: #22d3ee }", "my-styles", skipIfElementExists: true);
await jsRuntime.LoadCss("css/component.css");   // from an <EmbeddedResource>

var exists  = await jsRuntime.IsElementAvailableAsync("my-element-id");
await jsRuntime.RemoveElementAsync("my-element-id");
var scripts = await jsRuntime.GetLoadedScriptsAsync();

// wait for a global that a third party script defines
var ready = await jsRuntime.WaitForNamespaceAsync("google.maps");

<ins>BaseComponent for Js wrapper components</ins>

BlazorJS provides a small base component called BlazorJsBaseComponent<T> to create a JS wrapper component. This is a simple way to create a JS object reference from a module and use it in your blazor component.

  1. Create a razor component

<ins>YourComponent.razor</ins>

@inherits BlazorJs.BlazorJsBaseComponent<YourComponent>

<div @ref="ElementReference">

</div>

<ins>YourComponent.razor.cs</ins>

public partial class YourComponent
{
    protected override string ComponentJsFile() => "./js/PathToYourComponent.js";
    protected override string ComponentJsInitializeMethodName() => "initializeMethodForYourComponent";

    [Parameter]
    public string SomeGeneralParam { get; set; }

    [Parameter, ForJs]
    public int ParamForJs { get; set; } = 100;

    [Parameter, ForJs("anotherParamForJsWithDifferentNameInJs")]
    public int AnotherParamForJs { get; set; } = 100;

    protected override async Task OnJsOptionsChanged()
    {
        // This method will automatically be called when a parameter marked with [ForJs] has changed
        if (JsReference != null)
            await JsReference.InvokeVoidAsync("setOptions", MyJsOptions());
    }

    private object MyJsOptions()
    {
        return this.AsJsObject(new
        {
            configValueWirthoutParam = 123,
        });
    }

    /// <summary>
    /// Gets the JavaScript arguments to pass to the component.
    /// We override here because by default only the element reference and dotnet reference is passed
    /// but we want to have directly the JsOptions available.
    /// </summary>
    public override object[] GetJsArguments() => new[] { ElementReference, CreateDotNetObjectReference(), MyJsOptions() };
}
  1. Create your js file thats located in the path you have defined in ComponentJsFile()

<ins>./js/PathToYourComponent.js</ins>

class YourComponent {
    elementRef;
    dotnet;
    constructor(elementRef, dotNet, options) {
        this.elementRef = elementRef;
        this.dotnet = dotNet;
        this.createWhatever(options);
    }

     createWhatever(options) {
        // Do something with the options
        console.log(options.paramForJs);
        console.log(options.anotherParamForJsWithDifferentNameInJs);
        console.log(options.configValueWirthoutParam);
     }

    setOptions(options) {
        // Just update the options with the new ones
    }

    dispose() {
        // Dispose everything you created
    }
}

window.YourComponent = YourComponent;

// This method will be called from the BlazorJsBaseComponent and should match the name you have defined in `ComponentJsInitializeMethodName()`
export function initializeMethodForYourComponent(elementRef, dotnet, options) {
    return new YourComponent(elementRef, dotnet, options);
}

<ins>Browser detect</ins>

<BrowserDetect @bind-browserInfo="@Info"
               OSVersionUpdate="v => osVersion = v"
               OSArchitectureUpdate="a => architecture = a" />

@code {
    public BrowserInfo Info { get; set; }
}

BrowserInfo carries browser name and version, engine, operating system, screen resolution, time zone, user agent and the IsMobile / IsAndroid / IsIPhone / IsIPad flags.


The full documentation lives at fgilde.github.io/BlazorJS. BlazorJS is MIT licensed.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on BlazorJS:

Package Downloads
MudBlazor.Extensions

MudBlazor.Extensions is a small extension library for MudBlazor from https://mudblazor.com/

AuralizeBlazor

AuralizeBlazor is a wrapper component for audioMotion-analyzer.

MudExRichTextEditor

MudExRichTextEditor is a custom reusable control that allows us to easily consume Quill combining in a MudBlazor project.

GitHub repositories (2)

Showing the top 2 popular GitHub repositories that depend on BlazorJS:

Repository Stars
fgilde/MudBlazor.Extensions
MudBlazor.Extensions from https://www.mudex.org is a small extension for MudBlazor from https://mudblazor.com
fgilde/MudExRichTextEditor
Quill based RichEditor component for MudBlazor
Version Downloads Last Updated
2.3.0 0 9/4/2026
2.2.0 313,102 1/7/2025
2.1.6 72,056 6/28/2024
2.1.5 65,962 2/2/2024
2.1.4 15,344 11/25/2023
2.1.3 557 11/24/2023
2.1.2 10,660 11/2/2023
2.0.9 11,595 9/13/2023
2.0.8 5,394 7/29/2023
2.0.7 652 7/28/2023
2.0.6 1,792 7/14/2023
2.0.5 869 6/13/2023
2.0.4 16,792 4/21/2023
2.0.3 6,699 3/2/2023
2.0.2 2,801 2/15/2023
2.0.1 782 2/8/2023
2.0.0 3,304 1/23/2023
1.0.4 3,084 12/9/2022
1.0.3 807 12/9/2022
Loading failed