FluentSignals.Blazor 1.1.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package FluentSignals.Blazor --version 1.1.0
                    
NuGet\Install-Package FluentSignals.Blazor -Version 1.1.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="FluentSignals.Blazor" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FluentSignals.Blazor" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="FluentSignals.Blazor" />
                    
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 FluentSignals.Blazor --version 1.1.0
                    
#r "nuget: FluentSignals.Blazor, 1.1.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 FluentSignals.Blazor@1.1.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=FluentSignals.Blazor&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=FluentSignals.Blazor&version=1.1.0
                    
Install as a Cake Tool

FluentSignals.Blazor

Blazor integration for FluentSignals - A powerful reactive state management library. This package provides Blazor-specific components, SignalBus for inter-component communication, and helpers to make working with signals in Blazor applications seamless and efficient.

Features

  • 📡 SignalBus - Publish/Subscribe pattern for component communication
  • 📬 Queue-based subscriptions - Receive messages even if published before subscription
  • 🎯 Resource components - Display any async resource with loading/error states
  • 🔌 SignalR integration - Real-time data with ResourceSignalRView
  • 🌐 HTTP Resource components - Ready-to-use components for HTTP resources
  • 🎯 SignalComponentBase - Base component class with signal integration
  • Automatic UI updates - Components automatically re-render when signals change
  • 🔄 Lifecycle integration - Proper subscription cleanup on component disposal

Installation

dotnet add package FluentSignals.Blazor

Quick Start

Basic Setup

// Program.cs
builder.Services.AddFluentSignalsBlazor(options =>
{
    options.WithBaseUrl("https://api.example.com")
           .WithTimeout(TimeSpan.FromSeconds(30));
});

// Or with SignalBus
builder.Services.AddFluentSignalsBlazorWithSignalBus();

Using Signals in Components

@inherits SignalComponentBase

<h3>Counter: @count.Value</h3>
<button @onclick="Increment">Increment</button>

@code {
    private Signal<int> count = new(0);

    private void Increment()
    {
        count.Value++;
    }
}

SignalBus - Publishing Messages

@inject ISignalPublisher SignalPublisher

<button @onclick="PublishMessage">Send Message</button>

@code {
    private async Task PublishMessage()
    {
        await SignalPublisher.PublishAsync(new UserCreatedEvent 
        { 
            UserId = 123, 
            Name = "John Doe" 
        });
    }
}

SignalBus - Consuming Messages

@inject ISignalConsumer<UserCreatedEvent> SignalConsumer
@implements IDisposable

<h3>New Users</h3>
<ul>
    @foreach (var user in users)
    {
        <li>@user.Name</li>
    }
</ul>

@code {
    private List<UserCreatedEvent> users = new();
    private IDisposable? subscription;

    protected override void OnInitialized()
    {
        // Standard subscription - only receives new messages
        subscription = SignalConsumer.Subscribe(message =>
        {
            users.Add(message);
            InvokeAsync(StateHasChanged);
        });
    }

    public void Dispose()
    {
        subscription?.Dispose();
    }
}

Queue-based Subscriptions

@inject ISignalConsumer<NotificationMessage> SignalConsumer

@code {
    protected override void OnInitialized()
    {
        // Queue subscription - receives ALL messages, including those published before subscription
        subscription = SignalConsumer.SubscribeByQueue(async message =>
        {
            notifications.Add(message);
            await InvokeAsync(StateHasChanged);
        }, processExistingMessages: true);
    }
}

Resource Components


<ResourceSignalView TData="Product" Fetcher="@LoadProduct">
    <LoadingContent>
        <div class="spinner">Loading product...</div>
    </LoadingContent>
    <DataContent Context="product">
        <h3>@product.Name</h3>
        <p>Price: @product.Price.ToString("C")</p>
    </DataContent>
    <ErrorContent Context="error">
        <div class="error">@error.Message</div>
    </ErrorContent>
</ResourceSignalView>


<ResourceSignalRView TData="StockPrice" 
                     HubUrl="/stockHub" 
                     MethodName="PriceUpdate"
                     ShowConnectionStatus="true">
    <DataContent Context="stock">
        <div class="stock-price">
            <h4>@stock.Symbol</h4>
            <span class="price">@stock.Price.ToString("C")</span>
            <span class="change @(stock.Change >= 0 ? "up" : "down")">
                @stock.Change.ToString("+0.00;-0.00")
            </span>
        </div>
    </DataContent>
</ResourceSignalRView>


<HttpResourceView TData="User" Url="/api/users/1">
    <LoadingContent>
        <p>Loading user data...</p>
    </LoadingContent>
    <DataContent Context="user">
        <h3>@user.Name</h3>
        <p>Email: @user.Email</p>
    </DataContent>
    <ErrorContent Context="error">
        <p class="error">Failed to load user: @error.Message</p>
    </ErrorContent>
</HttpResourceView>

Advanced Features

Custom Signal Components

public class MyCustomComponent : SignalComponentBase
{
    private ComputedSignal<string> fullName;
    
    protected override void OnInitialized()
    {
        var firstName = new Signal<string>("John");
        var lastName = new Signal<string>("Doe");
        
        fullName = CreateComputed(() => $"{firstName.Value} {lastName.Value}");
        
        // Component will automatically re-render when fullName changes
        WatchSignal(fullName);
    }
}

Service Registration

// Register a consumer for a specific message type
builder.Services.AddSignalConsumer<OrderPlacedEvent>();

// The consumer can then be injected into components
@inject ISignalConsumer<OrderPlacedEvent> OrderConsumer

Best Practices

  1. Always dispose subscriptions - Use IDisposable on your components
  2. Use queue subscriptions for cross-page messaging - Messages persist across navigation
  3. Keep messages immutable - Create new instances rather than modifying existing ones
  4. Use typed messages - Create specific classes for different message types

Documentation

For more examples and detailed documentation, visit our GitHub repository.

License

This project is licensed under the MIT License.

Product Compatible and additional computed target framework versions.
.NET 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 was computed.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.1.5 109 7/17/2025
2.1.4 113 7/17/2025
2.1.3 132 7/15/2025
2.1.2 127 7/15/2025
2.1.1 141 7/8/2025
2.1.0 133 7/8/2025
2.0.0 136 6/29/2025
1.1.3 148 6/19/2025
1.1.2 146 6/17/2025
1.1.1 140 6/16/2025
1.1.0 138 6/16/2025
1.0.0 130 6/15/2025