Data Grid

Interactive table with advanced capabilities.

Recent orders
Order Customer Date Amount
#1042 Priya Nair £128.00
#1041 Owen Clarke £64.50
#1040 Maya Singh £212.10
#1039 Leo Ferrante £45.00

Nothing here yet

New orders will appear in this table as they come in.

html

<div class="grid-demo" data-state="loaded" style="--accent:#BFD9F2;">
  <table class="grid-table grid-table--comfortable grid-table--stripes">
    <caption>Recent orders</caption>
    <thead>
      <tr>
        <th scope="col">Order</th>
        <th scope="col">Customer</th>
        <th scope="col" aria-sort="ascending">Date<span class="grid-table__sort-ind" aria-hidden="true"></span></th>
        <th scope="col">Amount</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th scope="row">#1042</th>
        <td>Priya Nair</td>
        <td><time datetime="2026-08-12T09:30:00Z">12 Aug, 9:30am</time></td>
        <td>£128.00</td>
      </tr>
      <tr>
        <th scope="row">#1041</th>
        <td>Owen Clarke</td>
        <td><time datetime="2026-08-11T14:05:00Z">11 Aug, 2:05pm</time></td>
        <td>£64.50</td>
      </tr>
      <tr>
        <th scope="row">#1040</th>
        <td>Maya Singh</td>
        <td><time datetime="2026-08-10T17:45:00Z">10 Aug, 5:45pm</time></td>
        <td>£212.10</td>
      </tr>
      <tr>
        <th scope="row">#1039</th>
        <td>Leo Ferrante</td>
        <td><time datetime="2026-08-09T08:15:00Z">9 Aug, 8:15am</time></td>
        <td>£45.00</td>
      </tr>
    </tbody>
  </table>
  <div class="grid-empty">
    <svg viewBox="0 0 48 48" width="40" height="40" aria-hidden="true">
      <rect x="6" y="10" width="36" height="28" rx="3" fill="none" stroke="#241430" stroke-width="2"/>
      <path d="M6 18h36" stroke="#241430" stroke-width="2"/>
      <path d="M16 27l6 6 10-12" fill="none" stroke="#241430" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" opacity=".35"/>
    </svg>
    <p class="grid-empty__title">Nothing here yet</p>
    <p class="grid-empty__sub">New orders will appear in this table as they come in.</p>
  </div>
</div>
<style>
.grid-demo { font: 14px/1.5 system-ui, sans-serif; color: #241430; background: #FAF7F2; border: 1.5px solid #24143022; border-radius: 12px; padding: 4px; box-sizing: border-box; }
.grid-table { width: 100%; border-collapse: collapse; }
.grid-table caption { text-align: left; font-size: 15px; font-weight: 600; padding: 10px 12px 6px; }
.grid-table th, .grid-table td { text-align: left; border-bottom: 1px solid #24143018; }
.grid-table--comfortable th, .grid-table--comfortable td { padding: 10px 12px; }
.grid-table--compact th, .grid-table--compact td { padding: 4px 12px; font-size: 13px; }
.grid-table thead th { font-size: 12px; text-transform: uppercase; letter-spacing: .04em; color: #24143099; }
.grid-table th[aria-sort="ascending"], .grid-table th[aria-sort="descending"] { color: var(--accent); }
.grid-table__sort-ind::after { margin-left: 4px; font-size: 10px; }
.grid-table th[aria-sort="ascending"] .grid-table__sort-ind::after { content: "▲"; }
.grid-table th[aria-sort="descending"] .grid-table__sort-ind::after { content: "▼"; }
.grid-table--stripes tbody tr:nth-child(even) { background: #BFD9F218; }
.grid-empty { display: none; padding: 32px 12px; text-align: center; }
.grid-empty__title { margin: 10px 0 4px; font-weight: 600; }
.grid-empty__sub { margin: 0; font-size: 13px; color: #241430aa; }
.grid-demo[data-state="empty"] .grid-table { display: none; }
.grid-demo[data-state="empty"] .grid-empty { display: block; }
</style>

tailwind

<div class="grid-tw-demo font-sans text-sm text-[#241430] bg-[#FAF7F2] border border-[#24143022] rounded-xl p-1 box-border" data-state="loaded" style="--accent:#BFD9F2;">
  <table class="grid-tw-table grid-tw-table--comfortable grid-tw-table--stripes w-full border-collapse">
    <caption class="text-left text-[15px] font-semibold px-3 pt-2.5 pb-1.5">Recent orders</caption>
    <thead>
      <tr>
        <th scope="col" class="text-left text-[11px] uppercase tracking-wide text-[#241430]/60 border-b border-[#24143018]">Order</th>
        <th scope="col" class="text-left text-[11px] uppercase tracking-wide text-[#241430]/60 border-b border-[#24143018]">Customer</th>
        <th scope="col" aria-sort="ascending" class="grid-tw-table__sort text-left text-[11px] uppercase tracking-wide border-b border-[#24143018]">Date<span class="grid-tw-table__sort-ind" aria-hidden="true"></span></th>
        <th scope="col" class="text-left text-[11px] uppercase tracking-wide text-[#241430]/60 border-b border-[#24143018]">Amount</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th scope="row" class="text-left font-medium border-b border-[#24143018]">#1042</th>
        <td class="border-b border-[#24143018]">Priya Nair</td>
        <td class="border-b border-[#24143018]"><time datetime="2026-08-12T09:30:00Z">12 Aug, 9:30am</time></td>
        <td class="border-b border-[#24143018]">£128.00</td>
      </tr>
      <tr>
        <th scope="row" class="text-left font-medium border-b border-[#24143018]">#1041</th>
        <td class="border-b border-[#24143018]">Owen Clarke</td>
        <td class="border-b border-[#24143018]"><time datetime="2026-08-11T14:05:00Z">11 Aug, 2:05pm</time></td>
        <td class="border-b border-[#24143018]">£64.50</td>
      </tr>
      <tr>
        <th scope="row" class="text-left font-medium border-b border-[#24143018]">#1040</th>
        <td class="border-b border-[#24143018]">Maya Singh</td>
        <td class="border-b border-[#24143018]"><time datetime="2026-08-10T17:45:00Z">10 Aug, 5:45pm</time></td>
        <td class="border-b border-[#24143018]">£212.10</td>
      </tr>
      <tr>
        <th scope="row" class="text-left font-medium">#1039</th>
        <td>Leo Ferrante</td>
        <td><time datetime="2026-08-09T08:15:00Z">9 Aug, 8:15am</time></td>
        <td>£45.00</td>
      </tr>
    </tbody>
  </table>
  <div class="grid-tw-empty text-center px-3 py-8">
    <svg viewBox="0 0 48 48" width="40" height="40" aria-hidden="true">
      <rect x="6" y="10" width="36" height="28" rx="3" fill="none" stroke="#241430" stroke-width="2"/>
      <path d="M6 18h36" stroke="#241430" stroke-width="2"/>
      <path d="M16 27l6 6 10-12" fill="none" stroke="#241430" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" opacity=".35"/>
    </svg>
    <p class="mt-2.5 mb-1 font-semibold">Nothing here yet</p>
    <p class="m-0 text-[13px] text-[#241430]/65">New orders will appear in this table as they come in.</p>
  </div>
</div>
<style>
.grid-tw-table--comfortable th, .grid-tw-table--comfortable td { padding: 10px 12px; }
.grid-tw-table--compact th, .grid-tw-table--compact td { padding: 4px 12px; font-size: 13px; }
.grid-tw-table--stripes tbody tr:nth-child(even) { background: #BFD9F218; }
.grid-tw-table__sort[aria-sort="ascending"], .grid-tw-table__sort[aria-sort="descending"] { color: var(--accent); }
.grid-tw-table__sort-ind::after { margin-left: 4px; font-size: 10px; }
.grid-tw-table__sort[aria-sort="ascending"] .grid-tw-table__sort-ind::after { content: "▲"; }
.grid-tw-table__sort[aria-sort="descending"] .grid-tw-table__sort-ind::after { content: "▼"; }
.grid-tw-empty { display: none; }
.grid-tw-demo[data-state="empty"] .grid-tw-table { display: none; }
.grid-tw-demo[data-state="empty"] .grid-tw-empty { display: block; }
</style>

react

export function DataGrid({
  title = "Recent orders",
  sort = "ascending",
  density = "comfortable",
  state = "loaded",
  accent = "#BFD9F2",
  stripes = true,
}) {
  const rows = [
    { id: "#1042", customer: "Priya Nair", iso: "2026-08-12T09:30:00Z", date: "12 Aug, 9:30am", amount: "£128.00" },
    { id: "#1041", customer: "Owen Clarke", iso: "2026-08-11T14:05:00Z", date: "11 Aug, 2:05pm", amount: "£64.50" },
    { id: "#1040", customer: "Maya Singh", iso: "2026-08-10T17:45:00Z", date: "10 Aug, 5:45pm", amount: "£212.10" },
    { id: "#1039", customer: "Leo Ferrante", iso: "2026-08-09T08:15:00Z", date: "9 Aug, 8:15am", amount: "£45.00" },
  ];
  const pad = density === "compact" ? "4px 12px" : "10px 12px";
  const cellStyle = { textAlign: "left", borderBottom: "1px solid #24143018", padding: pad };
  if (state === "empty") {
    return (
      <div style={{ font: "14px/1.5 system-ui, sans-serif", color: "#241430", background: "#FAF7F2", border: "1.5px solid #24143022", borderRadius: 12, padding: "32px 12px", textAlign: "center", boxSizing: "border-box" }}>
        <p style={{ margin: "10px 0 4px", fontWeight: 600 }}>Nothing here yet</p>
        <p style={{ margin: 0, fontSize: 13, color: "#241430aa" }}>New orders will appear in this table as they come in.</p>
      </div>
    );
  }
  const arrow = sort === "ascending" ? "▲" : sort === "descending" ? "▼" : "";
  const dimColor = "#24143099";
  return (
    <div style={{ font: "14px/1.5 system-ui, sans-serif", color: "#241430", background: "#FAF7F2", border: "1.5px solid #24143022", borderRadius: 12, padding: 4, boxSizing: "border-box" }}>
      <table style={{ width: "100%", borderCollapse: "collapse" }}>
        <caption style={{ textAlign: "left", fontSize: 15, fontWeight: 600, padding: "10px 12px 6px" }}>{title}</caption>
        <thead>
          <tr>
            <th scope="col" style={{ ...cellStyle, fontSize: 12, textTransform: "uppercase", color: dimColor }}>Order</th>
            <th scope="col" style={{ ...cellStyle, fontSize: 12, textTransform: "uppercase", color: dimColor }}>Customer</th>
            <th scope="col" aria-sort={sort} style={{ ...cellStyle, fontSize: 12, textTransform: "uppercase", color: arrow ? accent : dimColor }}>Date {arrow}</th>
            <th scope="col" style={{ ...cellStyle, fontSize: 12, textTransform: "uppercase", color: dimColor }}>Amount</th>
          </tr>
        </thead>
        <tbody>
          {rows.map((r, i) => (
            <tr key={r.id} style={{ background: stripes && i % 2 === 1 ? "#BFD9F218" : "transparent" }}>
              <th scope="row" style={cellStyle}>{r.id}</th>
              <td style={cellStyle}>{r.customer}</td>
              <td style={cellStyle}><time dateTime={r.iso}>{r.date}</time></td>
              <td style={cellStyle}>{r.amount}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}