FractalDataWorks.UI.Components.Blazor
0.1.0-preview.11
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
<PackageReference Include="FractalDataWorks.UI.Components.Blazor" Version="0.1.0-preview.11" />
<PackageVersion Include="FractalDataWorks.UI.Components.Blazor" Version="0.1.0-preview.11" />
<PackageReference Include="FractalDataWorks.UI.Components.Blazor" />
paket add FractalDataWorks.UI.Components.Blazor --version 0.1.0-preview.11
#r "nuget: FractalDataWorks.UI.Components.Blazor, 0.1.0-preview.11"
#:package FractalDataWorks.UI.Components.Blazor@0.1.0-preview.11
#addin nuget:?package=FractalDataWorks.UI.Components.Blazor&version=0.1.0-preview.11&prerelease
#tool nuget:?package=FractalDataWorks.UI.Components.Blazor&version=0.1.0-preview.11&prerelease
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
IJSRuntimeadapter - 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
IComponentfor Blazor lifecycle [Inject]for dependency injection[CascadingParameter]for theme/configBuildRenderTree()for renderingStateHasChanged()for UI updatesEventCallback<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
BlazorPropertyComponentclasses 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
- Use source generators for configuration components
- Cascade themes via
CascadingValuefor consistent styling - Leverage
EditContextfor Blazor validation integration - Use stable sequence numbers in RenderTreeBuilder
- Use
@bind-Valuefor two-way binding - Use
@keyfor collections to prevent render issues - Do not block on
TaskinBuildRenderTree
See Also
- UI.Abstractions - CRTP base classes
- UI.Web.Abstractions - Web component metadata
- UI.Components.TUI - Terminal UI implementation
- Configuration.UI.SourceGenerators - Component generation
- Blazor Documentation - Official Blazor docs
| Product | Versions 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. |
-
net10.0
- Excubo.Blazor.Canvas (>= 3.2.96)
- FastGenericNew.SourceGenerator (>= 3.3.1)
- FractalDataWorks.UI.Abstractions (>= 0.1.0-preview.11)
- FractalDataWorks.UI.Web.Abstractions (>= 0.1.0-preview.11)
- Microsoft.AspNetCore.Components (>= 10.0.0)
- Microsoft.AspNetCore.Components.Web (>= 10.0.0)
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 |