// Generated by scripts/generate-showcase.mjs. Do not edit directly. page DataTableDetail { layout = "showcase" state playground_color = ctx.url.searchParams.get("pg_color") ?? "primary" state playground_size = ctx.url.searchParams.get("pg_size") ?? "default" state playground_columns = JSON.parse(ctx.url.searchParams.get("pg_columns") ?? "\"[{\\\"key\\\": \\\"name\\\", \\\"label\\\": \\\"Account\\\", \\\"sortable\\\": true}, {\\\"key\\\": \\\"plan\\\", \\\"label\\\": \\\"Plan\\\", \\\"sortable\\\": true}, {\\\"key\\\": \\\"owner\\\", \\\"label\\\": \\\"Owner\\\"}, {\\\"key\\\": \\\"seats\\\", \\\"label\\\": \\\"Seats\\\", \\\"align\\\": \\\"end\\\", \\\"sortable\\\": true}, {\\\"key\\\": \\\"statusHtml\\\", \\\"label\\\": \\\"Status\\\", \\\"align\\\": \\\"center\\\", \\\"html\\\": true}]\"") state playground_rows = JSON.parse(ctx.url.searchParams.get("pg_rows") ?? "\"[{\\\"id\\\": 1, \\\"name\\\": \\\"Northwind\\\", \\\"plan\\\": \\\"Scale\\\", \\\"owner\\\": \\\"A. Okafor\\\", \\\"seats\\\": 6, \\\"status\\\": \\\"Active\\\", \\\"statusHtml\\\": \\\"Active\\\"}, {\\\"id\\\": 2, \\\"name\\\": \\\"Acme Industrial\\\", \\\"plan\\\": \\\"Team\\\", \\\"owner\\\": \\\"R. Silva\\\", \\\"seats\\\": 13, \\\"status\\\": \\\"Trial\\\", \\\"statusHtml\\\": \\\"Trial\\\"}, {\\\"id\\\": 3, \\\"name\\\": \\\"Globex\\\", \\\"plan\\\": \\\"Enterprise\\\", \\\"owner\\\": \\\"M. Chen\\\", \\\"seats\\\": 20, \\\"status\\\": \\\"Past due\\\", \\\"statusHtml\\\": \\\"Past due\\\"}, {\\\"id\\\": 4, \\\"name\\\": \\\"Initech\\\", \\\"plan\\\": \\\"Starter\\\", \\\"owner\\\": \\\"J. Dubois\\\", \\\"seats\\\": 27, \\\"status\\\": \\\"Active\\\", \\\"statusHtml\\\": \\\"Active\\\"}, {\\\"id\\\": 5, \\\"name\\\": \\\"Umbrella\\\", \\\"plan\\\": \\\"Scale\\\", \\\"owner\\\": \\\"P. Novak\\\", \\\"seats\\\": 34, \\\"status\\\": \\\"Trial\\\", \\\"statusHtml\\\": \\\"Trial\\\"}, {\\\"id\\\": 6, \\\"name\\\": \\\"Stark Labs\\\", \\\"plan\\\": \\\"Team\\\", \\\"owner\\\": \\\"A. Okafor\\\", \\\"seats\\\": 41, \\\"status\\\": \\\"Past due\\\", \\\"statusHtml\\\": \\\"Past due\\\"}, {\\\"id\\\": 7, \\\"name\\\": \\\"Wayne Foods\\\", \\\"plan\\\": \\\"Enterprise\\\", \\\"owner\\\": \\\"R. Silva\\\", \\\"seats\\\": 48, \\\"status\\\": \\\"Active\\\", \\\"statusHtml\\\": \\\"Active\\\"}, {\\\"id\\\": 8, \\\"name\\\": \\\"Soylent\\\", \\\"plan\\\": \\\"Starter\\\", \\\"owner\\\": \\\"M. Chen\\\", \\\"seats\\\": 55, \\\"status\\\": \\\"Trial\\\", \\\"statusHtml\\\": \\\"Trial\\\"}, {\\\"id\\\": 9, \\\"name\\\": \\\"Hooli\\\", \\\"plan\\\": \\\"Scale\\\", \\\"owner\\\": \\\"J. Dubois\\\", \\\"seats\\\": 62, \\\"status\\\": \\\"Past due\\\", \\\"statusHtml\\\": \\\"Past due\\\"}, {\\\"id\\\": 10, \\\"name\\\": \\\"Vehement\\\", \\\"plan\\\": \\\"Team\\\", \\\"owner\\\": \\\"P. Novak\\\", \\\"seats\\\": 69, \\\"status\\\": \\\"Active\\\", \\\"statusHtml\\\": \\\"Active\\\"}, {\\\"id\\\": 11, \\\"name\\\": \\\"Massive Dynamic\\\", \\\"plan\\\": \\\"Enterprise\\\", \\\"owner\\\": \\\"A. Okafor\\\", \\\"seats\\\": 76, \\\"status\\\": \\\"Trial\\\", \\\"statusHtml\\\": \\\"Trial\\\"}, {\\\"id\\\": 12, \\\"name\\\": \\\"Cyberdyne\\\", \\\"plan\\\": \\\"Starter\\\", \\\"owner\\\": \\\"R. Silva\\\", \\\"seats\\\": 83, \\\"status\\\": \\\"Past due\\\", \\\"statusHtml\\\": \\\"Past due\\\"}, {\\\"id\\\": 13, \\\"name\\\": \\\"Tyrell\\\", \\\"plan\\\": \\\"Scale\\\", \\\"owner\\\": \\\"M. Chen\\\", \\\"seats\\\": 90, \\\"status\\\": \\\"Active\\\", \\\"statusHtml\\\": \\\"Active\\\"}, {\\\"id\\\": 14, \\\"name\\\": \\\"Aperture\\\", \\\"plan\\\": \\\"Team\\\", \\\"owner\\\": \\\"J. Dubois\\\", \\\"seats\\\": 97, \\\"status\\\": \\\"Trial\\\", \\\"statusHtml\\\": \\\"Trial\\\"}, {\\\"id\\\": 15, \\\"name\\\": \\\"Black Mesa\\\", \\\"plan\\\": \\\"Enterprise\\\", \\\"owner\\\": \\\"P. Novak\\\", \\\"seats\\\": 104, \\\"status\\\": \\\"Past due\\\", \\\"statusHtml\\\": \\\"Past due\\\"}]\"") state playground_rowKey = ctx.url.searchParams.get("pg_rowKey") ?? "id" state playground_remote = (ctx.url.searchParams.get("pg_remote") ?? "false") === "true" state playground_loadingLabel = ctx.url.searchParams.get("pg_loadingLabel") ?? "Data Table" state playground_errorLabel = ctx.url.searchParams.get("pg_errorLabel") ?? "Data Table" state playground_retryLabel = ctx.url.searchParams.get("pg_retryLabel") ?? "Data Table" state playground_caption = ctx.url.searchParams.get("pg_caption") ?? "Accounts" state playground_description = ctx.url.searchParams.get("pg_description") ?? "A polished default data table for a modern application." state playground_searchable = (ctx.url.searchParams.get("pg_searchable") ?? "true") === "true" state playground_searchPlaceholder = ctx.url.searchParams.get("pg_searchPlaceholder") ?? "Enter a sample value" state playground_paginated = (ctx.url.searchParams.get("pg_paginated") ?? "true") === "true" state playground_pageSize = Number(ctx.url.searchParams.get("pg_pageSize") ?? "5") state playground_paginationStyle = ctx.url.searchParams.get("pg_paginationStyle") ?? "compact" state playground_pageSizes = JSON.parse(ctx.url.searchParams.get("pg_pageSizes") ?? "[5,10,25]") state playground_selectable = (ctx.url.searchParams.get("pg_selectable") ?? "true") === "true" state playground_actions = JSON.parse(ctx.url.searchParams.get("pg_actions") ?? "\"[{\\\"id\\\": \\\"refresh\\\", \\\"label\\\": \\\"Refresh\\\", \\\"icon\\\": \\\"icon-[lucide--refresh-cw]\\\", \\\"when\\\": \\\"always\\\"}, {\\\"id\\\": \\\"read\\\", \\\"label\\\": \\\"Mark as read\\\", \\\"icon\\\": \\\"icon-[lucide--mail-open]\\\", \\\"when\\\": \\\"selection\\\"}, {\\\"id\\\": \\\"archive\\\", \\\"label\\\": \\\"Archive\\\", \\\"icon\\\": \\\"icon-[lucide--archive]\\\", \\\"when\\\": \\\"selection\\\"}, {\\\"id\\\": \\\"delete\\\", \\\"label\\\": \\\"Delete\\\", \\\"icon\\\": \\\"icon-[lucide--trash-2]\\\", \\\"when\\\": \\\"selection\\\", \\\"tone\\\": \\\"danger\\\"}]\"") state playground_striped = (ctx.url.searchParams.get("pg_striped") ?? "true") === "true" state playground_bordered = (ctx.url.searchParams.get("pg_bordered") ?? "true") === "true" state playground_gridlines = ctx.url.searchParams.get("pg_gridlines") ?? "rows" state playground_density = ctx.url.searchParams.get("pg_density") ?? "default" state playground_emptyLabel = ctx.url.searchParams.get("pg_emptyLabel") ?? "Data Table" state playground_noResultsLabel = ctx.url.searchParams.get("pg_noResultsLabel") ?? "Data Table" state playground_clearSearchLabel = ctx.url.searchParams.get("pg_clearSearchLabel") ?? "Data Table" state playground_stickyFirstColumn = (ctx.url.searchParams.get("pg_stickyFirstColumn") ?? "false") === "true" state playground_layout = ctx.url.searchParams.get("pg_layout") ?? "rows" state playground_class = ctx.url.searchParams.get("pg_class") ?? "showcase-instance showcase-instance--1" seo { title = "Data Table" description = "Sortable, filterable, paginated data table with row selection." } view {
Tables component

Data Table

Sortable, filterable, paginated data table with row selection.

29 props1 slots8 outputsTheme readyResponsive
Interactive playground

Configure Data Table

Change any prop and inspect the server-rendered component immediately.

Live preview
Component code
<DataTable
  columns='[
    {
      "id": "data-table-0-1",
      "key": "data-table-0-1",
      "value": "primary",
      "label": "Primary workflow",
      "title": "Primary workflow",
      "description": "A clean default configuration for everyday product interfaces.",
      "text": "A configurable sample message.",
      "href": "#data-table-demo",
      "actionHref": "#data-table-demo",
      "actionLabel": "View details",
      "icon": "icon-[lucide--sparkles]",
      "iconClass": "icon-[lucide--sparkles]",
      "variant": "primary",
      "status": "Active",
      "time": "09:30",
      "current": true,
      "selected": true,
      "checked": true,
      "disabled": false,
      "outgoing": false,
      "open": true,
      "number": 1,
      "count": 16,
      "percentage": 72,
      "color": "#7c3aed",
      "target": "_self",
      "ariaLabel": "Open primary workflow 1",
      "items": [
        {
          "label": "Nested option A",
          "value": "nested-a"
        },
        {
          "label": "Nested option B",
          "value": "nested-b"
        }
      ],
      "links": [
        {
          "label": "Documentation",
          "href": "#documentation"
        },
        {
          "label": "API reference",
          "href": "#api-reference"
        }
      ],
      "values": [
        "Included",
        "Standard"
      ]
    },
    {
      "id": "data-table-0-2",
      "key": "data-table-0-2",
      "value": "secondary",
      "label": "Additional option",
      "title": "Supporting example",
      "description": "A clean default configuration for everyday product interfaces.",
      "text": "Another configurable message.",
      "href": "#data-table-demo",
      "actionHref": "#data-table-demo",
      "actionLabel": "View details",
      "icon": "icon-[lucide--sparkles]",
      "iconClass": "icon-[lucide--sparkles]",
      "variant": "primary",
      "status": "Active",
      "time": "09:30",
      "current": false,
      "selected": false,
      "checked": false,
      "disabled": false,
      "outgoing": true,
      "open": true,
      "number": 2,
      "count": 32,
      "percentage": 72,
      "color": "#7c3aed",
      "target": "_self",
      "ariaLabel": "Open primary workflow 2",
      "items": [
        {
          "label": "Nested option A",
          "value": "nested-a"
        },
        {
          "label": "Nested option B",
          "value": "nested-b"
        }
      ],
      "links": [
        {
          "label": "Documentation",
          "href": "#documentation"
        },
        {
          "label": "API reference",
          "href": "#api-reference"
        }
      ],
      "values": [
        "Included",
        "Standard"
      ]
    }
  ]'
  rows='[
    {
      "id": "data-table-0-1",
      "key": "data-table-0-1",
      "value": "primary",
      "label": "Primary workflow",
      "title": "Primary workflow",
      "description": "A clean default configuration for everyday product interfaces.",
      "text": "A configurable sample message.",
      "href": "#data-table-demo",
      "actionHref": "#data-table-demo",
      "actionLabel": "View details",
      "icon": "icon-[lucide--sparkles]",
      "iconClass": "icon-[lucide--sparkles]",
      "variant": "primary",
      "status": "Active",
      "time": "09:30",
      "current": true,
      "selected": true,
      "checked": true,
      "disabled": false,
      "outgoing": false,
      "open": true,
      "number": 1,
      "count": 16,
      "percentage": 72,
      "color": "#7c3aed",
      "target": "_self",
      "ariaLabel": "Open primary workflow 1",
      "items": [
        {
          "label": "Nested option A",
          "value": "nested-a"
        },
        {
          "label": "Nested option B",
          "value": "nested-b"
        }
      ],
      "links": [
        {
          "label": "Documentation",
          "href": "#documentation"
        },
        {
          "label": "API reference",
          "href": "#api-reference"
        }
      ],
      "values": [
        "Included",
        "Standard"
      ]
    },
    {
      "id": "data-table-0-2",
      "key": "data-table-0-2",
      "value": "secondary",
      "label": "Additional option",
      "title": "Supporting example",
      "description": "A clean default configuration for everyday product interfaces.",
      "text": "Another configurable message.",
      "href": "#data-table-demo",
      "actionHref": "#data-table-demo",
      "actionLabel": "View details",
      "icon": "icon-[lucide--sparkles]",
      "iconClass": "icon-[lucide--sparkles]",
      "variant": "primary",
      "status": "Active",
      "time": "09:30",
      "current": false,
      "selected": false,
      "checked": false,
      "disabled": false,
      "outgoing": true,
      "open": true,
      "number": 2,
      "count": 32,
      "percentage": 72,
      "color": "#7c3aed",
      "target": "_self",
      "ariaLabel": "Open primary workflow 2",
      "items": [
        {
          "label": "Nested option A",
          "value": "nested-a"
        },
        {
          "label": "Nested option B",
          "value": "nested-b"
        }
      ],
      "links": [
        {
          "label": "Documentation",
          "href": "#documentation"
        },
        {
          "label": "API reference",
          "href": "#api-reference"
        }
      ],
      "values": [
        "Included",
        "Standard"
      ]
    }
  ]'
  loadingLabel="Data Table"
  errorLabel="Data Table"
  retryLabel="Data Table"
  description="A polished default data table for a modern application."
  searchPlaceholder="Enter a sample value"
  pageSize="3"
  pageSizes='[
    {
      "id": "data-table-0-1",
      "key": "data-table-0-1",
      "value": "primary",
      "label": "Primary workflow",
      "title": "Primary workflow",
      "description": "A clean default configuration for everyday product interfaces.",
      "text": "A configurable sample message.",
      "href": "#data-table-demo",
      "actionHref": "#data-table-demo",
      "actionLabel": "View details",
      "icon": "icon-[lucide--sparkles]",
      "iconClass": "icon-[lucide--sparkles]",
      "variant": "primary",
      "status": "Active",
      "time": "09:30",
      "current": true,
      "selected": true,
      "checked": true,
      "disabled": false,
      "outgoing": false,
      "open": true,
      "number": 1,
      "count": 16,
      "percentage": 72,
      "color": "#7c3aed",
      "target": "_self",
      "ariaLabel": "Open primary workflow 1",
      "items": [
        {
          "label": "Nested option A",
          "value": "nested-a"
        },
        {
          "label": "Nested option B",
          "value": "nested-b"
        }
      ],
      "links": [
        {
          "label": "Documentation",
          "href": "#documentation"
        },
        {
          "label": "API reference",
          "href": "#api-reference"
        }
      ],
      "values": [
        "Included",
        "Standard"
      ]
    },
    {
      "id": "data-table-0-2",
      "key": "data-table-0-2",
      "value": "secondary",
      "label": "Additional option",
      "title": "Supporting example",
      "description": "A clean default configuration for everyday product interfaces.",
      "text": "Another configurable message.",
      "href": "#data-table-demo",
      "actionHref": "#data-table-demo",
      "actionLabel": "View details",
      "icon": "icon-[lucide--sparkles]",
      "iconClass": "icon-[lucide--sparkles]",
      "variant": "primary",
      "status": "Active",
      "time": "09:30",
      "current": false,
      "selected": false,
      "checked": false,
      "disabled": false,
      "outgoing": true,
      "open": true,
      "number": 2,
      "count": 32,
      "percentage": 72,
      "color": "#7c3aed",
      "target": "_self",
      "ariaLabel": "Open primary workflow 2",
      "items": [
        {
          "label": "Nested option A",
          "value": "nested-a"
        },
        {
          "label": "Nested option B",
          "value": "nested-b"
        }
      ],
      "links": [
        {
          "label": "Documentation",
          "href": "#documentation"
        },
        {
          "label": "API reference",
          "href": "#api-reference"
        }
      ],
      "values": [
        "Included",
        "Standard"
      ]
    }
  ]'
  actions='[
    {
      "id": "data-table-0-1",
      "key": "data-table-0-1",
      "value": "primary",
      "label": "Primary workflow",
      "title": "Primary workflow",
      "description": "A clean default configuration for everyday product interfaces.",
      "text": "A configurable sample message.",
      "href": "#data-table-demo",
      "actionHref": "#data-table-demo",
      "actionLabel": "View details",
      "icon": "icon-[lucide--sparkles]",
      "iconClass": "icon-[lucide--sparkles]",
      "variant": "primary",
      "status": "Active",
      "time": "09:30",
      "current": true,
      "selected": true,
      "checked": true,
      "disabled": false,
      "outgoing": false,
      "open": true,
      "number": 1,
      "count": 16,
      "percentage": 72,
      "color": "#7c3aed",
      "target": "_self",
      "ariaLabel": "Open primary workflow 1",
      "items": [
        {
          "label": "Nested option A",
          "value": "nested-a"
        },
        {
          "label": "Nested option B",
          "value": "nested-b"
        }
      ],
      "links": [
        {
          "label": "Documentation",
          "href": "#documentation"
        },
        {
          "label": "API reference",
          "href": "#api-reference"
        }
      ],
      "values": [
        "Included",
        "Standard"
      ]
    },
    {
      "id": "data-table-0-2",
      "key": "data-table-0-2",
      "value": "secondary",
      "label": "Additional option",
      "title": "Supporting example",
      "description": "A clean default configuration for everyday product interfaces.",
      "text": "Another configurable message.",
      "href": "#data-table-demo",
      "actionHref": "#data-table-demo",
      "actionLabel": "View details",
      "icon": "icon-[lucide--sparkles]",
      "iconClass": "icon-[lucide--sparkles]",
      "variant": "primary",
      "status": "Active",
      "time": "09:30",
      "current": false,
      "selected": false,
      "checked": false,
      "disabled": false,
      "outgoing": true,
      "open": true,
      "number": 2,
      "count": 32,
      "percentage": 72,
      "color": "#7c3aed",
      "target": "_self",
      "ariaLabel": "Open primary workflow 2",
      "items": [
        {
          "label": "Nested option A",
          "value": "nested-a"
        },
        {
          "label": "Nested option B",
          "value": "nested-b"
        }
      ],
      "links": [
        {
          "label": "Documentation",
          "href": "#documentation"
        },
        {
          "label": "API reference",
          "href": "#api-reference"
        }
      ],
      "values": [
        "Included",
        "Standard"
      ]
    }
  ]'
  emptyLabel="Data Table"
  noResultsLabel="Data Table"
  clearSearchLabel="Data Table"
/>
Event outputInteract with the preview to inspect the typed payload.
Component props29 controls
Live examples

Designed for real product surfaces

Compare configurations and resize the browser to check responsive behavior.

Recommended

Sortable, searchable and paged

Click a header to sort, type to filter, and page through the results. Every derivation is a shared function, so the first page is server rendered and stays live after hydration.

01
Live preview
Component usage.wrn
<DataTable
  columns="[{\"key\": \"name\", \"label\": \"Account\", \"sortable\": true}, {\"key\": \"plan\", \"label\": \"Plan\", \"sortable\": true}, {\"key\": \"owner\", \"label\": \"Owner\"}, {\"key\": \"seats\", \"label\": \"Seats\", \"align\": \"end\", \"sortable\": true}, {\"key\": \"status\", \"label\": \"Status\", \"align\": \"center\"}]"
  rows="[{\"id\": 1, \"name\": \"Northwind\", \"plan\": \"Scale\", \"owner\": \"A. Okafor\", \"seats\": 6, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 2, \"name\": \"Acme Industrial\", \"plan\": \"Team\", \"owner\": \"R. Silva\", \"seats\": 13, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 3, \"name\": \"Globex\", \"plan\": \"Enterprise\", \"owner\": \"M. Chen\", \"seats\": 20, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 4, \"name\": \"Initech\", \"plan\": \"Starter\", \"owner\": \"J. Dubois\", \"seats\": 27, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 5, \"name\": \"Umbrella\", \"plan\": \"Scale\", \"owner\": \"P. Novak\", \"seats\": 34, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 6, \"name\": \"Stark Labs\", \"plan\": \"Team\", \"owner\": \"A. Okafor\", \"seats\": 41, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 7, \"name\": \"Wayne Foods\", \"plan\": \"Enterprise\", \"owner\": \"R. Silva\", \"seats\": 48, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 8, \"name\": \"Soylent\", \"plan\": \"Starter\", \"owner\": \"M. Chen\", \"seats\": 55, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 9, \"name\": \"Hooli\", \"plan\": \"Scale\", \"owner\": \"J. Dubois\", \"seats\": 62, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 10, \"name\": \"Vehement\", \"plan\": \"Team\", \"owner\": \"P. Novak\", \"seats\": 69, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 11, \"name\": \"Massive Dynamic\", \"plan\": \"Enterprise\", \"owner\": \"A. Okafor\", \"seats\": 76, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 12, \"name\": \"Cyberdyne\", \"plan\": \"Starter\", \"owner\": \"R. Silva\", \"seats\": 83, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 13, \"name\": \"Tyrell\", \"plan\": \"Scale\", \"owner\": \"M. Chen\", \"seats\": 90, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 14, \"name\": \"Aperture\", \"plan\": \"Team\", \"owner\": \"J. Dubois\", \"seats\": 97, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 15, \"name\": \"Black Mesa\", \"plan\": \"Enterprise\", \"owner\": \"P. Novak\", \"seats\": 104, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}]"
  loadingLabel="Data Table"
  errorLabel="Data Table"
  retryLabel="Data Table"
  caption="Accounts"
  description="15 records across four plans."
  searchPlaceholder="Enter a sample value"
  pageSize="5"
  pageSizes='[
    5,
    10,
    25
  ]'
  selectable="true"
  actions='[]'
  emptyLabel="Data Table"
  noResultsLabel="Data Table"
  clearSearchLabel="Data Table"
/>
Advanced

Bulk actions on selection

An inbox pattern: Refresh is always available, while Mark as read, Archive and Delete appear only once something is ticked. Each handler receives the selected records, and an action output fires for every click.

02
Live preview
Component usage.wrn
<DataTable
  size="sm"
  columns="[{\"key\": \"owner\", \"label\": \"From\", \"sortable\": true}, {\"key\": \"name\", \"label\": \"Subject\", \"sortable\": true}, {\"key\": \"plan\", \"label\": \"Folder\", \"align\": \"center\"}, {\"key\": \"statusHtml\", \"label\": \"State\", \"align\": \"center\", \"html\": true}]"
  rows="[{\"id\": 1, \"name\": \"Northwind\", \"plan\": \"Scale\", \"owner\": \"A. Okafor\", \"seats\": 6, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 2, \"name\": \"Acme Industrial\", \"plan\": \"Team\", \"owner\": \"R. Silva\", \"seats\": 13, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 3, \"name\": \"Globex\", \"plan\": \"Enterprise\", \"owner\": \"M. Chen\", \"seats\": 20, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 4, \"name\": \"Initech\", \"plan\": \"Starter\", \"owner\": \"J. Dubois\", \"seats\": 27, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 5, \"name\": \"Umbrella\", \"plan\": \"Scale\", \"owner\": \"P. Novak\", \"seats\": 34, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 6, \"name\": \"Stark Labs\", \"plan\": \"Team\", \"owner\": \"A. Okafor\", \"seats\": 41, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 7, \"name\": \"Wayne Foods\", \"plan\": \"Enterprise\", \"owner\": \"R. Silva\", \"seats\": 48, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 8, \"name\": \"Soylent\", \"plan\": \"Starter\", \"owner\": \"M. Chen\", \"seats\": 55, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 9, \"name\": \"Hooli\", \"plan\": \"Scale\", \"owner\": \"J. Dubois\", \"seats\": 62, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 10, \"name\": \"Vehement\", \"plan\": \"Team\", \"owner\": \"P. Novak\", \"seats\": 69, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 11, \"name\": \"Massive Dynamic\", \"plan\": \"Enterprise\", \"owner\": \"A. Okafor\", \"seats\": 76, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 12, \"name\": \"Cyberdyne\", \"plan\": \"Starter\", \"owner\": \"R. Silva\", \"seats\": 83, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 13, \"name\": \"Tyrell\", \"plan\": \"Scale\", \"owner\": \"M. Chen\", \"seats\": 90, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 14, \"name\": \"Aperture\", \"plan\": \"Team\", \"owner\": \"J. Dubois\", \"seats\": 97, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 15, \"name\": \"Black Mesa\", \"plan\": \"Enterprise\", \"owner\": \"P. Novak\", \"seats\": 104, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}]"
  loadingLabel="Compact example"
  errorLabel="Compact example"
  retryLabel="Compact example"
  caption="Inbox"
  description="Tick a row to reveal the selection-only actions."
  searchPlaceholder="Search mail"
  pageSize="6"
  pageSizes='[
    6,
    12,
    25
  ]'
  selectable="true"
  actions="[{\"id\": \"refresh\", \"label\": \"Refresh\", \"icon\": \"icon-[lucide--refresh-cw]\", \"when\": \"always\"}, {\"id\": \"read\", \"label\": \"Mark as read\", \"icon\": \"icon-[lucide--mail-open]\", \"when\": \"selection\"}, {\"id\": \"archive\", \"label\": \"Archive\", \"icon\": \"icon-[lucide--archive]\", \"when\": \"selection\"}, {\"id\": \"delete\", \"label\": \"Delete\", \"icon\": \"icon-[lucide--trash-2]\", \"when\": \"selection\", \"tone\": \"danger\"}]"
  emptyLabel="Compact example"
  noResultsLabel="Compact example"
  clearSearchLabel="Compact example"
/>
Recommended

Numbered pagination

Page numbers instead of arrows, windowed around the current page so a large table never renders hundreds of buttons.

03
Live preview
Component usage.wrn
<DataTable
  size="lg"
  columns="[{\"key\": \"name\", \"label\": \"Account\", \"sortable\": true}, {\"key\": \"plan\", \"label\": \"Plan\", \"sortable\": true}, {\"key\": \"owner\", \"label\": \"Owner\"}, {\"key\": \"seats\", \"label\": \"Seats\", \"align\": \"end\", \"sortable\": true}, {\"key\": \"status\", \"label\": \"Status\", \"align\": \"center\"}]"
  rows="[{\"id\": 1, \"name\": \"Northwind\", \"plan\": \"Scale\", \"owner\": \"A. Okafor\", \"seats\": 6, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 2, \"name\": \"Acme Industrial\", \"plan\": \"Team\", \"owner\": \"R. Silva\", \"seats\": 13, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 3, \"name\": \"Globex\", \"plan\": \"Enterprise\", \"owner\": \"M. Chen\", \"seats\": 20, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 4, \"name\": \"Initech\", \"plan\": \"Starter\", \"owner\": \"J. Dubois\", \"seats\": 27, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 5, \"name\": \"Umbrella\", \"plan\": \"Scale\", \"owner\": \"P. Novak\", \"seats\": 34, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 6, \"name\": \"Stark Labs\", \"plan\": \"Team\", \"owner\": \"A. Okafor\", \"seats\": 41, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 7, \"name\": \"Wayne Foods\", \"plan\": \"Enterprise\", \"owner\": \"R. Silva\", \"seats\": 48, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 8, \"name\": \"Soylent\", \"plan\": \"Starter\", \"owner\": \"M. Chen\", \"seats\": 55, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 9, \"name\": \"Hooli\", \"plan\": \"Scale\", \"owner\": \"J. Dubois\", \"seats\": 62, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 10, \"name\": \"Vehement\", \"plan\": \"Team\", \"owner\": \"P. Novak\", \"seats\": 69, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 11, \"name\": \"Massive Dynamic\", \"plan\": \"Enterprise\", \"owner\": \"A. Okafor\", \"seats\": 76, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 12, \"name\": \"Cyberdyne\", \"plan\": \"Starter\", \"owner\": \"R. Silva\", \"seats\": 83, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 13, \"name\": \"Tyrell\", \"plan\": \"Scale\", \"owner\": \"M. Chen\", \"seats\": 90, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 14, \"name\": \"Aperture\", \"plan\": \"Team\", \"owner\": \"J. Dubois\", \"seats\": 97, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 15, \"name\": \"Black Mesa\", \"plan\": \"Enterprise\", \"owner\": \"P. Novak\", \"seats\": 104, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}]"
  loadingLabel="Advanced example"
  errorLabel="Advanced example"
  retryLabel="Advanced example"
  caption="Accounts"
  description="15 records, four per page."
  searchPlaceholder="Search by name, email, or identifier"
  pageSize="4"
  paginationStyle="numbered"
  pageSizes='[
    4,
    8,
    15
  ]'
  actions='[]'
  emptyLabel="Advanced example"
  noResultsLabel="Advanced example"
  clearSearchLabel="Advanced example"
/>
Advanced

Full gridlines and custom cell markup

gridlines="grid" adds column rules as well as row rules. The Status column is rendered from markup rather than text, so a cell can hold a badge, a link or any other component output.

04
Live preview
Component usage.wrn
<DataTable
  size="lg"
  columns="[{\"key\": \"name\", \"label\": \"Account\", \"sortable\": true}, {\"key\": \"plan\", \"label\": \"Plan\", \"sortable\": true}, {\"key\": \"owner\", \"label\": \"Owner\"}, {\"key\": \"seats\", \"label\": \"Seats\", \"align\": \"end\", \"sortable\": true}, {\"key\": \"statusHtml\", \"label\": \"Status\", \"align\": \"center\", \"html\": true}]"
  rows="[{\"id\": 1, \"name\": \"Northwind\", \"plan\": \"Scale\", \"owner\": \"A. Okafor\", \"seats\": 6, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 2, \"name\": \"Acme Industrial\", \"plan\": \"Team\", \"owner\": \"R. Silva\", \"seats\": 13, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 3, \"name\": \"Globex\", \"plan\": \"Enterprise\", \"owner\": \"M. Chen\", \"seats\": 20, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 4, \"name\": \"Initech\", \"plan\": \"Starter\", \"owner\": \"J. Dubois\", \"seats\": 27, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 5, \"name\": \"Umbrella\", \"plan\": \"Scale\", \"owner\": \"P. Novak\", \"seats\": 34, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 6, \"name\": \"Stark Labs\", \"plan\": \"Team\", \"owner\": \"A. Okafor\", \"seats\": 41, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 7, \"name\": \"Wayne Foods\", \"plan\": \"Enterprise\", \"owner\": \"R. Silva\", \"seats\": 48, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 8, \"name\": \"Soylent\", \"plan\": \"Starter\", \"owner\": \"M. Chen\", \"seats\": 55, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 9, \"name\": \"Hooli\", \"plan\": \"Scale\", \"owner\": \"J. Dubois\", \"seats\": 62, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 10, \"name\": \"Vehement\", \"plan\": \"Team\", \"owner\": \"P. Novak\", \"seats\": 69, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 11, \"name\": \"Massive Dynamic\", \"plan\": \"Enterprise\", \"owner\": \"A. Okafor\", \"seats\": 76, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 12, \"name\": \"Cyberdyne\", \"plan\": \"Starter\", \"owner\": \"R. Silva\", \"seats\": 83, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 13, \"name\": \"Tyrell\", \"plan\": \"Scale\", \"owner\": \"M. Chen\", \"seats\": 90, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 14, \"name\": \"Aperture\", \"plan\": \"Team\", \"owner\": \"J. Dubois\", \"seats\": 97, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 15, \"name\": \"Black Mesa\", \"plan\": \"Enterprise\", \"owner\": \"P. Novak\", \"seats\": 104, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}]"
  loadingLabel="Advanced example"
  errorLabel="Advanced example"
  retryLabel="Advanced example"
  caption="Accounts"
  description="Status is supplied as markup through a html column."
  searchPlaceholder="Enter a sample value"
  pageSize="5"
  pageSizes='[
    5,
    10,
    25
  ]'
  actions='[]'
  striped="false"
  gridlines="grid"
  emptyLabel="Advanced example"
  noResultsLabel="Advanced example"
  clearSearchLabel="Advanced example"
/>
Compact

Fixed widths and alignment

Columns can set their own width and alignment: text left, counts right, short labels centred. Widths are plain CSS, so percentages, rem and ch all work.

05
Live preview
Component usage.wrn
<DataTable
  size="lg"
  columns="[{\"key\": \"name\", \"label\": \"Account\", \"sortable\": true, \"width\": \"40%\"}, {\"key\": \"seats\", \"label\": \"Seats\", \"align\": \"end\", \"sortable\": true, \"width\": \"20%\"}, {\"key\": \"plan\", \"label\": \"Plan\", \"align\": \"center\", \"width\": \"20%\"}, {\"key\": \"status\", \"label\": \"Status\", \"align\": \"end\", \"width\": \"20%\"}]"
  rows="[{\"id\": 1, \"name\": \"Northwind\", \"plan\": \"Scale\", \"owner\": \"A. Okafor\", \"seats\": 6, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 2, \"name\": \"Acme Industrial\", \"plan\": \"Team\", \"owner\": \"R. Silva\", \"seats\": 13, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 3, \"name\": \"Globex\", \"plan\": \"Enterprise\", \"owner\": \"M. Chen\", \"seats\": 20, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 4, \"name\": \"Initech\", \"plan\": \"Starter\", \"owner\": \"J. Dubois\", \"seats\": 27, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 5, \"name\": \"Umbrella\", \"plan\": \"Scale\", \"owner\": \"P. Novak\", \"seats\": 34, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 6, \"name\": \"Stark Labs\", \"plan\": \"Team\", \"owner\": \"A. Okafor\", \"seats\": 41, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 7, \"name\": \"Wayne Foods\", \"plan\": \"Enterprise\", \"owner\": \"R. Silva\", \"seats\": 48, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 8, \"name\": \"Soylent\", \"plan\": \"Starter\", \"owner\": \"M. Chen\", \"seats\": 55, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 9, \"name\": \"Hooli\", \"plan\": \"Scale\", \"owner\": \"J. Dubois\", \"seats\": 62, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 10, \"name\": \"Vehement\", \"plan\": \"Team\", \"owner\": \"P. Novak\", \"seats\": 69, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 11, \"name\": \"Massive Dynamic\", \"plan\": \"Enterprise\", \"owner\": \"A. Okafor\", \"seats\": 76, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 12, \"name\": \"Cyberdyne\", \"plan\": \"Starter\", \"owner\": \"R. Silva\", \"seats\": 83, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 13, \"name\": \"Tyrell\", \"plan\": \"Scale\", \"owner\": \"M. Chen\", \"seats\": 90, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 14, \"name\": \"Aperture\", \"plan\": \"Team\", \"owner\": \"J. Dubois\", \"seats\": 97, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 15, \"name\": \"Black Mesa\", \"plan\": \"Enterprise\", \"owner\": \"P. Novak\", \"seats\": 104, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}]"
  loadingLabel="Advanced example"
  errorLabel="Advanced example"
  retryLabel="Advanced example"
  caption="Column sizing"
  description="A richer configuration demonstrating additional content and hierarchy."
  searchable="false"
  searchPlaceholder="Enter a sample value"
  pageSize="5"
  pageSizes='[
    5,
    10,
    25
  ]'
  actions='[]'
  density="comfortable"
  emptyLabel="Advanced example"
  noResultsLabel="Advanced example"
  clearSearchLabel="Advanced example"
/>
Advanced

Comparison layout

layout="comparison" transposes the table: fields run down the left and each record gets its own column. The label column stays put while the rest scrolls sideways.

06
Live preview
Component usage.wrn
<DataTable
  size="lg"
  columns="[{\"key\": \"name\", \"label\": \"Plan\"}, {\"key\": \"plan\", \"label\": \"Tier\"}, {\"key\": \"owner\", \"label\": \"Owner\"}, {\"key\": \"seats\", \"label\": \"Seats\", \"align\": \"end\", \"type\": \"number\"}, {\"key\": \"status\", \"label\": \"Status\", \"align\": \"center\"}]"
  rows="[{\"id\": 1, \"name\": \"Northwind\", \"plan\": \"Scale\", \"owner\": \"A. Okafor\", \"seats\": 6, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 2, \"name\": \"Acme Industrial\", \"plan\": \"Team\", \"owner\": \"R. Silva\", \"seats\": 13, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 3, \"name\": \"Globex\", \"plan\": \"Enterprise\", \"owner\": \"M. Chen\", \"seats\": 20, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 4, \"name\": \"Initech\", \"plan\": \"Starter\", \"owner\": \"J. Dubois\", \"seats\": 27, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 5, \"name\": \"Umbrella\", \"plan\": \"Scale\", \"owner\": \"P. Novak\", \"seats\": 34, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 6, \"name\": \"Stark Labs\", \"plan\": \"Team\", \"owner\": \"A. Okafor\", \"seats\": 41, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 7, \"name\": \"Wayne Foods\", \"plan\": \"Enterprise\", \"owner\": \"R. Silva\", \"seats\": 48, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 8, \"name\": \"Soylent\", \"plan\": \"Starter\", \"owner\": \"M. Chen\", \"seats\": 55, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 9, \"name\": \"Hooli\", \"plan\": \"Scale\", \"owner\": \"J. Dubois\", \"seats\": 62, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 10, \"name\": \"Vehement\", \"plan\": \"Team\", \"owner\": \"P. Novak\", \"seats\": 69, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 11, \"name\": \"Massive Dynamic\", \"plan\": \"Enterprise\", \"owner\": \"A. Okafor\", \"seats\": 76, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 12, \"name\": \"Cyberdyne\", \"plan\": \"Starter\", \"owner\": \"R. Silva\", \"seats\": 83, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 13, \"name\": \"Tyrell\", \"plan\": \"Scale\", \"owner\": \"M. Chen\", \"seats\": 90, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 14, \"name\": \"Aperture\", \"plan\": \"Team\", \"owner\": \"J. Dubois\", \"seats\": 97, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 15, \"name\": \"Black Mesa\", \"plan\": \"Enterprise\", \"owner\": \"P. Novak\", \"seats\": 104, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}]"
  loadingLabel="Advanced example"
  errorLabel="Advanced example"
  retryLabel="Advanced example"
  caption="Feature"
  description="A richer configuration demonstrating additional content and hierarchy."
  searchable="false"
  searchPlaceholder="Enter a sample value"
  pageSize="3"
  pageSizes='[
    3,
    5,
    8
  ]'
  actions='[]'
  emptyLabel="Advanced example"
  noResultsLabel="Advanced example"
  clearSearchLabel="Advanced example"
  layout="comparison"
/>
Recommended

Sticky first column

With many columns the table scrolls sideways; stickyFirstColumn keeps the record name in view so a row never loses its label.

07
Live preview
Component usage.wrn
<DataTable
  size="lg"
  columns="[{\"key\": \"name\", \"label\": \"Account\", \"sortable\": true}, {\"key\": \"plan\", \"label\": \"Plan\", \"sortable\": true}, {\"key\": \"owner\", \"label\": \"Owner\"}, {\"key\": \"seats\", \"label\": \"Seats\", \"align\": \"end\", \"sortable\": true}, {\"key\": \"status\", \"label\": \"Status\", \"align\": \"center\"}]"
  rows="[{\"id\": 1, \"name\": \"Northwind\", \"plan\": \"Scale\", \"owner\": \"A. Okafor\", \"seats\": 6, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 2, \"name\": \"Acme Industrial\", \"plan\": \"Team\", \"owner\": \"R. Silva\", \"seats\": 13, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 3, \"name\": \"Globex\", \"plan\": \"Enterprise\", \"owner\": \"M. Chen\", \"seats\": 20, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 4, \"name\": \"Initech\", \"plan\": \"Starter\", \"owner\": \"J. Dubois\", \"seats\": 27, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 5, \"name\": \"Umbrella\", \"plan\": \"Scale\", \"owner\": \"P. Novak\", \"seats\": 34, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 6, \"name\": \"Stark Labs\", \"plan\": \"Team\", \"owner\": \"A. Okafor\", \"seats\": 41, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 7, \"name\": \"Wayne Foods\", \"plan\": \"Enterprise\", \"owner\": \"R. Silva\", \"seats\": 48, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 8, \"name\": \"Soylent\", \"plan\": \"Starter\", \"owner\": \"M. Chen\", \"seats\": 55, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 9, \"name\": \"Hooli\", \"plan\": \"Scale\", \"owner\": \"J. Dubois\", \"seats\": 62, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 10, \"name\": \"Vehement\", \"plan\": \"Team\", \"owner\": \"P. Novak\", \"seats\": 69, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 11, \"name\": \"Massive Dynamic\", \"plan\": \"Enterprise\", \"owner\": \"A. Okafor\", \"seats\": 76, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 12, \"name\": \"Cyberdyne\", \"plan\": \"Starter\", \"owner\": \"R. Silva\", \"seats\": 83, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 13, \"name\": \"Tyrell\", \"plan\": \"Scale\", \"owner\": \"M. Chen\", \"seats\": 90, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 14, \"name\": \"Aperture\", \"plan\": \"Team\", \"owner\": \"J. Dubois\", \"seats\": 97, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 15, \"name\": \"Black Mesa\", \"plan\": \"Enterprise\", \"owner\": \"P. Novak\", \"seats\": 104, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}]"
  loadingLabel="Advanced example"
  errorLabel="Advanced example"
  retryLabel="Advanced example"
  caption="Accounts"
  description="Scroll the table horizontally on a narrow screen."
  searchPlaceholder="Enter a sample value"
  pageSize="5"
  pageSizes='[
    5,
    10,
    25
  ]'
  actions='[]'
  emptyLabel="Advanced example"
  noResultsLabel="Advanced example"
  clearSearchLabel="Advanced example"
  stickyFirstColumn="true"
/>
Compact

Compact density, no pagination

Every row at once, tighter rows, no footer.

08
Live preview
Component usage.wrn
<DataTable
  size="lg"
  columns="[{\"key\": \"name\", \"label\": \"Account\", \"sortable\": true}, {\"key\": \"plan\", \"label\": \"Plan\", \"sortable\": true}, {\"key\": \"owner\", \"label\": \"Owner\"}, {\"key\": \"seats\", \"label\": \"Seats\", \"align\": \"end\", \"sortable\": true}, {\"key\": \"status\", \"label\": \"Status\", \"align\": \"center\"}]"
  rows="[{\"id\": 1, \"name\": \"Northwind\", \"plan\": \"Scale\", \"owner\": \"A. Okafor\", \"seats\": 6, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 2, \"name\": \"Acme Industrial\", \"plan\": \"Team\", \"owner\": \"R. Silva\", \"seats\": 13, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 3, \"name\": \"Globex\", \"plan\": \"Enterprise\", \"owner\": \"M. Chen\", \"seats\": 20, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 4, \"name\": \"Initech\", \"plan\": \"Starter\", \"owner\": \"J. Dubois\", \"seats\": 27, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 5, \"name\": \"Umbrella\", \"plan\": \"Scale\", \"owner\": \"P. Novak\", \"seats\": 34, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 6, \"name\": \"Stark Labs\", \"plan\": \"Team\", \"owner\": \"A. Okafor\", \"seats\": 41, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 7, \"name\": \"Wayne Foods\", \"plan\": \"Enterprise\", \"owner\": \"R. Silva\", \"seats\": 48, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 8, \"name\": \"Soylent\", \"plan\": \"Starter\", \"owner\": \"M. Chen\", \"seats\": 55, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 9, \"name\": \"Hooli\", \"plan\": \"Scale\", \"owner\": \"J. Dubois\", \"seats\": 62, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 10, \"name\": \"Vehement\", \"plan\": \"Team\", \"owner\": \"P. Novak\", \"seats\": 69, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 11, \"name\": \"Massive Dynamic\", \"plan\": \"Enterprise\", \"owner\": \"A. Okafor\", \"seats\": 76, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 12, \"name\": \"Cyberdyne\", \"plan\": \"Starter\", \"owner\": \"R. Silva\", \"seats\": 83, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}, {\"id\": 13, \"name\": \"Tyrell\", \"plan\": \"Scale\", \"owner\": \"M. Chen\", \"seats\": 90, \"status\": \"Active\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--success\\">Active</span>\"}, {\"id\": 14, \"name\": \"Aperture\", \"plan\": \"Team\", \"owner\": \"J. Dubois\", \"seats\": 97, \"status\": \"Trial\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>\"}, {\"id\": 15, \"name\": \"Black Mesa\", \"plan\": \"Enterprise\", \"owner\": \"P. Novak\", \"seats\": 104, \"status\": \"Past due\", \"statusHtml\": \"<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>\"}]"
  loadingLabel="Advanced example"
  errorLabel="Advanced example"
  retryLabel="Advanced example"
  caption="All accounts"
  description="A richer configuration demonstrating additional content and hierarchy."
  searchable="false"
  searchPlaceholder="Enter a sample value"
  paginated="false"
  pageSize="31"
  pageSizes='[
    5,
    10,
    25
  ]'
  actions='[]'
  striped="false"
  density="compact"
  emptyLabel="Advanced example"
  noResultsLabel="Advanced example"
  clearSearchLabel="Advanced example"
/>
Advanced

Empty state

What a table shows before any records arrive.

09
Live preview
Component usage.wrn
<DataTable
  size="lg"
  columns="[{\"key\": \"name\", \"label\": \"Account\", \"sortable\": true}, {\"key\": \"plan\", \"label\": \"Plan\", \"sortable\": true}, {\"key\": \"owner\", \"label\": \"Owner\"}, {\"key\": \"seats\", \"label\": \"Seats\", \"align\": \"end\", \"sortable\": true}, {\"key\": \"status\", \"label\": \"Status\", \"align\": \"center\"}]"
  rows="[]"
  loadingLabel="Advanced example"
  errorLabel="Advanced example"
  retryLabel="Advanced example"
  caption="Accounts"
  description="A richer configuration demonstrating additional content and hierarchy."
  searchPlaceholder="Enter a sample value"
  pageSize="35"
  pageSizes='[
    5,
    10,
    25
  ]'
  actions='[]'
  emptyLabel="No accounts match this filter yet"
  noResultsLabel="Advanced example"
  clearSearchLabel="Advanced example"
/>
Component outputs

Receive every typed component output

Use declarative output handlers in .wrn files or register a direct output handler from JavaScript. The canonical API exposes payload and does not require event.detail.

@sortFires when the component emits the sort event.
@searchFires when the current search query changes.
@pageChangeFires when the component emits the pageChange event.
@selectFires when an item, option, card, or result is selected.
@changeFires when the component's committed value or selection changes.
@rowClickFires when the component emits the rowClick event.
@actionFires when the component's primary or item action is activated.
@requestFires when the component emits the request event.
Declarative handlers.wrn
<DataTable
  @sort='console.log(payload)'
  @search='console.log(payload)'
  @pageChange='console.log(payload)'
  @select='console.log(payload)'
  @change='console.log(payload)'
  @rowClick='console.log(payload)'
  @action='console.log(payload)'
  @request='console.log(payload)'
/>
Direct output handlers.js
import { registerOutputHandler } from "@wrnexus/csr/outputs"

const component = document.querySelector("[data-ui-component=\"DataTable\"], [data-component=\"DataTable\"]")

if (component) registerOutputHandler(component, "sort", (payload) => {
  console.log("sort", payload)
})

if (component) registerOutputHandler(component, "search", (payload) => {
  console.log("search", payload)
})

if (component) registerOutputHandler(component, "pageChange", (payload) => {
  console.log("pageChange", payload)
})

if (component) registerOutputHandler(component, "select", (payload) => {
  console.log("select", payload)
})

if (component) registerOutputHandler(component, "change", (payload) => {
  console.log("change", payload)
})

if (component) registerOutputHandler(component, "rowClick", (payload) => {
  console.log("rowClick", payload)
})

if (component) registerOutputHandler(component, "action", (payload) => {
  console.log("action", payload)
})

if (component) registerOutputHandler(component, "request", (payload) => {
  console.log("request", payload)
})
Component API

Props and configuration

All content and behavior shown above is supplied through these props and slots.

PropTypeDefaultRequired
colorstring"primary"No
sizestring"default"No
columnsunknown[][]No
rowsunknown[][]No
rowKeystring"id"No
remotebooleanfalseNo
loadingLabelstring"Loading"No
errorLabelstring"Could not load this data"No
retryLabelstring"Try again"No
captionstring""No
descriptionstring""No
searchablebooleantrueNo
searchPlaceholderstring"Search"No
paginatedbooleantrueNo
pageSizenumber10No
paginationStylestring"compact"No
pageSizesnumber[][10, 25, 50]No
selectablebooleanfalseNo
actionsunknown[][]No
stripedbooleantrueNo
borderedbooleantrueNo
gridlinesstring"rows"No
densitystring"default"No
emptyLabelstring"No records to show"No
noResultsLabelstring"No records match your search"No
clearSearchLabelstring"Clear search"No
stickyFirstColumnbooleanfalseNo
layoutstring"rows"No
classstring""No
} }