FractalDataWorks.UI.Components.Blazor 0.1.0-preview.11

This is a prerelease version of FractalDataWorks.UI.Components.Blazor.
dotnet add package FractalDataWorks.UI.Components.Blazor --version 0.1.0-preview.11
                    
NuGet\Install-Package FractalDataWorks.UI.Components.Blazor -Version 0.1.0-preview.11
                    
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="FractalDataWorks.UI.Components.Blazor" Version="0.1.0-preview.11" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FractalDataWorks.UI.Components.Blazor" Version="0.1.0-preview.11" />
                    
Directory.Packages.props
<PackageReference Include="FractalDataWorks.UI.Components.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 FractalDataWorks.UI.Components.Blazor --version 0.1.0-preview.11
                    
#r "nuget: FractalDataWorks.UI.Components.Blazor, 0.1.0-preview.11"
                    
#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 FractalDataWorks.UI.Components.Blazor@0.1.0-preview.11
                    
#: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=FractalDataWorks.UI.Components.Blazor&version=0.1.0-preview.11&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=FractalDataWorks.UI.Components.Blazor&version=0.1.0-preview.11&prerelease
                    
Install as a Cake Tool

FractalDataWorks.UI.Components.Blazor

Blazor-specific implementation of the FractalDataWorks UI component system. Provides RenderTreeBuilder-based rendering on top of the Web.Abstractions layer with Blazor component lifecycle integration.

Overview

This package implements the Blazor rendering layer for FractalDataWorks UI components. It inherits from WebComponentBase<TSelf, TModel> and adds Blazor-specific rendering via RenderTreeBuilder, JavaScript interop via IJSRuntime, and Blazor component lifecycle hooks.

Key Features:

  • Blazor RenderTreeBuilder rendering with full lifecycle
  • JavaScript interop via IJSRuntime adapter
  • Cascading parameters for theme and configuration
  • Primitive input components (TextInput, NumericInput, Switch, DateTimePicker, TextArea)
  • TypeCollection dropdowns for type-safe selection
  • Collection rendering with multiple display modes
  • Validation integration with EditContext
  • Two-way binding support

Target Framework: net10.0 Dependencies:

  • FractalDataWorks.UI.Web.Abstractions
  • Microsoft.AspNetCore.Components (v10.0+)
  • Microsoft.AspNetCore.Components.Web (v10.0+)
  • Microsoft.JSInterop (v10.0+)

Architecture

┌─────────────────────────────────────────────┐
│ FractalDataWorks.UI.Abstractions            │
│ - ComponentBase<TSelf, TModel>              │
│ - PropertyComponent<TSelf, TProperty>       │
│ - ZERO dependencies                         │
└─────────────────────────────────────────────┘
                    ▲
                    │ inherits
┌─────────────────────────────────────────────┐
│ FractalDataWorks.UI.Web.Abstractions        │
│ - WebComponentBase<TSelf, TModel>           │
│ - ComponentMetadata + JSON serialization    │
│ - IJavaScriptInterop interface              │
└─────────────────────────────────────────────┘
                    ▲
                    │ inherits
┌─────────────────────────────────────────────┐
│ FractalDataWorks.UI.Components.Blazor      │  ← THIS PACKAGE
│ - BlazorComponent<TSelf, TModel>            │
│ - RenderTreeBuilder rendering               │
│ - Primitive components (inputs)             │
│ - TypeCollection dropdowns                  │
│ - Collection displays                       │
└─────────────────────────────────────────────┘

Core Components

BlazorComponent<TSelf, TModel>

Foundation for all Blazor UI components. Inherits from WebComponentBase and implements IComponent for Blazor lifecycle.

From BlazorComponent.cs:18-123:

public abstract class BlazorComponent<TSelf, TModel> : WebComponentBase<TSelf, TModel>, IComponent
    where TSelf : BlazorComponent<TSelf, TModel>
{
    private RenderHandle _renderHandle;
    private bool _initialized;

    /// <summary>
    /// Injected IJSRuntime for JavaScript interop.
    /// </summary>
    [Inject] protected IJSRuntime? BlazorJSRuntime { get; set; }

    /// <summary>
    /// Cascading theme configuration.
    /// </summary>
    [CascadingParameter] protected UIThemeConfiguration? Theme { get; set; }

    /// <summary>
    /// Cascading EditContext for validation.
    /// </summary>
    [CascadingParameter] protected EditContext? EditContext { get; set; }

    /// <summary>
    /// Attaches this component to the render tree.
    /// </summary>
    void IComponent.Attach(RenderHandle renderHandle)
    {
        _renderHandle = renderHandle;
    }

    /// <summary>
    /// Triggers a re-render of this component.
    /// </summary>
    protected void StateHasChanged()
    {
        _renderHandle.Render(BuildRenderTree);
    }

    /// <summary>
    /// Builds the render tree for this component.
    /// Override to provide custom rendering logic.
    /// </summary>
    protected abstract void BuildRenderTree(RenderTreeBuilder builder);

    /// <summary>
    /// Gets CSS class for this component based on metadata and theme.
    /// </summary>
    protected virtual string GetCssClass()
    {
        var cssClass = $"component {typeof(TSelf).Name.ToLowerInvariant()}";

        if (Theme?.CustomProperties.TryGetValue("ComponentClass", out var themeClass) == true)
        {
            cssClass += $" {themeClass}";
        }

        return cssClass;
    }
}

Blazor-Specific Features:

  • Implements IComponent for Blazor lifecycle
  • [Inject] for dependency injection
  • [CascadingParameter] for theme/config
  • BuildRenderTree() for rendering
  • StateHasChanged() for UI updates
  • EventCallback<T> for change notifications

BlazorPropertyComponent<TSelf, TProperty>

Base for property-level Blazor components.

From BlazorPropertyComponent.cs:14-69:

public abstract class BlazorPropertyComponent<TSelf, TProperty> : PropertyComponent<TSelf, TProperty>, IComponent
    where TSelf : BlazorPropertyComponent<TSelf, TProperty>
{
    private RenderHandle _renderHandle;
    private bool _initialized;

    /// <summary>
    /// Cascading theme configuration.
    /// </summary>
    [CascadingParameter] protected UIThemeConfiguration? Theme { get; set; }

    /// <summary>
    /// Attaches this component to the render tree.
    /// </summary>
    void IComponent.Attach(RenderHandle renderHandle)
    {
        _renderHandle = renderHandle;
    }

    /// <summary>
    /// Sets parameters from parent component.
    /// </summary>
    async Task IComponent.SetParametersAsync(ParameterView parameters)
    {
        parameters.SetParameterProperties(this);

        if (!_initialized)
        {
            await OnInitialized();
            _initialized = true;
        }

        StateHasChanged();
    }

    /// <summary>
    /// Called when component is initialized.
    /// </summary>
    protected virtual Task OnInitialized()
    {
        return Task.CompletedTask;
    }

    /// <summary>
    /// Triggers a re-render.
    /// </summary>
    protected void StateHasChanged()
    {
        _renderHandle.Render(BuildRenderTree);
    }

    /// <summary>
    /// Builds the render tree for this property component.
    /// </summary>
    protected abstract void BuildRenderTree(RenderTreeBuilder builder);
}

Primitive Components

Input primitive components with Blazor two-way binding support.

TextInput<TValue>

Single-line text input component.

From Primitives/TextInput.razor.cs:12-63:

public partial class TextInput<TValue>
{
    [Parameter] public TValue? Value { get; set; }
    [Parameter] public EventCallback<TValue> ValueChanged { get; set; }
    [Parameter] public string? Label { get; set; }
    [Parameter] public string? HelpText { get; set; }
    [Parameter] public string? Placeholder { get; set; }
    [Parameter] public int MaxLength { get; set; } = 255;
    [Parameter] public bool ReadOnly { get; set; }
    [Parameter] public bool Required { get; set; }
}

Usage:

<TextInput TValue="string"
    @bind-Value="model.Name"
    Label="Name"
    Placeholder="Enter your name"
    MaxLength="100" />

NumericInput<TValue>

Numeric input for int, decimal, double, or float values.

From Primitives/NumericInput.razor.cs:12-63:

public partial class NumericInput<TValue>
{
    [Parameter] public TValue? Value { get; set; }
    [Parameter] public EventCallback<TValue> ValueChanged { get; set; }
    [Parameter] public string? Label { get; set; }
    [Parameter] public string? HelpText { get; set; }
    [Parameter] public TValue? Min { get; set; }
    [Parameter] public TValue? Max { get; set; }
    [Parameter] public TValue? Step { get; set; }
    [Parameter] public bool ReadOnly { get; set; }
}

Usage:

<NumericInput TValue="int"
    @bind-Value="model.Port"
    Label="SMTP Port"
    Min="1"
    Max="65535" />

Switch

Boolean toggle component.

From Primitives/Switch.razor.cs:9-44:

public partial class Switch
{
    [Parameter] public bool Value { get; set; }
    [Parameter] public EventCallback<bool> ValueChanged { get; set; }
    [Parameter] public string? Label { get; set; }
    [Parameter] public string? HelpText { get; set; }
    [Parameter] public bool ReadOnly { get; set; }
}

Usage:

<Switch
    @bind-Value="model.IsEnabled"
    Label="Enable Feature"
    HelpText="Toggle to enable or disable" />

DateTimePicker

Date, time, or datetime selection. Uses IDateTimePickerMode TypeCollection for mode selection.

From Primitives/DateTimePicker.razor.cs:12-77:

public partial class DateTimePicker
{
    [Parameter] public DateTime? Value { get; set; }
    [Parameter] public EventCallback<DateTime?> ValueChanged { get; set; }
    [Parameter] public string? Label { get; set; }
    [Parameter] public string? HelpText { get; set; }
    [Parameter] public bool ReadOnly { get; set; }
    [Parameter] public IDateTimePickerMode? Mode { get; set; }
    [Parameter] public string? Format { get; set; }
}

Usage:

<DateTimePicker
    @bind-Value="model.CreatedAt"
    Label="Created Date"
    Mode="DateTimePickerModes.DateTime"
    Format="yyyy-MM-dd HH:mm" />

TextArea

Multi-line text input.

From Primitives/TextArea.razor.cs:9-59:

public partial class TextArea
{
    [Parameter] public string? Value { get; set; }
    [Parameter] public EventCallback<string> ValueChanged { get; set; }
    [Parameter] public string? Label { get; set; }
    [Parameter] public string? HelpText { get; set; }
    [Parameter] public string? Placeholder { get; set; }
    [Parameter] public int Rows { get; set; } = 5;
    [Parameter] public int MaxLength { get; set; } = 2000;
    [Parameter] public bool ReadOnly { get; set; }
}

Usage:

<TextArea
    @bind-Value="model.Description"
    Label="Description"
    Rows="5"
    MaxLength="1000" />

TypeCollectionDropdown<TCollection, TOption>

Dropdown for selecting TypeCollection options.

From Primitives/TypeCollectionDropdown.razor.cs:17-107:

public partial class TypeCollectionDropdown<TCollection, TOption>
    where TCollection : TypeCollectionBase<TOption, TOption>
    where TOption : class, ITypeOption
{
    [Parameter] public int SelectedId { get; set; }
    [Parameter] public EventCallback<int> SelectedIdChanged { get; set; }
    [Parameter] public string? Label { get; set; }
    [Parameter] public string? HelpText { get; set; }
    [Parameter] public string? Placeholder { get; set; } = "-- Select --";
    [Parameter] public bool ReadOnly { get; set; }
}

Usage:

<TypeCollectionDropdown
    TCollection="ConnectionTypes"
    TOption="IConnectionType"
    @bind-SelectedId="model.ConnectionTypeId"
    Label="Connection Type"
    Placeholder="-- Select Connection Type --" />

Features:

  • Auto-discovers all TypeOptions in collection via reflection
  • Displays Name property from TypeOptions
  • Two-way binding to option ID
  • Supports read-only mode

ConfigurationCollection<TModel, TComponent>

Displays collections of nested configuration objects.

From Primitives/ConfigurationCollection.razor.cs:20-131:

public partial class ConfigurationCollection<TModel, TComponent>
    where TComponent : ComponentBase<TComponent, TModel>
{
    [Parameter] public List<TModel>? Items { get; set; }
    [Parameter] public EventCallback<List<TModel>> ItemsChanged { get; set; }
    [Parameter] public ICollectionDisplayMode? DisplayMode { get; set; }
    [Parameter] public bool AllowAdd { get; set; } = true;
    [Parameter] public bool AllowRemove { get; set; } = true;
    [Parameter] public bool AllowReorder { get; set; } = false;
    [Parameter] public string? AddButtonText { get; set; }
    [Parameter] public Func<TModel>? ItemFactory { get; set; }
}

Usage:

<ConfigurationCollection
    TModel="SmtpServerConfiguration"
    TComponent="SmtpServerConfigurationComponent"
    @bind-Items="model.SmtpServers"
    DisplayMode="CollectionDisplayModes.Accordion"
    AllowAdd="true"
    AllowRemove="true"
    AddButtonText="Add SMTP Server" />

Display Modes (from CollectionDisplayModes TypeCollection in UI.Abstractions):

  • Accordion - Collapsible panels (default)
  • Tabs - Tab navigation
  • List - Flat list
  • Grid - Card grid
  • Tree - Hierarchical tree

JavaScript Interop

Blazor-specific implementation of IJavaScriptInterop.

From BlazorJavaScriptInterop.cs:11-35:

public sealed class BlazorJavaScriptInterop : IJavaScriptInterop
{
    private readonly IJSRuntime _jsRuntime;

    public BlazorJavaScriptInterop(IJSRuntime jsRuntime)
    {
        _jsRuntime = jsRuntime;
    }

    public async Task<T> Invoke<T>(string identifier, params object[] args)
    {
        return await _jsRuntime.InvokeAsync<T>(identifier, args);
    }

    public async Task InvokeVoid(string identifier, params object[] args)
    {
        await _jsRuntime.InvokeVoidAsync(identifier, args);
    }
}

The IJavaScriptInterop interface (from UI.Web.Abstractions) defines Invoke<T> and InvokeVoid methods that this class implements using Blazor's IJSRuntime.

Usage in components:

// JSInterop is set automatically in BlazorComponent.OnInitialized()
if (JSInterop != null)
{
    await JSInterop.InvokeVoid("initializeComponent", elementId);
}

Theme System

Cascading theme configuration for consistent styling.

From Theme/UIThemeConfiguration.cs:9-55:

public class UIThemeConfiguration
{
    public UIColorScheme Colors { get; set; } = UIColorScheme.Dark;
    public string FontFamily { get; set; } = "Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif";
    public string FontSize { get; set; } = "14px";
    public string LineHeight { get; set; } = "1.5";
    public string BorderRadius { get; set; } = "4px";
    public string SpacingUnit { get; set; } = "8px";
    public IDictionary<string, string> CustomProperties { get; set; } = new Dictionary<string, string>();

    public static UIThemeConfiguration DarkTheme => new() { Colors = UIColorScheme.Dark };
    public static UIThemeConfiguration LightTheme => new() { Colors = UIColorScheme.Light };
}

public class UIColorScheme
{
    public string Primary { get; set; } = "#3b82f6";
    public string Secondary { get; set; } = "#64748b";
    public string Success { get; set; } = "#22c55e";
    public string Error { get; set; } = "#ef4444";
    public string Warning { get; set; } = "#eab308";
    public string Info { get; set; } = "#0ea5e9";
    public string Foreground { get; set; } = "#f8fafc";
    public string Background { get; set; } = "#0f172a";
    public string Muted { get; set; } = "#64748b";
    public string Border { get; set; } = "#334155";

    public static UIColorScheme Dark => new() { /* dark colors */ };
    public static UIColorScheme Light => new() { /* light colors */ };
}

Usage in Component Tree:

<CascadingValue Value="@Theme">
    <MyConfigurationComponent @bind-Value="@Config" />
</CascadingValue>

@code {
    private UIThemeConfiguration Theme { get; set; } = UIThemeConfiguration.DarkTheme;
}

Components access theme via [CascadingParameter] as shown in BlazorComponent and BlazorPropertyComponent.

Validation Integration

Blazor components integrate with DataAnnotations and EditContext. The BlazorComponent base class includes a cascading EditContext parameter:

From BlazorComponent.cs:36-37:

/// <summary>
/// Cascading EditContext for validation.
/// </summary>
[CascadingParameter] protected EditContext? EditContext { get; set; }

Components can subscribe to validation events in their OnInitialized method to integrate with Blazor's validation system.

Source Generator Integration

Source generators (from FractalDataWorks.Configuration.UI.SourceGenerators) create Blazor components automatically from [ManagedConfiguration] classes. The generators produce components that:

  • Inherit from BlazorComponent<TSelf, TModel>
  • Create nested BlazorPropertyComponent classes for each property
  • Use the appropriate primitive component based on property type
  • Handle two-way binding and change notifications

Type Mapping:

Property Type Component
string TextInput<string>
int, long, decimal, double, float NumericInput<T>
bool Switch
DateTime, DateTime? DateTimePicker
string with [MaxLength > 100] TextArea
Properties ending in TypeId TypeCollectionDropdown<TCollection, TOption>
List<T>, IList<T> ConfigurationCollection<T, TComponent>

See Configuration.UI.SourceGenerators for generator documentation.

Styling

Components apply CSS classes based on component type. The GetCssClass() method in BlazorComponent generates class names:

From BlazorComponent.cs:112-122:

protected virtual string GetCssClass()
{
    var cssClass = $"component {typeof(TSelf).Name.ToLowerInvariant()}";

    if (Theme?.CustomProperties.TryGetValue("ComponentClass", out var themeClass) == true)
    {
        cssClass += $" {themeClass}";
    }

    return cssClass;
}

Performance

RenderTreeBuilder

  • Sequence numbers must be stable for Blazor's diffing algorithm
  • Use ShouldRender() to prevent unnecessary re-renders

From BlazorComponent.cs:98-101:

protected virtual bool ShouldRender()
{
    return true;
}

Override this method to implement custom render optimization logic.

Best Practices

  1. Use source generators for configuration components
  2. Cascade themes via CascadingValue for consistent styling
  3. Leverage EditContext for Blazor validation integration
  4. Use stable sequence numbers in RenderTreeBuilder
  5. Use @bind-Value for two-way binding
  6. Use @key for collections to prevent render issues
  7. Do not block on Task in BuildRenderTree

See Also

Product Compatible and additional computed target framework versions.
.NET 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. 
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
0.1.0-preview.11 4 1/12/2026
0.1.0-preview.10 17 1/12/2026
0.1.0-preview.9 35 1/9/2026
0.1.0-preview.7 96 1/7/2026