Skip to main content
Urbicon UI

Client Processing

The table sorts, filters, searches and pages your rows in the browser. Pass an array, and every control on the page works.

Hand over the rows

processing: 'client' means the table does the work. Search, filters, sorting, grouping and paging all happen in the browser, on the array you passed. The items prop is the short way to say it:

An array and some columns

Type in the search field, click a header, page through. Nothing else to wire.
Employee
Role
Department
Location
Emma Wilson
Staff Engineer
Platform
Berlin
Liam Chen
Product Designer
Design
Hamburg
Sofia Martinez
Engineering Manager
Platform
Munich
James Park
Frontend Developer
Product
Remote
Aisha Patel
Data Scientist
Data
Berlin
<script lang="ts">
  import { Table } from '@urbicon-ui/table';
  import { employees } from './data';

  const columns = [
    { accessor: 'name', title: 'Employee', 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 }
  ];
</script>

<Table {columns} items={employees} viewDefaults={{ pageSize: 5 }} />

You don't need source for this. Reach for it once a loading or an error state comes into it.

Rows that arrive with the page are the same case. A SvelteKit load has them ready before the page renders, so the first screen is server-rendered and the table takes over from there:

Rows from a SvelteKit load

No fetch in the browser, no empty first paint.
// +page.server.ts
export const load = async () => ({ employees: await db.employees.findMany() });

// +page.svelte
<script lang="ts">
  import { Table } from '@urbicon-ui/table';
  import { columns } from './columns';

  let { data } = $props();
</script>

<Table {columns} items={data.employees} />

While the rows are loading

When you fetch the rows yourself, source carries the two states the table renders for you:

Your fetch, the table's states

<script lang="ts">
  import { Table } from '@urbicon-ui/table';

  let items = $state<Employee[]>([]);
  let loading = $state(true);
  let error = $state<string | null>(null);

  fetchEmployees()
    .then((rows) => (items = rows))
    .catch((e) => (error = e.message))
    .finally(() => (loading = false));
</script>

<Table {columns} source={{ processing: 'client', items, loading, error }} />

The table shows its loading row while loading is true, and its error state with whatever error holds as the message. You decide when each applies. The rows keep being sorted, filtered and paged in the browser throughout.

source wins over items

Both props reach the same rows. When you pass both, the table reads source and the items prop does nothing.

When the browser runs out

The whole set sits in memory, and every sort touches all of it. A few thousand rows is comfortable. A hundred thousand is a tab that stops responding.

Two ways out, and they answer different questions. If the rows still fit and only the rendering hurts, keep client processing and draw fewer of them: Virtual Scrolling. If the set is too large to send at all, the backend sorts and pages it instead: Server Processing.