Skip to main content
Urbicon UI

Table

A data table that sorts, filters, groups and pages your rows, in the browser or against your backend. Becomes a card list when its own container gets too narrow for a grid.

Playground

Name
Role
Department
Location
Emma Wilson
Staff Engineer
Platform
Berlin
Liam Chen
Product Designer
Design
Hamburg
Sofia Martinez
Eng. Manager
Platform
Munich
James Park
Frontend Dev
Product
Remote
Aisha Patel
Data Scientist
Data
Berlin
Variant
Size
Selection mode
<script lang="ts">
  import { createTableView, Table } from '@urbicon-ui/table';
  import '@urbicon-ui/table/style/index.css';

  const columns = [
    { accessor: 'name', title: 'Name', sortable: true, searchable: true },
    { accessor: 'role', title: 'Role', sortable: true, searchable: true },
    { accessor: 'department', title: 'Department', sortable: true, groupable: true },
    { accessor: 'location', title: 'Location', sortable: true }
  ];
  const items = [
    { id: 1, name: 'Emma Wilson', role: 'Staff Engineer', department: 'Platform', location: 'Berlin' },
    { id: 2, name: 'Liam Chen', role: 'Product Designer', department: 'Design', location: 'Hamburg' },
    { id: 3, name: 'Sofia Martinez', role: 'Eng. Manager', department: 'Platform', location: 'Munich' },
    { id: 4, name: 'James Park', role: 'Frontend Dev', department: 'Product', location: 'Remote' },
    { id: 5, name: 'Aisha Patel', role: 'Data Scientist', department: 'Data', location: 'Berlin' },
    { id: 6, name: 'Noah Kim', role: 'DevOps Engineer', department: 'Platform', location: 'Hamburg' },
    { id: 7, name: 'Olivia Brown', role: 'UX Researcher', department: 'Design', location: 'Munich' },
    { id: 8, name: 'Lucas Weber', role: 'Backend Dev', department: 'Product', location: 'Berlin' }
  ];
  const view = createTableView({ defaults: { pageSize: 5 } });
</script>

<Table
  {columns}
  {items}
  {view}
  cardsBelow="32rem"
  searchPlaceholder="Search team..."
/>

Column Factories

TableColumns builds a column with the cell component, the alignment and the flags already set, so a typed column is one call. For a column no factory covers, write the object yourself: Column Configuration.

Factory-Powered Table

Eight of the nine take (accessor, title, options); actions takes (title, options), since it reads no field.
Employee
Role
Status
Salary
Joined
Actions
EW
Emma Wilson
emma@acme.dev
Staff Engineer
Active
142,000.00
LC
Liam Chen
liam@acme.dev
Product Designer
Active
98,000.00
SM
Sofia Martinez
sofia@acme.dev
Engineering Manager
Active
165,000.00
JP
James Park
james@acme.dev
Frontend Developer
On leave
92,000.00
AP
Aisha Patel
aisha@acme.dev
Data Scientist
Active
128,000.00
NK
Noah Kim
noah@acme.dev
DevOps Engineer
Active
115,000.00
<script>
  import { Table, TableColumns, type Column } from '@urbicon-ui/table';

  type Employee = {
    id: number;
    name: string;
    role: string;
    status: string;
    salary: number;
    joinedAt: string;
  };

  // Rows are keyed by `id` when they have one, by array index otherwise.
  const items: Employee[] = [
    {
      id: 1, name: 'Emma Wilson', role: 'Staff Engineer',
      status: 'active', salary: 142000, joinedAt: '2021-03-15'
    }
    // …
  ];

  // The annotation is what checks the accessors: with `Column<Employee>[]`,
  // a first argument that is not a key of the row is a type error. Without
  // it, nothing checks them.
  const cols: Column<Employee>[] = [
    TableColumns.userAvatar('name', 'Employee'),
    TableColumns.text('role', 'Role'),
    // StatusBadge knows eleven statuses: active, inactive, pending, online,
    // offline, processing, completed, failed, draft, published, archived.
    // Anything else reads "Unknown" until you name it here.
    TableColumns.status('status', 'Status', {
      statusMap: {
        'on-leave': { intent: 'warning', text: 'On leave', icon: true },
        offboarding: { intent: 'neutral', text: 'Offboarding', icon: false }
      }
    }),
    TableColumns.number('salary', 'Salary'),
    TableColumns.date('joinedAt', 'Joined'),
    // Every handler receives the row, and each button follows its own handler:
    // pass onView and the view button appears, leave onDelete out and no delete
    // button renders. The showView / showEdit / showDelete flags are for the
    // two exceptions — rendering a button you handle elsewhere, or hiding one
    // you do handle.
    TableColumns.actions('Actions', {
      onView: (employee) => {},
      onEdit: (employee) => {}
    })
  ];
</script>

<Table
  {items}
  columns={cols}
  cardsBelow="36rem"
  viewDefaults={{ pageSize: 6 }}
  enableSmartFilter={false}
/>

All nine:

FactoryWhat it builds
textPlain text, with an optional formatter
numberRight-aligned, locale-aware number formatting
dateLocale-aware date formatting
statusColoured badge, centred and groupable
userAvatarAvatar next to the name
linkRenders the value as an anchor
copyClick-to-copy button, centred and unsortable
customThe value as text, with your own classes, wrapping and click handling
actionsView / edit / delete buttons; synthetic, no accessor

Where to go next

Who does the work. A few hundred rows sort, filter and page in the browser: Client Processing. Past a few thousand it becomes the backend's job, and you hand the table one page at a time: Server Processing. Give it a query function and it runs the fetch itself: Query Function.

What the reader can change. Six settings decide which rows they see: search, sort, page, page size, filters and grouping. They live in one view object (viewDefaults sets its starting values), and URL State puts it in the address bar, so a view can be reloaded, shared and read by the server. What each setting does is on Filtering & Search and Sorting, Grouping & Summaries.

Once the rows are on screen. Row Selection for acting on rows, Custom Cells for rendering them your way, Virtual Scrolling and Sticky Pinning for long lists.

API Reference

50 props
Prop
Type
Default
Description

01 Types

Local type definitions used by this component.

31 types
Name
Kind
Category
Used by
Description

Installation

Import

import { Table, TableColumns } from '@urbicon-ui/table';

Styles

Import the table theme CSS in your app's root layout or entry point.
import '@urbicon-ui/table/style/index.css';