Data Table
Tier 2: Light-DOMHigh-performance table with sorting, search filter, and pagination
Installation
$npx nnui add data-table
copy$npm install nnui-kit
copyInteractive Preview & Code
0 selected
0 of 8 selected
| Invoice ⇅ | Status ⇅ | Client ⇅ | Amount ⇅ | |
|---|---|---|---|---|
| INV-001 | Paid | Acme Corp | $2,450.00 | |
| INV-002 | Pending | Global Media | $850.00 | |
| INV-003 | Overdue | Stripe Partners | $1,200.00 | |
| INV-004 | Paid | Vercel Inc | $4,600.00 | |
| INV-005 | Processing | Supabase Co | $3,150.00 | |
| INV-006 | Paid | Linear App | $920.00 | |
| INV-007 | Pending | Raycast Ltd | $1,780.00 | |
| INV-008 | Paid | Tailwind Labs | $5,200.00 |
Showing 1 to 4 of 8 rows
<ui-data-table page-size="4" sortable filterable selectable>
<div data-part="toolbar">
<input data-part="search" data-ui="input" placeholder="Search rows...">
<span data-part="selection-count">0 selected</span>
</div>
<div data-part="table-container">
<table>
<thead>
<tr>
<th><input type="checkbox" data-part="select-all"></th>
<th data-sortable="true" data-sort-key="invoice">Invoice <span data-part="sort-icon">⇅</span></th>
<th data-sortable="true" data-sort-key="status">Status <span data-part="sort-icon">⇅</span></th>
<th data-sortable="true" data-sort-key="client">Client <span data-part="sort-icon">⇅</span></th>
<th data-sortable="true" data-sort-key="amount">Amount <span data-part="sort-icon">⇅</span></th>
</tr>
</thead>
<tbody data-part="tbody">
<tr data-row-id="1">
<td><input type="checkbox" data-part="row-select"></td>
<td>INV-001</td>
<td>Paid</td>
<td>Acme Corp</td>
<td>$2,450.00</td>
</tr>
</tbody>
</table>
</div>
<div data-part="pagination">
<span data-part="pagination-info"></span>
<button data-part="prev-btn" data-ui="button" data-variant="outline" data-size="sm">Previous</button>
<button data-part="next-btn" data-ui="button" data-variant="outline" data-size="sm">Next</button>
</div>
</ui-data-table>import React, { useState } from 'react';
import 'nnui-kit'; // Registers universal Light-DOM elements
export default function DatatableDemo() {
const [tab, setTab] = useState('all');
const [val, setVal] = useState(50);
const [selected, setSelected] = useState('');
const [query, setQuery] = useState('');
return (
<ui-data-table page-size="4" sortable filterable selectable>
<div data-part="toolbar">
<input data-part="search" data-ui="input" placeholder="Search rows..." />
<span data-part="selection-count">0 selected</span>
</div>
<div data-part="table-container">
<table>
<thead>
<tr>
<th><input type="checkbox" data-part="select-all" /></th>
<th data-sortable="true" data-sort-key="invoice">Invoice <span data-part="sort-icon">⇅</span></th>
<th data-sortable="true" data-sort-key="status">Status <span data-part="sort-icon">⇅</span></th>
<th data-sortable="true" data-sort-key="client">Client <span data-part="sort-icon">⇅</span></th>
<th data-sortable="true" data-sort-key="amount">Amount <span data-part="sort-icon">⇅</span></th>
</tr>
</thead>
<tbody data-part="tbody">
<tr data-row-id="1">
<td><input type="checkbox" data-part="row-select" /></td>
<td>INV-001</td>
<td>Paid</td>
<td>Acme Corp</td>
<td>$2,450.00</td>
</tr>
</tbody>
</table>
</div>
<div data-part="pagination">
<span data-part="pagination-info"></span>
<button data-part="prev-btn" data-ui="button" data-variant="outline" data-size="sm">Previous</button>
<button data-part="next-btn" data-ui="button" data-variant="outline" data-size="sm">Next</button>
</div>
</ui-data-table>
);
}<script setup>
import { ref } from 'vue';
import 'nnui-kit'; // Registers universal Light-DOM elements
const activeTab = ref('all');
const sliderValue = ref(50);
const selected = ref('');
const query = ref('');
const handleSelect = (val) => {
console.log('Selected:', val);
};
</script>
<template>
<ui-data-table page-size="4" sortable filterable selectable>
<div data-part="toolbar">
<input data-part="search" data-ui="input" placeholder="Search rows...">
<span data-part="selection-count">0 selected</span>
</div>
<div data-part="table-container">
<table>
<thead>
<tr>
<th><input type="checkbox" data-part="select-all"></th>
<th data-sortable="true" data-sort-key="invoice">Invoice <span data-part="sort-icon">⇅</span></th>
<th data-sortable="true" data-sort-key="status">Status <span data-part="sort-icon">⇅</span></th>
<th data-sortable="true" data-sort-key="client">Client <span data-part="sort-icon">⇅</span></th>
<th data-sortable="true" data-sort-key="amount">Amount <span data-part="sort-icon">⇅</span></th>
</tr>
</thead>
<tbody data-part="tbody">
<tr data-row-id="1">
<td><input type="checkbox" data-part="row-select"></td>
<td>INV-001</td>
<td>Paid</td>
<td>Acme Corp</td>
<td>$2,450.00</td>
</tr>
</tbody>
</table>
</div>
<div data-part="pagination">
<span data-part="pagination-info"></span>
<button data-part="prev-btn" data-ui="button" data-variant="outline" data-size="sm">Previous</button>
<button data-part="next-btn" data-ui="button" data-variant="outline" data-size="sm">Next</button>
</div>
</ui-data-table>
</template><script>
import 'nnui-kit';
let activeTab = $state('all');
let sliderValue = $state(50);
let selected = $state('');
</script>
<ui-data-table page-size="4" sortable filterable selectable>
<div data-part="toolbar">
<input data-part="search" data-ui="input" placeholder="Search rows...">
<span data-part="selection-count">0 selected</span>
</div>
<div data-part="table-container">
<table>
<thead>
<tr>
<th><input type="checkbox" data-part="select-all"></th>
<th data-sortable="true" data-sort-key="invoice">Invoice <span data-part="sort-icon">⇅</span></th>
<th data-sortable="true" data-sort-key="status">Status <span data-part="sort-icon">⇅</span></th>
<th data-sortable="true" data-sort-key="client">Client <span data-part="sort-icon">⇅</span></th>
<th data-sortable="true" data-sort-key="amount">Amount <span data-part="sort-icon">⇅</span></th>
</tr>
</thead>
<tbody data-part="tbody">
<tr data-row-id="1">
<td><input type="checkbox" data-part="row-select"></td>
<td>INV-001</td>
<td>Paid</td>
<td>Acme Corp</td>
<td>$2,450.00</td>
</tr>
</tbody>
</table>
</div>
<div data-part="pagination">
<span data-part="pagination-info"></span>
<button data-part="prev-btn" data-ui="button" data-variant="outline" data-size="sm">Previous</button>
<button data-part="next-btn" data-ui="button" data-variant="outline" data-size="sm">Next</button>
</div>
</ui-data-table>How to Utilize
NNUI-KIT components live directly in the document Light DOM. You can use them via declarative HTML, with our first-class React wrapper, or platform-natively in Svelte, Vue, Astro, and HTMX:
React 18 & 19 (Next.js 15 App Router)
import 'nnui-kit/css';
import 'nnui-kit/client';
import { DataTable } from 'nnui-kit/react';Universal Web Components (Vue 3, Svelte 5, Astro, HTMX)
<!-- Automatically registers <ui-data-table> and <nnui-data-table> -->
import 'nnui-kit/css';
import 'nnui-kit/client';Platform Features
Zero Shadow DOM
Element lives in the Light DOM. Tailwind and global CSS apply directly.
persist="url | local"
Add declarative persistence attribute to auto-sync state with URL search params.
sync="cross-tab"
Synchronize changes across all open browser tabs via BroadcastChannel.