FluTable 0.2.0

dotnet add package FluTable --version 0.2.0
                    
NuGet\Install-Package FluTable -Version 0.2.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="FluTable" Version="0.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FluTable" Version="0.2.0" />
                    
Directory.Packages.props
<PackageReference Include="FluTable" />
                    
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 FluTable --version 0.2.0
                    
#r "nuget: FluTable, 0.2.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 FluTable@0.2.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=FluTable&version=0.2.0
                    
Install as a Cake Addin
#tool nuget:?package=FluTable&version=0.2.0
                    
Install as a Cake Tool

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 SortBy for click-to-sort headers (asc/desc/none cycle, / indicator, aria-sort), or pass a custom IComparer<TItem>
  • EmptyContent slot — supply a RenderFragment to render when Items is empty, no need to wrap the grid in @if
  • Overflow tooltip — set OverflowTooltip="true" to auto-attach a title attribute whenever a cell's text is actually truncated
  • Generic <TItem> — works with any model, no reflection, no attributes
  • CellTemplate and HeaderTemplate — put any Razor markup in cells and headers (icons, badges, buttons, subtitles)
  • Multi-line cells — set Multiline="true" for white-space: pre-wrap cells
  • 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 InteractiveServer or InteractiveWebAssembly) 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 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.
  • 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.

Version Downloads Last Updated
0.2.0 243 4/20/2026
0.1.0 101 4/11/2026