Components

Data Grid

Data grids allow users to quickly scan, sort, compare, and take action on large amounts of data.

Columns

The data grid component provides several column components to define and customize the structure and behavior of your grid. Each column type serves a specific purpose, making it easy to tailor the grid to your needs.

Base

The ColumnBase is the foundational class for all column components in the data grid. It provides common properties and functionality that other column types inherit, serving as the base for custom column implementations.

Template

The TemplateColumn is a flexible column type not bound to any specific model property. It uses arbitrary Razor fragments to supply contents for its cells. It can't infer the column's title or sort order automatically.

Property

The PropertyColumn is used to bind to a specific property of your data model. It automatically displays the value of the bound property for each row. This column infers sorting rules automatically, and uses the property's name as its title if not otherwise set.

Edit

The EditColumn extends the functionality of PropertyColumn by enabling incell editing. This column type is ideal for editable grids where users need to update data directly.

Usage

The data grid component provides a powerful way to display and manage large sets of data.

NameRoleStatus
Alice JohnsonAdministratorActive
Bob SmithEditorInactive
Charlie BrownViewerPending
Diana LeeModeratorActive
<LumexDataGrid Data="@_users">
    <PropertyColumn Property="@(p => p.Name)" />
    <PropertyColumn Property="@(p => p.Role)" />
    <PropertyColumn Property="@(p => p.Status)" />
</LumexDataGrid>

@code {
    private readonly IQueryable<User> _users = new List<User>
    {
        new( "Alice Johnson", "Administrator", "Active" ),
        new( "Bob Smith", "Editor", "Inactive" ),
        new( "Charlie Brown", "Viewer", "Pending" ),
        new( "Diana Lee", "Moderator", "Active" )
    }.AsQueryable();

    private record User( string Name, string Role, string Status );
}

Loading

Indicate a loading state while data is being fetched, displaying a loading message or spinner.

NameRoleStatus
Alice JohnsonAdministratorActive
Bob SmithEditorInactive
Charlie BrownViewerPending
Diana LeeModeratorActive
Loading...
<LumexDataGrid Data="@_users" Loading="@true">
    <PropertyColumn Property="@(p => p.Name)" />
    <PropertyColumn Property="@(p => p.Role)" />
    <PropertyColumn Property="@(p => p.Status)" />
</LumexDataGrid>

@code {
    private readonly IQueryable<User> _users = new List<User>
    {
        new( "Alice Johnson", "Administrator", "Active" ),
        new( "Bob Smith", "Editor", "Inactive" ),
        new( "Charlie Brown", "Viewer", "Pending" ),
        new( "Diana Lee", "Moderator", "Active" )
    }.AsQueryable();

    private record User( string Name, string Role, string Status );
}

Hoverable

Enable hover effects on rows to enhance visual feedback during interaction.

NameRoleStatus
Alice JohnsonAdministratorActive
Bob SmithEditorInactive
Charlie BrownViewerPending
Diana LeeModeratorActive
<LumexDataGrid Data="@_users" Hoverable="@true">
    <PropertyColumn Property="@(p => p.Name)" />
    <PropertyColumn Property="@(p => p.Role)" />
    <PropertyColumn Property="@(p => p.Status)" />
</LumexDataGrid>

@code {
    private readonly IQueryable<User> _users = new List<User>
    {
        new( "Alice Johnson", "Administrator", "Active" ),
        new( "Bob Smith", "Editor", "Inactive" ),
        new( "Charlie Brown", "Viewer", "Pending" ),
        new( "Diana Lee", "Moderator", "Active" )
    }.AsQueryable();

    private record User( string Name, string Role, string Status );
}

Striped

Alternate row colors for better readability in large datasets.

NameRoleStatus
Alice JohnsonAdministratorActive
Bob SmithEditorInactive
Charlie BrownViewerPending
Diana LeeModeratorActive
<LumexDataGrid Data="@_users" Striped="@true">
    <PropertyColumn Property="@(p => p.Name)" />
    <PropertyColumn Property="@(p => p.Role)" />
    <PropertyColumn Property="@(p => p.Status)" />
</LumexDataGrid>

@code {
    private readonly IQueryable<User> _users = new List<User>
    {
        new( "Alice Johnson", "Administrator", "Active" ),
        new( "Bob Smith", "Editor", "Inactive" ),
        new( "Charlie Brown", "Viewer", "Pending" ),
        new( "Diana Lee", "Moderator", "Active" )
    }.AsQueryable();

    private record User( string Name, string Role, string Status );
}

Keep the header fixed at the top as users scroll through data.

NameRoleStatus
Alice JohnsonAdminActive
Bob SmithModeratorInactive
Carol BrownUserPending
David WilsonAdminActive
Eve DavisUserBanned
Frank MillerModeratorActive
Grace HallUserActive
Hank LeeAdminPending
Ivy ClarkUserInactive
Jack WhiteModeratorBanned
Kathy YoungAdminActive
Leo KingUserPending
Mona BakerModeratorActive
Nate AdamsUserBanned
Olive MooreAdminInactive
Paul HarrisUserActive
Quincy EvansModeratorPending
Rita TorresAdminBanned
Sam RamirezUserInactive
Tina PerezModeratorActive
<LumexDataGrid Data="@_users"
               StickyHeader="@true"
               Class="max-h-64">
    <PropertyColumn Property="@(p => p.Name)" />
    <PropertyColumn Property="@(p => p.Role)" />
    <PropertyColumn Property="@(p => p.Status)" />
</LumexDataGrid>

@code {
    private readonly IQueryable<User> _users = new List<User>
    {
        new("Alice Johnson", "Admin", "Active"),
        new("Bob Smith", "Moderator", "Inactive"),
        new("Carol Brown", "User", "Pending"),
        new("David Wilson", "Admin", "Active"),
        new("Eve Davis", "User", "Banned"),
        new("Frank Miller", "Moderator", "Active"),
        new("Grace Hall", "User", "Active"),
        new("Hank Lee", "Admin", "Pending"),
        new("Ivy Clark", "User", "Inactive"),
        new("Jack White", "Moderator", "Banned"),
        new("Kathy Young", "Admin", "Active"),
        new("Leo King", "User", "Pending"),
        new("Mona Baker", "Moderator", "Active"),
        new("Nate Adams", "User", "Banned"),
        new("Olive Moore", "Admin", "Inactive"),
        new("Paul Harris", "User", "Active"),
        new("Quincy Evans", "Moderator", "Pending"),
        new("Rita Torres", "Admin", "Banned"),
        new("Sam Ramirez", "User", "Inactive"),
        new("Tina Perez", "Moderator", "Active")
    }.AsQueryable();

    private record User( string Name, string Role, string Status );
}

Radius

Adjust border radius for a more rounded or square appearance.

NameRoleStatus
Alice JohnsonAdministratorActive
Bob SmithEditorInactive
Charlie BrownViewerPending
Diana LeeModeratorActive
<LumexDataGrid Data="@_users" Radius="@_radius">
    <PropertyColumn Property="@(p => p.Name)" />
    <PropertyColumn Property="@(p => p.Role)" />
    <PropertyColumn Property="@(p => p.Status)" />
</LumexDataGrid>

<fieldset class="flex gap-4">
    @foreach( var radius in _radiuses )
    {
        var value = radius.ToString();

        <div class="flex gap-1">
            <input type="radio"
                   id="@($"radius-{value.ToLower()}")"
                   name="radius"
                   @bind:event="onchange"
                   @bind:get="@radius"
                   @bind:set="@OnRadiusSelect" />
            <label for="@($"radius-{value.ToLower()}")">@value</label>
        </div>
    }
</fieldset>

@code {
    private readonly IQueryable<User> _users = new List<User>
    {
        new( "Alice Johnson", "Administrator", "Active" ),
        new( "Bob Smith", "Editor", "Inactive" ),
        new( "Charlie Brown", "Viewer", "Pending" ),
        new( "Diana Lee", "Moderator", "Active" )
    }.AsQueryable();

    private readonly Radius[] _radiuses = [
        Radius.None,
        Radius.Small,
        Radius.Medium,
        Radius.Large
    ];

    private Radius _radius = Radius.Small;

    private void OnRadiusSelect( Radius value )
    {
        _radius = value;
    }

    private record User( string Name, string Role, string Status );
}

Shadows

Add shadows for an elevated, visually distinct look.

NameRoleStatus
Alice JohnsonAdministratorActive
Bob SmithEditorInactive
Charlie BrownViewerPending
Diana LeeModeratorActive
<LumexDataGrid Data="@_users" Shadow="@_shadow">
    <PropertyColumn Property="@(p => p.Name)" />
    <PropertyColumn Property="@(p => p.Role)" />
    <PropertyColumn Property="@(p => p.Status)" />
</LumexDataGrid>

<fieldset class="flex gap-4">
    @foreach( var shadow in _shadows )
    {
        var value = shadow.ToString();

        <div class="flex gap-1">
            <input type="radio"
                   id="@($"shadow-{value.ToLower()}")" 
                   name="shadow"
                   @bind:event="onchange"
                   @bind:get="@shadow"
                   @bind:set="@OnShadowSelect" />
            <label for="@($"shadow-{value.ToLower()}")">@value</label>
        </div>
    }
</fieldset>

@code {
    private readonly IQueryable<User> _users = new List<User>
    {
        new( "Alice Johnson", "Administrator", "Active" ),
        new( "Bob Smith", "Editor", "Inactive" ),
        new( "Charlie Brown", "Viewer", "Pending" ),
        new( "Diana Lee", "Moderator", "Active" )
    }.AsQueryable();

    private readonly Shadow[] _shadows = [
        Shadow.None,
        Shadow.Small,
        Shadow.Medium,
        Shadow.Large
    ];

    private Shadow _shadow = Shadow.Small;

    private void OnShadowSelect( Shadow value )
    {
        _shadow = value;
    }

    private record User( string Name, string Role, string Status );
}

Table Layout

Choose between different table layouts, such as Fixed or Auto.

NameRoleStatus
Alice JohnsonAdministratorActive
Bob SmithEditorInactive
Charlie BrownViewerPending
Diana LeeModeratorActive
<LumexDataGrid Data="@_users" Layout="@Layout.Fixed">
    <PropertyColumn Property="@(p => p.Name)" />
    <PropertyColumn Property="@(p => p.Role)" />
    <PropertyColumn Property="@(p => p.Status)" />
</LumexDataGrid>

@code {
    private readonly IQueryable<User> _users = new List<User>
    {
        new( "Alice Johnson", "Administrator", "Active" ),
        new( "Bob Smith", "Editor", "Inactive" ),
        new( "Charlie Brown", "Viewer", "Pending" ),
        new( "Diana Lee", "Moderator", "Active" )
    }.AsQueryable();

    private record User( string Name, string Role, string Status );
}

Disabled Items

Mark specific rows as disabled to prevent interactions on them.

NameRoleStatus
Alice JohnsonAdministratorActive
Bob SmithEditorInactive
Charlie BrownViewerPending
Diana LeeModeratorActive
<LumexDataGrid Data="@_users"
               DisabledItems="@DisabledUsers">
    <PropertyColumn Property="@(p => p.Name)" />
    <PropertyColumn Property="@(p => p.Role)" />
    <PropertyColumn Property="@(p => p.Status)" />
</LumexDataGrid>

@code {
    private readonly IQueryable<User> _users = new List<User>
    {
        new( "Alice Johnson", "Administrator", "Active" ),
        new( "Bob Smith", "Editor", "Inactive" ),
        new( "Charlie Brown", "Viewer", "Pending" ),
        new( "Diana Lee", "Moderator", "Active" )
    }.AsQueryable();

    private ICollection<User> DisabledUsers =>
        _users.Where( u => u.Status == "Inactive" ).ToList();

    private record User( string Name, string Role, string Status );
}

Selected Items

Single Selection

The data grid supports single selection mode, allowing users to select one row at a time.

NameRoleStatus
Alice JohnsonAdministratorActive
Bob SmithEditorInactive
Charlie BrownViewerPending
Diana LeeModeratorActive
<LumexDataGrid Data="@_users"
               Color="@_color"
               SelectionMode="@SelectionMode.Single"
               @bind-SelectedItems="@_selectedUsers">
    <PropertyColumn Property="@(p => p.Name)" />
    <PropertyColumn Property="@(p => p.Role)" />
    <PropertyColumn Property="@(p => p.Status)" />
</LumexDataGrid>

<fieldset class="flex gap-4">
    @foreach( var color in _colors )
    {
        var value = color.ToString();

        <div class="flex gap-1">
            <input type="radio"
                   id="@($"single-sel-{value.ToLower()}")"
                   name="color"
                   @bind:event="onchange"
                   @bind:get="@color"
                   @bind:set="@OnColorSelect" />
            <label for="@($"single-sel-{value.ToLower()}")">@value</label>
        </div>
    }
</fieldset>

@if( _selectedUsers.Count > 0 )
{
    <div class="text-small">
        <p>Selected user:</p>
        <ul>
            @foreach( var user in _selectedUsers )
            {
                <li>@user</li>
            }
        </ul>
    </div>
}

@code {
    private readonly IQueryable<User> _users = new List<User>
    {
        new( "Alice Johnson", "Administrator", "Active" ),
        new( "Bob Smith", "Editor", "Inactive" ),
        new( "Charlie Brown", "Viewer", "Pending" ),
        new( "Diana Lee", "Moderator", "Active" )
    }.AsQueryable();

    private readonly ThemeColor[] _colors = [
        ThemeColor.Default,
        ThemeColor.Primary,
        ThemeColor.Secondary,
        ThemeColor.Success,
        ThemeColor.Warning,
        ThemeColor.Danger,
        ThemeColor.Info
    ];

    private ThemeColor _color = ThemeColor.Default;
    private ICollection<User> _selectedUsers = [];

    private void OnColorSelect( ThemeColor value )
    {
        _color = value;
    }

    private record User( string Name, string Role, string Status );
}

Multiple Selection

The data grid also supports multiple selection mode, enabling users to select multiple rows simultaneously.

NameRoleStatus
Alice JohnsonAdministratorActive
Bob SmithEditorInactive
Charlie BrownViewerPending
Diana LeeModeratorActive
<LumexDataGrid Data="@_users"
               Color="@_color"
               SelectionMode="@SelectionMode.Multiple"
               @bind-SelectedItems="@_selectedUsers">
    <PropertyColumn Property="@(p => p.Name)" />
    <PropertyColumn Property="@(p => p.Role)" />
    <PropertyColumn Property="@(p => p.Status)" />
</LumexDataGrid>

<fieldset class="flex gap-4">
    @foreach( var color in _colors )
    {
        var value = color.ToString();

        <div class="flex gap-1">
            <input type="radio"
                   id="@($"multiple-sel-{value.ToLower()}")"
                   name="color"
                   @bind:event="onchange"
                   @bind:get="@color"
                   @bind:set="@OnColorSelect" />
            <label for="@($"multiple-sel-{value.ToLower()}")">@value</label>
        </div>
    }
</fieldset>

@if( _selectedUsers.Count > 0 )
{
    <div class="text-small">
        <p>Selected users:</p>
        <ul>
            @foreach( var user in _selectedUsers )
            {
                <li>@user</li>
            }
        </ul>
    </div>
}

@code {
    private readonly IQueryable<User> _users = new List<User>
    {
        new( "Alice Johnson", "Administrator", "Active" ),
        new( "Bob Smith", "Editor", "Inactive" ),
        new( "Charlie Brown", "Viewer", "Pending" ),
        new( "Diana Lee", "Moderator", "Active" )
    }.AsQueryable();

    private readonly ThemeColor[] _colors = [
        ThemeColor.Default,
        ThemeColor.Primary,
        ThemeColor.Secondary,
        ThemeColor.Success,
        ThemeColor.Warning,
        ThemeColor.Danger,
        ThemeColor.Info
    ];

    private ThemeColor _color = ThemeColor.Default;
    private ICollection<User> _selectedUsers = [];

    private void OnColorSelect( ThemeColor value )
    {
        _color = value;
    }

    private record User( string Name, string Role, string Status );
}

A checkbox column automatically appears as the first column for easy row selection, alongside row selection on click.

Editing

Allow incell editing for easy data updates directly within the grid using EditColumn.

NameRoleStatus
Alice JohnsonAdministratorActive
Bob SmithEditorInactive
Charlie BrownViewerPending
Diana LeeModeratorActive
<LumexDataGrid Data="@_users">
    <EditColumn Property="@(p => p.Name)" />
    <EditColumn Property="@(p => p.Role)" />
    <EditColumn Property="@(p => p.Status)" />
</LumexDataGrid>

@code {
    private readonly IQueryable<User> _users = new List<User>
    {
        new( "Alice Johnson", "Administrator", "Active" ),
        new( "Bob Smith", "Editor", "Inactive" ),
        new( "Charlie Brown", "Viewer", "Pending" ),
        new( "Diana Lee", "Moderator", "Active" )
    }.AsQueryable();

    private record User( string Name, string Role, string Status );
}

Sorting

Enable sorting by column headers to arrange data in ascending or descending order.

NameRoleStatus
Alice JohnsonAdministratorActive
Bob SmithEditorInactive
Charlie BrownViewerPending
Diana LeeModeratorActive
<LumexDataGrid Data="@_users">
    <PropertyColumn Property="@(p => p.Name)" Sortable="@true" />
    <PropertyColumn Property="@(p => p.Role)" Sortable="@true" />
    <PropertyColumn Property="@(p => p.Status)" Sortable="@true" />
</LumexDataGrid>

@code {
    private readonly IQueryable<User> _users = new List<User>
    {
        new( "Alice Johnson", "Administrator", "Active" ),
        new( "Bob Smith", "Editor", "Inactive" ),
        new( "Charlie Brown", "Viewer", "Pending" ),
        new( "Diana Lee", "Moderator", "Active" )
    }.AsQueryable();

    private record User( string Name, string Role, string Status );
}

Virtualization

Optimize performance by loading only the rows visible within the viewport.

IDStateCityCompanyStatus

26636 results found

@inject HttpClient HttpClient
@inject NavigationManager NavigationManager

<LumexDataGrid DataSource="@_foodRecallProvider"
               ItemSize="48"
               Virtualize="@true"
               StickyHeader="@true"
               Class="max-h-64">
    <PropertyColumn Property="@(p => p.Event_Id)" Title="ID" />
    <PropertyColumn Property="@(p => p.State)" />
    <PropertyColumn Property="@(p => p.City)" />
    <PropertyColumn Property="@(p => p.Recalling_Firm)" Title="Company" />
    <PropertyColumn Property="@(p => p.Status)" />
</LumexDataGrid>

<p class="text-small">
    <strong>@_resultsCount results found</strong>
</p>

@code {
    private DataSource<FoodRecall>? _foodRecallProvider;
    private int _resultsCount;

    protected override async Task OnInitializedAsync()
    {
        _foodRecallProvider = async req =>
        {
            var url = NavigationManager.GetUriWithQueryParameters( "https://api.fda.gov/food/enforcement.json", new Dictionary<string, object?>
            {
                { "skip", req.StartIndex },
                { "limit", req.Count },
            } );

            var response = await HttpClient.GetFromJsonAsync<FoodRecallQueryResult>( url, req.CancellationToken );
            return DataSourceResult.From(
                items: response!.Results,
                totalItemCount: response!.Meta.Results.Total );
        };

        _resultsCount = ( await HttpClient.GetFromJsonAsync<FoodRecallQueryResult>( "https://api.fda.gov/food/enforcement.json" ) )!.Meta.Results.Total;
    }

    private class FoodRecall
    {
        public string Status { get; set; } = default!;
        public string City { get; set; } = default!;
        public string State { get; set; } = default!;
        public string Recalling_Firm { get; set; } = default!;
        public string Event_Id { get; set; } = default!;
    }

    private class FoodRecallQueryResult
    {
        public FoodRecall[] Results { get; set; } = default!;
        public FoodRecallMeta Meta { get; set; } = default!;

        public class FoodRecallMeta
        {
            public MetaResults Results { get; set; } = default!;

            public record MetaResults( int Skip, int Limit, int Total );
        }
    }
}

For virtualization to work properly and reliably, you must ensure that every row renders with the same known height. Otherwise, scrolling will behave erratically.

Custom Cells

Customize individual column cells to display specific content or styles using the Content parameter.

NameRoleStatus
Alice JohnsonAdministratorActive
Bob SmithEditorInactive
Charlie BrownViewerPending
Diana LeeModeratorActive
<LumexDataGrid Data="@_users">
    <PropertyColumn Property="@(p => p.Name)" />
    <PropertyColumn Property="@(p => p.Role)" />
    <PropertyColumn Property="@(p => p.Status)">
        <Content>
            @{
                var status = context.Status;
                <span class="px-1.5 py-0.5 text-tiny rounded-full @(_statusVariants[status])">@status</span>
            }
        </Content>
    </PropertyColumn>
</LumexDataGrid>

@code {
    private readonly IQueryable<User> _users = new List<User>
    {
        new( "Alice Johnson", "Administrator", "Active" ),
        new( "Bob Smith", "Editor", "Inactive" ),
        new( "Charlie Brown", "Viewer", "Pending" ),
        new( "Diana Lee", "Moderator", "Active" )
    }.AsQueryable();

    private readonly Dictionary<string, string> _statusVariants = new()
        {
            ["Pending"] = "border border-primary-300 bg-primary-50 text-primary",
            ["Active"] = "border border-danger-300 bg-danger-50 text-danger",
            ["Inactive"] = "border border-default-300 bg-default-100 text-default-500"
        };

    private record User( string Name, string Role, string Status );
}

Alternatively, you can provide an arbitrary content to the TemplateColumn, such as action buttons.

NameRoleStatusActions
Alice JohnsonAdministratorActive
Bob SmithEditorInactive
Charlie BrownViewerPending
Diana LeeModeratorActive
<LumexDataGrid Data="@_users">
    <PropertyColumn Property="@(p => p.Name)" />
    <PropertyColumn Property="@(p => p.Role)" />
    <PropertyColumn Property="@(p => p.Status)" />
    <TemplateColumn Title="Actions" Align="@Align.Center">
        <LumexButton Size="@Size.Small"
                     Variant="@Variant.Light"
                     Class="min-w-8 w-8 h-8 px-0">
            <LumexIcon Icon="@Icons.Rounded.MoreVert"
                       Color="@ThemeColor.None"
                       Class="text-default-500" />
        </LumexButton>
    </TemplateColumn>
</LumexDataGrid>

@code {
    private readonly IQueryable<User> _users = new List<User>
    {
        new( "Alice Johnson", "Administrator", "Active" ),
        new( "Bob Smith", "Editor", "Inactive" ),
        new( "Charlie Brown", "Viewer", "Pending" ),
        new( "Diana Lee", "Moderator", "Active" )
    }.AsQueryable();

    private record User( string Name, string Role, string Status );
}

Rendering numerous components or event handlers can affect grid performance. Mitigate this by using pagination or virtualization to reduce the number of components displayed at a time.

Custom Format

The data grid allows you to define custom formatting for column values, enabling you to display data in a specific way, such as formatting dates, numbers, or strings.

NameRoleSalaryStatus
Alice JohnsonAdministrator$3,000Active
Bob SmithEditor$2,000Inactive
Charlie BrownViewer$1,500Pending
Diana LeeModerator$2,500Active
<LumexDataGrid Data="@_users">
    <PropertyColumn Property="@(p => p.Name)" />
    <PropertyColumn Property="@(p => p.Role)" />
    <PropertyColumn Property="@(p => p.Salary)" Format="C0" />
    <PropertyColumn Property="@(p => p.Status)" />
</LumexDataGrid>

@code {
    private readonly IQueryable<User> _users = new List<User>
    {
        new( "Alice Johnson", "Administrator", 3000, "Active" ),
        new( "Bob Smith", "Editor", 2000, "Inactive" ),
        new( "Charlie Brown", "Viewer", 1500, "Pending" ),
        new( "Diana Lee", "Moderator", 2500, "Active" )
    }.AsQueryable();

    private record User( string Name, string Role, decimal Salary, string Status );
}

Dynamic Columns

The data grid supports dynamic columns, allowing you to show or hide columns based on runtime conditions for a flexible and responsive layout.

NameRoleStatus
Alice JohnsonAdministratorActive
Bob SmithEditorInactive
Charlie BrownViewerPending
Diana LeeModeratorActive
Select columns to show
<LumexDataGrid Data="@_users">
    <PropertyColumn Property="@(p => p.Name)" />
    <PropertyColumn Property="@(p => p.Role)" Visible="@_isRoleColShown" />
    <PropertyColumn Property="@(p => p.Status)" Visible="@_isStatusColShown" />
</LumexDataGrid>

<LumexCheckboxGroup Label="Select columns to show"
                    Classes="@(new() { Wrapper = "flex-row" })">
    <LumexCheckbox @bind-Value="@_isRoleColShown">Role</LumexCheckbox>
    <LumexCheckbox @bind-Value="@_isStatusColShown">Status</LumexCheckbox>
</LumexCheckboxGroup>

@code {
    private readonly IQueryable<User> _users = new List<User>
    {
        new( "Alice Johnson", "Administrator", "Active" ),
        new( "Bob Smith", "Editor", "Inactive" ),
        new( "Charlie Brown", "Viewer", "Pending" ),
        new( "Diana Lee", "Moderator", "Active" )
    }.AsQueryable();

    private bool _isRoleColShown = true;
    private bool _isStatusColShown = true;

    private record User( string Name, string Role, string Status );
}

Header Content

The data grid allows a complete replacement of the default header cell contents.

NameRole
STATUS
Alice JohnsonAdministratorActive
Bob SmithEditorInactive
Charlie BrownViewerPending
Diana LeeModeratorActive
<LumexDataGrid Data="@FilteredUsers"
               Layout="@Layout.Fixed"
               Classes="@(new() { Table = "w-full" })">
    <PropertyColumn Property="@(p => p.Name)" />
    <PropertyColumn Property="@(p => p.Role)" />
    <PropertyColumn Property="@(p => p.Status)" HeaderContent="@HeaderContent" />
</LumexDataGrid>

@code {
    private RenderFragment<LumexColumnBase<User>> HeaderContent => context =>
    @<div class="flex items-center gap-2">
        @context.Title?.ToUpperInvariant()
        <LumexPopover Offset="2"
                      Radius="@Radius.Small"
                      Placement="@PopoverPlacement.BottomEnd">
            <LumexPopoverTrigger Size="@Size.Small"
                                 Variant="@Variant.Flat"
                                 Class="min-w-8 w-8 h-8 px-0 hover:bg-none">
                <LumexIcon Icon="@Icons.Rounded.Tune"
                           Size="@new("20")"
                           Color="@ThemeColor.Default" />
            </LumexPopoverTrigger>
            <LumexPopoverContent>
                <LumexTextbox Autofocus="@true"
                              Size="@Size.Small"
                              Color="@ThemeColor.Primary"
                              Variant="@InputVariant.Underlined"
                              Behavior="@InputBehavior.OnInput"
                              Placeholder="Status..."
                              @bind-Value="@_statusFilter" />
            </LumexPopoverContent>
        </LumexPopover>
    </div>;

    private readonly IQueryable<User> _users = new List<User>
    {
        new( "Alice Johnson", "Administrator", "Active" ),
        new( "Bob Smith", "Editor", "Inactive" ),
        new( "Charlie Brown", "Viewer", "Pending" ),
        new( "Diana Lee", "Moderator", "Active" )
    }.AsQueryable();

    private string? _statusFilter;

    private IQueryable<User> FilteredUsers
    {
        get
        {
            var result = _users;

            if( !string.IsNullOrEmpty( _statusFilter ) )
            {
                result = result.Where( u => u.Status.Contains( _statusFilter, StringComparison.CurrentCultureIgnoreCase ) );
            }

            return result;
        }
    }

    private record User( string Name, string Role, string Status );
}

Empty Content

The data grid allows you to define custom content or messages that display when there is no data to show.

NameRoleStatus
\(o_o)/ No records found
<LumexDataGrid Data="@_users" EmptyContent="@_emptyContent">
    <PropertyColumn Property="@(p => p.Name)" />
    <PropertyColumn Property="@(p => p.Role)" />
    <PropertyColumn Property="@(p => p.Status)" />
</LumexDataGrid>

@code {
    private readonly RenderFragment _emptyContent =
    @<div class="flex flex-col space-y-2">
        <span class="text-3xl">\(o_o)/</span>
        <span>No records found</span>
    </div>;

    private readonly IQueryable<User> _users = Array.Empty<User>().AsQueryable();

    private record User( string Name, string Role, string Status );
}

Loading Content

The data grid component allows you to display custom loading content, giving users a visual cue while data is being fetched or processed.

NameRoleStatus
Alice JohnsonAdministratorActive
Bob SmithEditorInactive
Charlie BrownViewerPending
Diana LeeModeratorActive
Fetching data...
<LumexDataGrid Data="@_users"
               Loading="@true"
               LoadingContent="@_loadingContent">
    <PropertyColumn Property="@(p => p.Name)" />
    <PropertyColumn Property="@(p => p.Role)" />
    <PropertyColumn Property="@(p => p.Status)" />
</LumexDataGrid>

@code {
    private readonly RenderFragment _loadingContent =
    @<text>
        <svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-indigo-500"
             xmlns="http://www.w3.org/2000/svg"
             fill="none"
             viewBox="0 0 24 24">
            <circle class="opacity-25"
                    cx="12"
                    cy="12"
                    r="10"
                    stroke="currentColor"
                    stroke-width="4" />
            <path class="opacity-75"
                  fill="currentColor"
                  d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
        </svg>
        Fetching data...
    </text>;

    private readonly IQueryable<User> _users = new List<User>
    {
        new( "Alice Johnson", "Administrator", "Active" ),
        new( "Bob Smith", "Editor", "Inactive" ),
        new( "Charlie Brown", "Viewer", "Pending" ),
        new( "Diana Lee", "Moderator", "Active" )
    }.AsQueryable();

    private record User( string Name, string Role, string Status );
}

Toolbar Content

Add a toolbar with custom actions or filters above the grid.

NameRoleStatus
Alice JohnsonAdministratorActive
Bob SmithEditorInactive
Charlie BrownViewerPending
Diana LeeModeratorActive
<LumexDataGrid Data="@FilteredUsers">
    <ToolbarContent>
        <div>
            <LumexTextbox Placeholder="Search by name..."
                          Behavior="@InputBehavior.OnInput"
                          Class="max-w-xs"
                          @bind-Value="@_nameFilter">
                <StartContent>
                    <LumexIcon Icon="@Icons.Rounded.Search"
                               Size="@new("20")"
                               Color="@ThemeColor.Default" />
                </StartContent>
            </LumexTextbox>
        </div>
    </ToolbarContent>
    <ChildContent>
        <PropertyColumn Property="@(p => p.Name)" />
        <PropertyColumn Property="@(p => p.Role)" />
        <PropertyColumn Property="@(p => p.Status)" />
    </ChildContent>
</LumexDataGrid>

@code {
    private readonly IQueryable<User> _users = new List<User>
    {
        new( "Alice Johnson", "Administrator", "Active" ),
        new( "Bob Smith", "Editor", "Inactive" ),
        new( "Charlie Brown", "Viewer", "Pending" ),
        new( "Diana Lee", "Moderator", "Active" )
    }.AsQueryable();

    private string? _nameFilter;

    private IQueryable<User> FilteredUsers
    {
        get
        {
            var result = _users;

            if( !string.IsNullOrEmpty( _nameFilter ) )
            {
                result = result.Where( u => u.Name.Contains( _nameFilter, StringComparison.CurrentCultureIgnoreCase ) );
            }

            return result;
        }
    }

    private record User( string Name, string Role, string Status );
}

Remote Data

Fetch data from an external source to populate the grid.

NameHeightMass
Luke Skywalker17277
C-3PO16775
R2-D29632
Darth Vader202136
Leia Organa15049
Owen Lars178120
Beru Whitesun lars16575
R5-D49732
Biggs Darklighter18384
Obi-Wan Kenobi18277
@inject HttpClient HttpClient

<LumexDataGrid DataSource="@_peopleProvider">
    <PropertyColumn Property="@(p => p.Name)" />
    <PropertyColumn Property="@(p => p.Height)" />
    <PropertyColumn Property="@(p => p.Mass)" />
</LumexDataGrid>

@code {
    private DataSource<Person>? _peopleProvider;

    protected override void OnInitialized()
    {
        _peopleProvider = async req =>
        {
            var response = await HttpClient.GetFromJsonAsync<PersonQueryResult>( "https://swapi.py4e.com/api/people/", req.CancellationToken );

            return DataSourceResult.From(
                items: response!.Results,
                totalItemCount: response!.Results.Length );
        };
    }

    private record Person( string Name, string Height, string Mass );
    private record PersonQueryResult( int Count, string Next, string Previous, Person[] Results );
}

It's only possible to perform data operations, such as sorting, filtering, or others, that are supported by the external API.

Row Click

The data grid exposes an OnRowClick event, triggered whenever a row is clicked.

NameRoleStatus
Alice JohnsonAdministratorActive
Bob SmithEditorInactive
Charlie BrownViewerPending
Diana LeeModeratorActive
<LumexDataGrid T="User"
               Data="@_users"
               OnRowClick="@OnRowClickHandler">
    <PropertyColumn Property="@(p => p.Name)" />
    <PropertyColumn Property="@(p => p.Role)" />
    <PropertyColumn Property="@(p => p.Status)" />
</LumexDataGrid>

@if( _clickedRow is not null )
{
    <div class="text-small">
        <p>Row data: @_clickedRow.Item</p>
        <p>Row index: @_clickedRow.Index</p>
    </div>
}

@code {
    private readonly IQueryable<User> _users = new List<User>
    {
        new( "Alice Johnson", "Administrator", "Active" ),
        new( "Bob Smith", "Editor", "Inactive" ),
        new( "Charlie Brown", "Viewer", "Pending" ),
        new( "Diana Lee", "Moderator", "Active" )
    }.AsQueryable();

    private DataGridRowClickEventArgs<User>? _clickedRow;

    private void OnRowClickHandler( DataGridRowClickEventArgs<User> args )
    {
        _clickedRow = args;
    }

    private record User( string Name, string Role, string Status );
}

Custom Styles

This component suppots named slots (represented as data-* attributes) that allow you to apply custom CSS to specific parts of the component.

  • Root: The base container of the data grid.
  • Wrapper: The wrapper element that surrounds the data grid.
  • EmptyWrapper: The wrapper of a content displayed when the data grid is empty.
  • LoadingWrapper: The wrapper of a content displayed while the data grid is loading.
  • Table: The table element within the data grid.
  • Thead: The table header element (thead) in the data grid.
  • Tbody: The table body element (tbody) in the data grid.
  • Tfoot: The table footer element (tfoot) in the data grid.
  • Tr: The table row element (tr) in the data grid.
  • Th: The table header cell (th) in the data grid.
  • Td: The table data cell (td) in the data grid.
  • Placeholder: The placeholder content displayed in the virtualized data grid.
  • SortIcon: The sort icon displayed in the header of sortable columns.

You can customize the component(s) by passing any Tailwind CSS classes to the following component parameters:

DataGrid

  • Class: The CSS class names for the base container of the data grid.
  • Classes: The CSS class names for the data grid slots.

API

See the API references below for a complete guide to all the parameters available for the components mentioned here.

.

An unhandled error has occurred. Reload 🗙