FluTable 0.2.0
dotnet add package FluTable --version 0.2.0
NuGet\Install-Package FluTable -Version 0.2.0
<PackageReference Include="FluTable" Version="0.2.0" />
<PackageVersion Include="FluTable" Version="0.2.0" />
<PackageReference Include="FluTable" />
paket add FluTable --version 0.2.0
#r "nuget: FluTable, 0.2.0"
#:package FluTable@0.2.0
#addin nuget:?package=FluTable&version=0.2.0
#tool nuget:?package=FluTable&version=0.2.0
FluTable
A zero-dependency generic Blazor data grid that maintains 100% table width under column resize, supports double-click auto-fit, and exposes every cell and header through RenderFragment templates.
Built because FluentDataGrid locks column widths to absolute pixels after the first drag — there's no way to stay at 100% of the container without patching its private JS state. FluTable is percentage-based from the ground up.
Features
- Percentage-based column resize — total table width always equals 100%, no horizontal scroll surprises
- Double-click auto-fit — reads the natural content width of every cell in a column and sizes to it
- Column sorting — set
SortByfor click-to-sort headers (asc/desc/none cycle,▲/▼indicator,aria-sort), or pass a customIComparer<TItem> EmptyContentslot — supply aRenderFragmentto render whenItemsis empty, no need to wrap the grid in@if- Overflow tooltip — set
OverflowTooltip="true"to auto-attach atitleattribute whenever a cell's text is actually truncated - Generic
<TItem>— works with any model, no reflection, no attributes CellTemplateandHeaderTemplate— put any Razor markup in cells and headers (icons, badges, buttons, subtitles)- Multi-line cells — set
Multiline="true"forwhite-space: pre-wrapcells - Built-in row actions — hover-reveal insert-above, insert-below, delete icons with an optional
NewRowFactory - CSS variable theming — customize font size, cell padding, and wrapper margin from the consuming app
- Zero runtime dependencies — only
Microsoft.AspNetCore.App, no FluentUI, no MudBlazor, no third-party icon packs - .NET 10, Blazor Server + WebAssembly
Installation
dotnet add package FluTable
Then add the script reference to your App.razor (or _Host.cshtml, wherever your root layout lives):
<script type="module" src="@Assets["_content/FluTable/app-grid.js"]"></script>
The script self-initializes via MutationObserver — no JS interop call required from Blazor. It watches for [data-ag-table] elements and wires resize handles automatically, so it works with Server, WebAssembly, and enhanced navigation without manual setup.
Important: pages that render a
<FluTable>must use an interactive render mode (@rendermode InteractiveServerorInteractiveWebAssembly) because the resize handles depend on JS interop.
Usage
@page "/meetings"
@rendermode InteractiveServer
<FluTable TItem="Meeting" Items="@_rows" NewRowFactory="@(() => new Meeting())">
<FluTableColumn TItem="Meeting" Header="" Width="40px" Resizable="false">
<CellTemplate>
<input type="checkbox" @bind="context.IsSelected" />
</CellTemplate>
</FluTableColumn>
<FluTableColumn TItem="Meeting" Header="Title">
<CellTemplate>@context.Title</CellTemplate>
</FluTableColumn>
<FluTableColumn TItem="Meeting" Header="Notes" Multiline="true">
<CellTemplate>@context.Notes</CellTemplate>
</FluTableColumn>
<FluTableColumn TItem="Meeting" Header="Date">
<CellTemplate>@context.Date.ToString("yyyy-MM-dd")</CellTemplate>
</FluTableColumn>
</FluTable>
@code {
private List<Meeting> _rows = new();
private sealed class Meeting
{
public bool IsSelected { get; set; }
public string Title { get; set; } = "";
public string Notes { get; set; } = "";
public DateTime Date { get; set; }
}
}
Rich header templates
Any Razor markup works inside a HeaderTemplate:
<FluTableColumn TItem="Meeting" Header="Title">
<HeaderTemplate>
<strong>Title</strong>
<span class="badge">@_rows.Count</span>
</HeaderTemplate>
<CellTemplate>@context.Title</CellTemplate>
</FluTableColumn>
Interaction model
| Gesture | Result |
|---|---|
| Drag resize handle | Continuously resize the column; takes width from the next flex column to compensate |
| Double-click resize handle | Auto-fit the column to its widest content (header + body cells) |
Hover row (with ShowRowActions="true") |
Reveal insert-above, insert-below, and delete icons on the right |
The last flex column deliberately has no resize handle — there's no right-hand neighbor to donate from, so dragging/double-clicking would be a no-op.
FluTable parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
Items |
List<TItem> |
required | The row data. Mutated in-place by row actions when NewRowFactory is set. |
ChildContent |
RenderFragment? |
— | Column definitions (<FluTableColumn> children). |
EmptyContent |
RenderFragment? |
— | Rendered inside a full-width row when Items is empty. Headers still render. |
NewRowFactory |
Func<TItem>? |
null |
When set, row-action insert buttons call this to produce new rows and mutate Items directly. |
ShowRowActions |
bool |
true |
Toggles the hover-reveal insert/delete buttons on each row. |
OnInsertAt |
EventCallback<int> |
— | Fired when NewRowFactory is null and the user clicks insert above/below. Receives the target index. |
OnDeleteRow |
EventCallback<int> |
— | Fired after a row is deleted. |
FluTableColumn parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
Header |
string |
required | Plain-text header. Overridden by HeaderTemplate if set. |
Width |
string? |
null |
e.g. "40px" or "20%". If null, flex columns share the remaining space equally on first render. |
Resizable |
bool |
true |
false marks the column as fixed — no resize handle, width not included in resize math. |
Multiline |
bool |
false |
Applies white-space: pre-wrap; word-break: break-word to cells in this column. |
OverflowTooltip |
bool |
false |
When true, cells get a title attribute automatically whenever their rendered text is truncated (scrollWidth > clientWidth). Skipped for multiline cells. |
SortBy |
Func<TItem, object?>? |
null |
Makes the column sortable via header click. Value returned is compared naturally (IComparable / string fallback). |
Comparer |
IComparer<TItem>? |
null |
Custom row comparer for domain-specific ordering (e.g. severity rank). Wins over SortBy when both are set. |
CellTemplate |
RenderFragment<TItem>? |
— | Cell content. context is the row item. |
HeaderTemplate |
RenderFragment? |
— | Custom header markup. |
Sorting
<FluTableColumn TItem="Meeting" Header="Date"
SortBy="@(m => m.Date)">
<CellTemplate>@context.Date.ToString("yyyy-MM-dd")</CellTemplate>
</FluTableColumn>
Clicks cycle ascending → descending → none (source order). Only one column sorts at a time. aria-sort is set on the <th> for accessibility.
Empty state
<FluTable TItem="Device" Items="@devices">
<EmptyContent>
<div class="empty">No devices connected.</div>
</EmptyContent>
<ChildContent>
<FluTableColumn TItem="Device" Header="Name">
<CellTemplate>@context.Name</CellTemplate>
</FluTableColumn>
</ChildContent>
</FluTable>
CSS variable theming
FluTable's internal CSS reads these custom properties with sensible fallbacks. Set them on any ancestor element and they cascade in:
| Variable | Default | Effect |
|---|---|---|
--ag-font-size |
14px |
Body cell font size |
--ag-cell-padding |
4px |
Vertical padding on each body cell |
--ag-margin |
0px |
Outer margin on the grid wrapper |
<div style="--ag-font-size:16px; --ag-cell-padding:6px;">
<FluTable TItem="Meeting" Items="@_rows">...</FluTable>
</div>
The grid also reads FluentUI design tokens (--accent-fill-rest, --neutral-foreground-rest, etc.) when available, falling back to Microsoft-ish defaults otherwise — so it themes nicely in a FluentUI Blazor app without requiring it as a dependency.
Requirements
- .NET 10
- A Blazor host (Server, WebAssembly, or Hybrid) with interactive rendering enabled on the consuming page
License
MIT
| 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
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.