# Getting Started with SpawnDev.BlazorJS
This guide covers everything you need to start using SpawnDev.BlazorJS in your Blazor WebAssembly application.
---
## Installation
Install from NuGet:
```bash
dotnet add package SpawnDev.BlazorJS
```
Or add directly to your `.csproj`:
```xml
```
**Supported frameworks:** .NET 8, .NET 9, .NET 10
---
## Program.cs Setup
Two changes are required in `Program.cs`:
1. Register `BlazorJSRuntime` with `AddBlazorJSRuntime()`
2. Replace `RunAsync()` with `BlazorJSRunAsync()`
### Minimal Setup
```csharp
using SpawnDev.BlazorJS;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add("#app");
builder.RootComponents.Add("head::after");
// Add BlazorJSRuntime singleton service
builder.Services.AddBlazorJSRuntime();
// IMPORTANT: Use BlazorJSRunAsync instead of RunAsync
await builder.Build().BlazorJSRunAsync();
```
### Setup with Worker Scope Detection (out var JS)
The `AddBlazorJSRuntime` method has an overload that outputs the `BlazorJSRuntime` instance immediately. This lets you detect the current scope (Window, Worker, etc.) before the app finishes building - critical for apps that run in both Window and Worker contexts.
```csharp
using SpawnDev.BlazorJS;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
// Get BlazorJSRuntime immediately via out parameter
builder.Services.AddBlazorJSRuntime(out var JS);
// Only add root components when running in a Window scope
// Workers do not have a DOM - adding components there would fail
if (JS.IsWindow)
{
builder.RootComponents.Add("#app");
builder.RootComponents.Add("head::after");
}
// Register your services
builder.Services.AddSingleton();
await builder.Build().BlazorJSRunAsync();
```
This is the standard pattern used across all SpawnDev projects. The same `Program.cs` runs in both the browser Window and any Web Worker contexts. `BlazorJSRunAsync()` handles the difference automatically - in a Window it calls `RunAsync()` to start the Blazor renderer, in a Worker it enters a service-only mode.
### Setup with WebWorkerService
When using [SpawnDev.BlazorJS.WebWorkers](https://github.com/LostBeard/SpawnDev.BlazorJS.WebWorkers) for multi-threaded workers:
```csharp
using SpawnDev.BlazorJS;
using SpawnDev.BlazorJS.WebWorkers;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.Services.AddBlazorJSRuntime(out var JS);
if (JS.IsWindow)
{
builder.RootComponents.Add("#app");
builder.RootComponents.Add("head::after");
}
// Add WebWorkerService for spawning workers from .NET
builder.Services.AddWebWorkerService();
// Register services - these are available in both Window and Worker scopes
builder.Services.AddSingleton();
await builder.Build().BlazorJSRunAsync();
```
### Loading a JavaScript Library Before App Start
Some JS libraries need to be loaded before the app starts. You can do this using the `BlazorJSRuntime` instance from `out var JS`:
```csharp
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.Services.AddBlazorJSRuntime(out var JS);
if (JS.IsWindow)
{
builder.RootComponents.Add("#app");
builder.RootComponents.Add("head::after");
// Load a JS library before the app starts
await JS.LoadScript("_content/MyLibrary/my-library.js");
}
await builder.Build().BlazorJSRunAsync();
```
---
## Injecting BlazorJSRuntime
### In Razor Components
```csharp
@inject BlazorJSRuntime JS
Window Height: @windowHeight
@code {
int windowHeight;
protected override void OnInitialized()
{
windowHeight = JS.Get("window.innerHeight");
}
}
```
Or using the `[Inject]` attribute in code-behind:
```csharp
[Inject]
BlazorJSRuntime JS { get; set; }
```
### In Services
```csharp
public class MyService
{
private readonly BlazorJSRuntime _js;
public MyService(BlazorJSRuntime js)
{
_js = js;
}
public string GetUserAgent()
{
return _js.Get("navigator.userAgent");
}
}
```
### Static Access from JSObject Classes
Inside any class that inherits from `JSObject`, you can access `BlazorJSRuntime` via the protected static `JS` property:
```csharp
public class MyWrapper : JSObject
{
public MyWrapper(IJSInProcessObjectReference _ref) : base(_ref) { }
// JS.New calls the BlazorJSRuntime static instance
public MyWrapper(string url) : base(JS.New("MyClass", url)) { }
}
```
---
## Basic Operations
### Get and Set Global Properties
```csharp
// Get a property value (supports dot notation)
var height = JS.Get("window.innerHeight");
var title = JS.Get("document.title");
var userAgent = JS.Get("navigator.userAgent");
// Set a property value
JS.Set("document.title", "My Blazor App");
JS.Set("myGlobalVar", 42);
// Get a typed JSObject wrapper
using var window = JS.Get("window");
using var document = JS.Get("document");
using var navigator = JS.Get("navigator");
```
### Call Synchronous Methods
Use `Call` for methods that return a value, `CallVoid` for void methods:
```csharp
// Call a method that returns a value
var item = JS.Call("localStorage.getItem", "myKey");
// Call a void method
JS.CallVoid("localStorage.setItem", "myKey", "myValue");
JS.CallVoid("console.log", "Hello from Blazor!");
// Call with multiple arguments
var sum = JS.Call("Math.max", 10, 20);
```
### Call Asynchronous Methods (Promise-returning)
Use `CallAsync` for JavaScript methods that return a Promise:
```csharp
// Fetch API - returns a Promise
using var response = await JS.CallAsync("fetch", "/api/data");
var text = await response.Text();
// Any Promise-returning method
var result = await JS.CallAsync("myAsyncFunction", arg1, arg2);
```
**CRITICAL:** Using `CallAsync` on a synchronous JS method will THROW. Using `Call` on an async/Promise method will return a `Promise` object, not the resolved value. Match the call type to the JS method type.
### Create New JavaScript Objects
```csharp
// Create a new JS object using the constructor name
using var audio = JS.New