{"id":15092085,"url":"https://github.com/soenneker/soenneker.blazor.datatables","last_synced_at":"2026-06-14T04:02:02.437Z","repository":{"id":255936159,"uuid":"853892286","full_name":"soenneker/soenneker.blazor.datatables","owner":"soenneker","description":"A Blazor interop library for DataTables","archived":false,"fork":false,"pushed_at":"2026-06-06T02:22:39.000Z","size":4266,"stargazers_count":2,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-06-06T02:23:51.830Z","etag":null,"topics":["blazor","csharp","datatables","datatablesinterop","dotnet","interop","javascript","js"],"latest_commit_sha":null,"homepage":"https://soenneker.com","language":"CSS","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/soenneker.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":".github/SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"soenneker","thanks_dev":"soenneker"}},"created_at":"2024-09-07T20:31:20.000Z","updated_at":"2026-06-06T02:22:43.000Z","dependencies_parsed_at":"2026-03-15T18:02:34.376Z","dependency_job_id":null,"html_url":"https://github.com/soenneker/soenneker.blazor.datatables","commit_stats":{"total_commits":151,"total_committers":3,"mean_commits":"50.333333333333336","dds":"0.052980132450331174","last_synced_commit":"9635aec8f793ca6f9ffed84cf8fa67d839c3365c"},"previous_names":["soenneker/soenneker.blazor.datatables"],"tags_count":543,"template":false,"template_full_name":null,"purl":"pkg:github/soenneker/soenneker.blazor.datatables","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soenneker%2Fsoenneker.blazor.datatables","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soenneker%2Fsoenneker.blazor.datatables/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soenneker%2Fsoenneker.blazor.datatables/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soenneker%2Fsoenneker.blazor.datatables/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/soenneker","download_url":"https://codeload.github.com/soenneker/soenneker.blazor.datatables/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soenneker%2Fsoenneker.blazor.datatables/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34127345,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blazor","csharp","datatables","datatablesinterop","dotnet","interop","javascript","js"],"created_at":"2024-09-25T11:00:50.392Z","updated_at":"2026-06-09T22:00:38.529Z","avatar_url":"https://github.com/soenneker.png","language":"CSS","funding_links":["https://github.com/sponsors/soenneker","https://thanks.dev/soenneker"],"categories":[],"sub_categories":[],"readme":"[![](https://img.shields.io/nuget/v/soenneker.blazor.datatables.svg?style=for-the-badge)](https://www.nuget.org/packages/soenneker.blazor.datatables/)\n[![](https://img.shields.io/github/actions/workflow/status/soenneker/soenneker.blazor.datatables/publish-package.yml?style=for-the-badge)](https://github.com/soenneker/soenneker.blazor.datatables/actions/workflows/publish-package.yml)\n[![](https://img.shields.io/nuget/dt/soenneker.blazor.datatables.svg?style=for-the-badge)](https://www.nuget.org/packages/soenneker.blazor.datatables/)\n[![](https://img.shields.io/github/actions/workflow/status/soenneker/soenneker.blazor.datatables/codeql.yml?label=CodeQL\u0026style=for-the-badge)](https://github.com/soenneker/soenneker.blazor.datatables/actions/workflows/codeql.yml)\n\n# ![](https://user-images.githubusercontent.com/4441470/224455560-91ed3ee7-f510-4041-a8d2-3fc093025112.png) Soenneker.Blazor.DataTables\n### A Blazor interop library for DataTables\n\nThis library simplifies the integration of DataTables into Blazor applications, providing access to options, events, etc. A demo project showcasing common usages is included.\n\nDiligence was taken to align the Blazor API with JS. Refer to the [DataTables documentation](https://datatables.net/) for details. This is a work-in-progress; contribution is welcomed.\n\n## Installation\n\n```\ndotnet add package Soenneker.Blazor.DataTables\n```\n\n### Add the following to your `Startup.cs` file\n\n```csharp\npublic void ConfigureServices(IServiceCollection services)\n{\n    services.AddDataTablesInteropAsScoped();\n}\n```\n\n## Usage\n\n```razor\n@using Soenneker.Blazor.DataTables\n\n\u003cDataTable Options=\"_options\"\u003e\n    \u003cthead\u003e\n        \u003ctr\u003e\n            \u003cth\u003eName\u003c/th\u003e\n            \u003cth\u003ePosition\u003c/th\u003e\n            \u003cth\u003eOffice\u003c/th\u003e\n            \u003cth\u003eAge\u003c/th\u003e\n            \u003cth\u003eStart date\u003c/th\u003e\n            \u003cth\u003eSalary\u003c/th\u003e\n        \u003c/tr\u003e\n    \u003c/thead\u003e\n    \u003ctbody\u003e\n        \u003ctr\u003e\n            \u003ctd\u003eJohn Doe\u003c/td\u003e\n            \u003ctd\u003eDeveloper\u003c/td\u003e\n            \u003ctd\u003eLondon\u003c/td\u003e\n            \u003ctd\u003e28\u003c/td\u003e\n            \u003ctd\u003e2017/04/25\u003c/td\u003e\n            \u003ctd\u003e$320,800\u003c/td\u003e\n        \u003c/tr\u003e\n    \u003c/tbody\u003e\n\u003c/DataTable\u003e\n\n@code{\n\n    private readonly DataTableOptions _options_ = new()\n    {\n        Searching = true,\n        LengthChange = false,\n        Info = false,\n        Paging = false,\n        Order = [new object[] {0, \"asc\"}]\n    };\n}\n```\n\n## Custom Processing Indicators\n\nWhen using server-side processing, you can override the default DataTables processing indicator with custom Blazor content. This feature allows you to:\n\n- Display custom Blazor components as the processing indicator\n- Use any Blazor markup, components, and styling\n- Disable the processing indicator entirely\n\n### Basic Usage\n\n```razor\n\u003cDataTable Options=\"tableOptions\" OnServerSideRequest=\"HandleServerSideRequest\"\u003e\n    \u003cProcessingIndicator\u003e\n        \u003cdiv class=\"text-center\"\u003e\n            \u003cdiv class=\"spinner-border text-primary\" role=\"status\"\u003e\u003c/div\u003e\n            \u003cdiv class=\"mt-2\"\u003eLoading data from server...\u003c/div\u003e\n        \u003c/div\u003e\n    \u003c/ProcessingIndicator\u003e\n    \u003cChildContent\u003e\n        \u003cthead\u003e\n            \u003ctr\u003e\n                \u003cth data-data=\"id\"\u003eID\u003c/th\u003e\n                \u003cth data-data=\"name\"\u003eName\u003c/th\u003e\n                \u003cth data-data=\"email\"\u003eEmail\u003c/th\u003e\n            \u003c/tr\u003e\n        \u003c/thead\u003e\n        \u003ctbody\u003e\n        \u003c/tbody\u003e\n    \u003c/ChildContent\u003e\n\u003c/DataTable\u003e\n```\n\n## Continuation Token Support\n\nThe DataTables component supports continuation token-based pagination, which is useful for services like Azure Table Storage, Cosmos DB, or other cloud storage solutions that use continuation tokens instead of traditional offset-based pagination.\n\n### How Continuation Tokens Work\n\nThe component automatically emulates traditional DataTables pagination behavior while using continuation tokens under the hood:\n\n1. **First Request**: No continuation token is sent\n2. **Server Response**: Server returns data along with a continuation token for the next page\n3. **Subsequent Requests**: The continuation token from the previous response is sent with the next request\n4. **Last Page**: When there are no more pages, the server returns `null` for the continuation token\n5. **Pagination Display**: DataTables shows traditional pagination controls (Previous/Next buttons, page numbers) that work seamlessly with continuation tokens\n\n### Basic Usage\n\n```razor\n\u003cDataTable @ref=\"_dataTable\"\n           OnServerSideRequest=\"HandleServerSideRequest\"\n           Options=\"_tableOptions\"\u003e\n    \u003cthead\u003e\n        \u003ctr\u003e\n            \u003cth data-data=\"id\"\u003eID\u003c/th\u003e\n            \u003cth data-data=\"name\"\u003eName\u003c/th\u003e\n            \u003cth data-data=\"email\"\u003eEmail\u003c/th\u003e\n        \u003c/tr\u003e\n    \u003c/thead\u003e\n    \u003ctbody\u003e\n    \u003c/tbody\u003e\n\u003c/DataTable\u003e\n\n@code {\n    private DataTable _dataTable = null!;\n    private readonly DataTableOptions _tableOptions = new()\n    {\n        ServerSide = true,\n        PageLength = 10\n    };\n\n    private async Task\u003cDataTableServerResponse\u003e HandleServerSideRequest(DataTableServerSideRequest request)\n    {\n        // The request.ContinuationToken will contain the token from the previous response\n        // or null for the first request\n        \n        var result = await YourService.GetDataAsync(\n            pageSize: request.Length,\n            continuationToken: request.ContinuationToken,\n            searchTerm: request.Search?.Value,\n            orderBy: GetOrderByColumn(request.Order)\n        );\n        \n        // Return the response with the continuation token for the next page\n        // Note: The component automatically calculates appropriate TotalRecords and TotalFilteredRecords\n        // to make DataTables think it's using traditional pagination\n        return DataTableServerResponse.Success(\n            draw: request.Draw,\n            recordsTotal: result.TotalRecords,\n            recordsFiltered: result.TotalFilteredRecords,\n            data: result.Data,\n            continuationToken: result.NextContinuationToken // null when no more pages\n        );\n    }\n}\n```\n\n### Controlling Continuation Tokens\n\nYou can programmatically control continuation tokens using the DataTable component methods:\n\n```csharp\n// Reset pagination to start from the beginning\n_dataTable.ResetContinuationToken();\n\n// Set a specific continuation token for the next request\n_dataTable.SetContinuationToken(\"your-custom-token\");\n```\n\n### Backward Navigation Support\n\nThe component automatically stores continuation tokens for visited pages, enabling backward navigation:\n\n1. **Original Token Storage**: When you navigate to a page, the component stores the original continuation token used to reach that page\n2. **Consistent Backward Navigation**: When you click \"Previous\" or navigate to a previous page, the component uses the same original token for that page\n3. **Token Consistency**: Navigating back to a page multiple times will always use the same continuation token that was originally used to reach that page\n4. **Efficient Navigation**: If a direct token isn't available for a requested page, the component finds the closest available token and uses it\n5. **Seamless UX**: Users can navigate forward and backward through pages just like with traditional pagination\n\nThis ensures that your server-side logic receives the same continuation tokens when users navigate back to previously visited pages, maintaining consistency with your data source.\n\n### Implementation Notes\n\n- **Automatic Handling**: The component automatically manages continuation tokens between requests\n- **Pagination Emulation**: The component automatically calculates appropriate `TotalRecords` and `TotalFilteredRecords` values to make DataTables think it's using traditional offset-based pagination\n- **Virtual Page Tracking**: The component maintains a virtual page state that maps to continuation tokens, allowing DataTables pagination controls to work seamlessly\n- **Backward Navigation**: The component stores continuation tokens for visited pages, enabling backward navigation to previous pages\n- **Token Storage**: Continuation tokens are stored for each visited page, allowing efficient navigation in both directions\n- **Fingerprinting**: The component uses request fingerprinting to detect when search/filter parameters change and automatically resets the continuation token\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoenneker%2Fsoenneker.blazor.datatables","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsoenneker%2Fsoenneker.blazor.datatables","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoenneker%2Fsoenneker.blazor.datatables/lists"}