UWC Components
  • Default
  • Material
  • Fluent

On this page

Data Table

A feature-rich data table with sort, multi-sort, column filters, pagination, row selection, row expansion, inline edit, column resize, column visibility, CSV export, and row actions.

uwc-datatable is a full-featured data grid. Pass columns (column definitions) and data (row array) as properties. It handles sorting, filtering, pagination, row selection, and more out of the box.

Import

All components

import '@uwc/components';

Selected component (Lit / Angular / Vue)

import { UwcDatatable } from '@uwc/components/datatable';
customElements.define('uwc-datatable', UwcDatatable);

React

import { UwcDatatable } from '@uwc/components/react';

Usage

Lit

import { LitElement, html } from 'lit';
import { customElement } from 'lit/decorators.js';

@customElement('app-demo')
export class AppDemo extends LitElement {
  createRenderRoot() { return this; }
  columns = [{ field: 'name', header: 'Name' }, { field: 'role', header: 'Role' }];
  data    = [{ name: 'Alice', role: 'Admin' }, { name: 'Bob', role: 'User' }];
  render() {
    return html`<uwc-datatable .columns=${this.columns} .data=${this.data}></uwc-datatable>`;
  }
}

React

import React from 'react';
import { UwcDatatable } from '@uwc/components/react';

const columns = [{ field: 'name', header: 'Name' }, { field: 'role', header: 'Role' }];
const data    = [{ name: 'Alice', role: 'Admin' }, { name: 'Bob', role: 'User' }];

export default function App() {
  return <UwcDatatable columns={columns} data={data} />;
}

Angular

import { Component } from '@angular/core';
import '@uwc/datatable';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `<uwc-datatable [columns]="columns" [data]="data"></uwc-datatable>`,
})
export class AppComponent {
  columns = [{ field: 'name', header: 'Name' }, { field: 'role', header: 'Role' }];
  data    = [{ name: 'Alice', role: 'Admin' }, { name: 'Bob', role: 'User' }];
}

Vue

import '@uwc/datatable';

export default {
  data() {
    return {
      columns: [{ field: 'name', header: 'Name' }, { field: 'role', header: 'Role' }],
      data:    [{ name: 'Alice', role: 'Admin' }, { name: 'Bob', role: 'User' }],
    };
  },
  template: `<uwc-datatable :columns="columns" :data="data"></uwc-datatable>`,
};

Basic Usage

import { LitElement, html } from 'lit';
import { customElement } from 'lit/decorators.js';

@customElement('datatable-overview-demo')
export class AppDemo extends LitElement {
  createRenderRoot() { return this; }

  columns = [
    { field: 'name',  header: 'Name',  sortable: true },
    { field: 'email', header: 'Email' },
    { field: 'role',  header: 'Role',  sortable: true },
  ];
  data = [
    { name: 'Alice Johnson', email: 'alice@example.com', role: 'Admin'     },
    { name: 'Bob Smith',     email: 'bob@example.com',   role: 'Developer' },
    { name: 'Carol White',   email: 'carol@example.com', role: 'Designer'  },
    { name: 'David Brown',   email: 'david@example.com', role: 'Manager'   },
    { name: 'Eve Davis',     email: 'eve@example.com',   role: 'Developer' },
  ];

  render() {
    return html`
      <uwc-datatable .columns=${this.columns} .data=${this.data} paginator rows="3"></uwc-datatable>
    `;
  }
}
import React from 'react';
import { UwcDatatable } from '@uwc/components/react';

const columns = [
  { field: 'name',  header: 'Name',  sortable: true },
  { field: 'email', header: 'Email' },
  { field: 'role',  header: 'Role',  sortable: true },
];
const data = [
  { name: 'Alice Johnson', email: 'alice@example.com', role: 'Admin'     },
  { name: 'Bob Smith',     email: 'bob@example.com',   role: 'Developer' },
  { name: 'Carol White',   email: 'carol@example.com', role: 'Designer'  },
  { name: 'David Brown',   email: 'david@example.com', role: 'Manager'   },
  { name: 'Eve Davis',     email: 'eve@example.com',   role: 'Developer' },
];

export default function App() {
  return <UwcDatatable columns={columns} data={data} paginator rows={3} />;
}
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <uwc-datatable [columns]="columns" [data]="data" paginator [rows]="3"></uwc-datatable>
  `
})
export class AppComponent {
  columns = [
    { field: 'name',  header: 'Name',  sortable: true },
    { field: 'email', header: 'Email' },
    { field: 'role',  header: 'Role',  sortable: true },
  ];
  data = [
    { name: 'Alice Johnson', email: 'alice@example.com', role: 'Admin'     },
    { name: 'Bob Smith',     email: 'bob@example.com',   role: 'Developer' },
    { name: 'Carol White',   email: 'carol@example.com', role: 'Designer'  },
  ];
}
export default {
  data() {
    return {
      columns: [
        { field: 'name',  header: 'Name',  sortable: true },
        { field: 'email', header: 'Email' },
        { field: 'role',  header: 'Role',  sortable: true },
      ],
      data: [
        { name: 'Alice Johnson', email: 'alice@example.com', role: 'Admin'     },
        { name: 'Bob Smith',     email: 'bob@example.com',   role: 'Developer' },
        { name: 'Carol White',   email: 'carol@example.com', role: 'Designer'  },
      ],
    };
  },
  template: `
    <uwc-datatable :columns="columns" :data="data" paginator :rows="3"></uwc-datatable>
  `
};

Simple Data Table

Pass columns and data as properties. Add sortable: true to column definitions to enable column sorting. Use paginator and rows to activate the built-in pager.

import { LitElement, html } from 'lit';
import { customElement } from 'lit/decorators.js';

@customElement('datatable-simple-demo')
export class AppDemo extends LitElement {
  createRenderRoot() { return this; }

  columns = [
    { field: 'name',    header: 'Name',    sortable: true },
    { field: 'country', header: 'Country', sortable: true },
    { field: 'company', header: 'Company'                 },
    { field: 'status',  header: 'Status'                  },
  ];
  data = [
    { name: 'Alice Johnson', country: 'USA',     company: 'Acme Corp',   status: 'Active'   },
    { name: 'Bob Smith',     country: 'UK',      company: 'Globex Inc',  status: 'Inactive' },
    { name: 'Carol White',   country: 'Canada',  company: 'Initech',     status: 'Active'   },
    { name: 'David Brown',   country: 'Germany', company: 'Umbrella Co', status: 'Pending'  },
    { name: 'Eve Davis',     country: 'France',  company: 'Cyberdyne',   status: 'Active'   },
  ];

  render() {
    return html`
      <uwc-datatable .columns=${this.columns} .data=${this.data} paginator rows="4"></uwc-datatable>
    `;
  }
}
import React from 'react';
import { UwcDatatable } from '@uwc/components/react';

const columns = [
  { field: 'name',    header: 'Name',    sortable: true },
  { field: 'country', header: 'Country', sortable: true },
  { field: 'company', header: 'Company'                 },
  { field: 'status',  header: 'Status'                  },
];
const data = [
  { name: 'Alice Johnson', country: 'USA',     company: 'Acme Corp',   status: 'Active'   },
  { name: 'Bob Smith',     country: 'UK',      company: 'Globex Inc',  status: 'Inactive' },
  { name: 'Carol White',   country: 'Canada',  company: 'Initech',     status: 'Active'   },
  { name: 'David Brown',   country: 'Germany', company: 'Umbrella Co', status: 'Pending'  },
];

export default function App() {
  return <UwcDatatable columns={columns} data={data} paginator rows={4} />;
}
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <uwc-datatable [columns]="columns" [data]="data" paginator [rows]="4"></uwc-datatable>
  `
})
export class AppComponent {
  columns = [
    { field: 'name',    header: 'Name',    sortable: true },
    { field: 'country', header: 'Country', sortable: true },
    { field: 'company', header: 'Company'                 },
    { field: 'status',  header: 'Status'                  },
  ];
  data = [
    { name: 'Alice Johnson', country: 'USA',    company: 'Acme Corp',  status: 'Active'   },
    { name: 'Bob Smith',     country: 'UK',     company: 'Globex Inc', status: 'Inactive' },
    { name: 'Carol White',   country: 'Canada', company: 'Initech',    status: 'Active'   },
  ];
}
export default {
  data() {
    return {
      columns: [
        { field: 'name',    header: 'Name',    sortable: true },
        { field: 'country', header: 'Country', sortable: true },
        { field: 'company', header: 'Company'                 },
        { field: 'status',  header: 'Status'                  },
      ],
      data: [
        { name: 'Alice Johnson', country: 'USA',    company: 'Acme Corp',  status: 'Active'   },
        { name: 'Bob Smith',     country: 'UK',     company: 'Globex Inc', status: 'Inactive' },
        { name: 'Carol White',   country: 'Canada', company: 'Initech',    status: 'Active'   },
      ],
    };
  },
  template: `
    <uwc-datatable :columns="columns" :data="data" paginator :rows="4"></uwc-datatable>
  `
};

Row selection

Set selection-mode to "single" or "multiple" to enable row selection. Pair with data-key (a unique field name) to track identity. The uwc-selection-change event fires with { selection }.

import { LitElement, html } from 'lit';
import { customElement, state } from 'lit/decorators.js';

@customElement('datatable-selection-demo')
export class AppDemo extends LitElement {
  createRenderRoot() { return this; }

  @state() private _selection: any[] = [];

  columns = [
    { field: 'name',   header: 'Name'   },
    { field: 'role',   header: 'Role'   },
    { field: 'status', header: 'Status' },
  ];
  data = [
    { id: 1, name: 'Alice',  role: 'Designer',  status: 'Active'   },
    { id: 2, name: 'Bob',    role: 'Developer', status: 'Active'   },
    { id: 3, name: 'Carol',  role: 'Manager',   status: 'Inactive' },
    { id: 4, name: 'David',  role: 'DevOps',    status: 'Active'   },
  ];

  render() {
    return html`
      <uwc-datatable
        .columns=${this.columns}
        .data=${this.data}
        selection-mode="multiple"
        data-key="id"
        @uwc-selection-change=${(e) => { this._selection = e.detail.selection; }}>
      </uwc-datatable>
      <p style="margin:.75rem 0 0;font-size:.8rem;font-family:monospace;color:#374151;">
        Selected: ${this._selection.map(r => r.name).join(', ') || 'none'}
      </p>
    `;
  }
}
import React, { useState } from 'react';
import { UwcDatatable } from '@uwc/components/react';

const columns = [
  { field: 'name',   header: 'Name'   },
  { field: 'role',   header: 'Role'   },
  { field: 'status', header: 'Status' },
];
const data = [
  { id: 1, name: 'Alice',  role: 'Designer',  status: 'Active'   },
  { id: 2, name: 'Bob',    role: 'Developer', status: 'Active'   },
  { id: 3, name: 'Carol',  role: 'Manager',   status: 'Inactive' },
  { id: 4, name: 'David',  role: 'DevOps',    status: 'Active'   },
];

export default function App() {
  const [selection, setSelection] = useState([]);
  return (
    <>
      <UwcDatatable
        columns={columns}
        data={data}
        selectionMode="multiple"
        dataKey="id"
        onUwcSelectionChange={(e) => setSelection(e.detail.selection)}
      />
      <p style={{ marginTop: '.75rem', fontSize: '.8rem', fontFamily: 'monospace', color: '#374151' }}>
        Selected: {selection.map(r => r.name).join(', ') || 'none'}
      </p>
    </>
  );
}
import { Component, signal, computed } from '@angular/core';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <uwc-datatable
      [columns]="columns"
      [data]="data"
      selection-mode="multiple"
      data-key="id"
      (uwc-selection-change)="selection.set($event.detail.selection)">
    </uwc-datatable>
    <p style="margin:.75rem 0 0;font-size:.8rem;font-family:monospace;color:#374151;">
      Selected: {{ selectionNames() || 'none' }}
    </p>
  `
})
export class AppComponent {
  readonly selection = signal<any[]>([]);
  readonly selectionNames = computed(() => this.selection().map(r => r.name).join(', '));

  columns = [
    { field: 'name',   header: 'Name'   },
    { field: 'role',   header: 'Role'   },
    { field: 'status', header: 'Status' },
  ];
  data = [
    { id: 1, name: 'Alice',  role: 'Designer',  status: 'Active'   },
    { id: 2, name: 'Bob',    role: 'Developer', status: 'Active'   },
    { id: 3, name: 'Carol',  role: 'Manager',   status: 'Inactive' },
    { id: 4, name: 'David',  role: 'DevOps',    status: 'Active'   },
  ];
}
export default {
  data() {
    return {
      selection: [],
      columns: [
        { field: 'name',   header: 'Name'   },
        { field: 'role',   header: 'Role'   },
        { field: 'status', header: 'Status' },
      ],
      data: [
        { id: 1, name: 'Alice',  role: 'Designer',  status: 'Active'   },
        { id: 2, name: 'Bob',    role: 'Developer', status: 'Active'   },
        { id: 3, name: 'Carol',  role: 'Manager',   status: 'Inactive' },
        { id: 4, name: 'David',  role: 'DevOps',    status: 'Active'   },
      ],
    };
  },
  computed: {
    selectionNames() {
      return this.selection.map(r => r.name).join(', ') || 'none';
    },
  },
  template: `
    <uwc-datatable
      :columns="columns"
      :data="data"
      selection-mode="multiple"
      data-key="id"
      @uwc-selection-change="selection = $event.detail.selection">
    </uwc-datatable>
    <p style="margin:.75rem 0 0;font-size:.8rem;font-family:monospace;color:#374151;">
      Selected: {{ selectionNames }}
    </p>
  `
};

Column filters

Add filter: true to any column definition to render a per-column text filter below the header. Filtering is case-insensitive and applied client-side.

import { LitElement, html } from 'lit';
import { customElement } from 'lit/decorators.js';

@customElement('datatable-filter-demo')
export class AppDemo extends LitElement {
  createRenderRoot() { return this; }

  columns = [
    { field: 'name',    header: 'Name',    sortable: true, filter: true },
    { field: 'country', header: 'Country', sortable: true, filter: true },
    { field: 'role',    header: 'Role',    sortable: true, filter: true },
    { field: 'status',  header: 'Status',                  filter: true },
  ];
  data = [
    { name: 'Alice Johnson', country: 'USA',     role: 'Designer',  status: 'Active'   },
    { name: 'Bob Smith',     country: 'UK',      role: 'Developer', status: 'Active'   },
    { name: 'Carol White',   country: 'Canada',  role: 'Manager',   status: 'Inactive' },
    { name: 'David Brown',   country: 'Germany', role: 'DevOps',    status: 'Active'   },
    { name: 'Eve Davis',     country: 'France',  role: 'Designer',  status: 'Pending'  },
    { name: 'Frank Lee',     country: 'USA',     role: 'Developer', status: 'Active'   },
  ];

  render() {
    return html`
      <uwc-datatable .columns=${this.columns} .data=${this.data} paginator rows="4"></uwc-datatable>
    `;
  }
}
import React from 'react';
import { UwcDatatable } from '@uwc/components/react';

const columns = [
  { field: 'name',    header: 'Name',    sortable: true, filter: true },
  { field: 'country', header: 'Country', sortable: true, filter: true },
  { field: 'role',    header: 'Role',    sortable: true, filter: true },
  { field: 'status',  header: 'Status',                  filter: true },
];
const data = [
  { name: 'Alice Johnson', country: 'USA',     role: 'Designer',  status: 'Active'   },
  { name: 'Bob Smith',     country: 'UK',      role: 'Developer', status: 'Active'   },
  { name: 'Carol White',   country: 'Canada',  role: 'Manager',   status: 'Inactive' },
  { name: 'David Brown',   country: 'Germany', role: 'DevOps',    status: 'Active'   },
  { name: 'Eve Davis',     country: 'France',  role: 'Designer',  status: 'Pending'  },
  { name: 'Frank Lee',     country: 'USA',     role: 'Developer', status: 'Active'   },
];

export default function App() {
  return <UwcDatatable columns={columns} data={data} paginator rows={4} />;
}
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <uwc-datatable [columns]="columns" [data]="data" paginator [rows]="4"></uwc-datatable>
  `
})
export class AppComponent {
  columns = [
    { field: 'name',    header: 'Name',    sortable: true, filter: true },
    { field: 'country', header: 'Country', sortable: true, filter: true },
    { field: 'role',    header: 'Role',    sortable: true, filter: true },
    { field: 'status',  header: 'Status',                  filter: true },
  ];
  data = [
    { name: 'Alice Johnson', country: 'USA',     role: 'Designer',  status: 'Active'   },
    { name: 'Bob Smith',     country: 'UK',      role: 'Developer', status: 'Active'   },
    { name: 'Carol White',   country: 'Canada',  role: 'Manager',   status: 'Inactive' },
    { name: 'David Brown',   country: 'Germany', role: 'DevOps',    status: 'Active'   },
    { name: 'Eve Davis',     country: 'France',  role: 'Designer',  status: 'Pending'  },
    { name: 'Frank Lee',     country: 'USA',     role: 'Developer', status: 'Active'   },
  ];
}
export default {
  data() {
    return {
      columns: [
        { field: 'name',    header: 'Name',    sortable: true, filter: true },
        { field: 'country', header: 'Country', sortable: true, filter: true },
        { field: 'role',    header: 'Role',    sortable: true, filter: true },
        { field: 'status',  header: 'Status',                  filter: true },
      ],
      data: [
        { name: 'Alice Johnson', country: 'USA',     role: 'Designer',  status: 'Active'   },
        { name: 'Bob Smith',     country: 'UK',      role: 'Developer', status: 'Active'   },
        { name: 'Carol White',   country: 'Canada',  role: 'Manager',   status: 'Inactive' },
        { name: 'David Brown',   country: 'Germany', role: 'DevOps',    status: 'Active'   },
        { name: 'Eve Davis',     country: 'France',  role: 'Designer',  status: 'Pending'  },
        { name: 'Frank Lee',     country: 'USA',     role: 'Developer', status: 'Active'   },
      ],
    };
  },
  template: `
    <uwc-datatable :columns="columns" :data="data" paginator :rows="4"></uwc-datatable>
  `
};

Loading state

Set loading to show a spinner overlay while data is being fetched asynchronously.

import { LitElement, html } from 'lit';
import { customElement, state } from 'lit/decorators.js';

@customElement('datatable-loading-demo')
export class AppDemo extends LitElement {
  createRenderRoot() { return this; }

  @state() private _loading = false;
  @state() private _data: any[] = [];

  columns = [
    { field: 'name',  header: 'Name'  },
    { field: 'role',  header: 'Role'  },
    { field: 'email', header: 'Email' },
  ];

  async _load() {
    this._loading = true;
    this._data = [];
    await new Promise(r => setTimeout(r, 1500));
    this._data = [
      { name: 'Alice Johnson', role: 'Admin',     email: 'alice@example.com' },
      { name: 'Bob Smith',     role: 'Developer', email: 'bob@example.com'   },
      { name: 'Carol White',   role: 'Designer',  email: 'carol@example.com' },
    ];
    this._loading = false;
  }

  render() {
    return html`
      <uwc-button label="Fetch data" @click=${this._load} style="margin-bottom:.75rem;display:block;"></uwc-button>
      <uwc-datatable .columns=${this.columns} .data=${this._data} ?loading=${this._loading}></uwc-datatable>
    `;
  }
}
import React, { useState } from 'react';
import { UwcDatatable, UwcButton } from '@uwc/components/react';

const columns = [
  { field: 'name',  header: 'Name'  },
  { field: 'role',  header: 'Role'  },
  { field: 'email', header: 'Email' },
];

export default function App() {
  const [loading, setLoading] = useState(false);
  const [data, setData] = useState([]);

  async function load() {
    setLoading(true);
    setData([]);
    await new Promise(r => setTimeout(r, 1500));
    setData([
      { name: 'Alice Johnson', role: 'Admin',     email: 'alice@example.com' },
      { name: 'Bob Smith',     role: 'Developer', email: 'bob@example.com'   },
      { name: 'Carol White',   role: 'Designer',  email: 'carol@example.com' },
    ]);
    setLoading(false);
  }

  return (
    <>
      <UwcButton label="Fetch data" onUwcClick={load} style={{ marginBottom: '.75rem', display: 'block' }} />
      <UwcDatatable columns={columns} data={data} loading={loading} />
    </>
  );
}
import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <uwc-button label="Fetch data" (click)="load()" style="margin-bottom:.75rem;display:block;"></uwc-button>
    <uwc-datatable [columns]="columns" [data]="data()" [loading]="loading()"></uwc-datatable>
  `
})
export class AppComponent {
  readonly loading = signal(false);
  readonly data = signal<any[]>([]);

  columns = [
    { field: 'name',  header: 'Name'  },
    { field: 'role',  header: 'Role'  },
    { field: 'email', header: 'Email' },
  ];

  async load() {
    this.loading.set(true);
    this.data.set([]);
    await new Promise(r => setTimeout(r, 1500));
    this.data.set([
      { name: 'Alice Johnson', role: 'Admin',     email: 'alice@example.com' },
      { name: 'Bob Smith',     role: 'Developer', email: 'bob@example.com'   },
      { name: 'Carol White',   role: 'Designer',  email: 'carol@example.com' },
    ]);
    this.loading.set(false);
  }
}
export default {
  data() {
    return {
      loading: false,
      data: [],
      columns: [
        { field: 'name',  header: 'Name'  },
        { field: 'role',  header: 'Role'  },
        { field: 'email', header: 'Email' },
      ],
    };
  },
  methods: {
    async load() {
      this.loading = true;
      this.data = [];
      await new Promise(r => setTimeout(r, 1500));
      this.data = [
        { name: 'Alice Johnson', role: 'Admin',     email: 'alice@example.com' },
        { name: 'Bob Smith',     role: 'Developer', email: 'bob@example.com'   },
        { name: 'Carol White',   role: 'Designer',  email: 'carol@example.com' },
      ];
      this.loading = false;
    },
  },
  template: `
    <uwc-button label="Fetch data" @click="load" style="margin-bottom:.75rem;display:block;"></uwc-button>
    <uwc-datatable :columns="columns" :data="data" :loading="loading"></uwc-datatable>
  `
};

Properties

Name Type Default Description
data Record<string, unknown>[] []
columns ColumnDef[] []
selectionMode SelectionMode 'multiple'
rowKey string 'id'
loading boolean false
rows number 20
rowsPerPageOptions number[] [10, 20, 50, 100]
scrollableHeight string '520px'
rowExpandable boolean false
stripedRows boolean true
resizableColumns boolean true
stateStorage string ''
emptyMessage string 'No records found.'
_pageSize number 20
_colPanelOpen boolean false
styles array [styles]