EN
Getting started

DataTable examples

This page shows seven ways to use DataTable, with working code.

Each example is a table class and runs on its own page in the demo panel. All of them use the same order data (Order, 5,000 records). To put a table on a page, write the class as a Livewire component:

<livewire:tables.examples.basic-orders-table />

For a step-by-step guide, see DataTable; for every option, see DataTable: all options.

Basic

Start with this smallest table when you want to add search, sorting and pagination to a list page quickly.

use Acun\Ui\DataTable\Column;
use Acun\Ui\DataTable\DataTable;
use App\Models\Order;
use Illuminate\Database\Eloquent\Builder;

final class BasicOrdersTable extends DataTable
{
    public string $title = 'Orders';

    protected function query(): Builder
    {
        return Order::query();
    }

    protected function columns(): array
    {
        return [
            Column::make('Order no.', 'number')->searchable()->sortable()->nowrap(),
            Column::make('Customer', 'customer_name')->searchable('contains')->sortable(),
            Column::make('Product', 'product')->renderUsing(fn (Order $order) => $order->productName()),
            Column::make('Amount', 'amount')
                ->renderUsing(fn (Order $order) => $order->formattedAmount())
                ->align('right')
                ->sortable(),
            Column::make('Order date', 'ordered_at')->dateTime()->sortable(),
        ];
    }
}

You write only two methods: query() says which records to show, columns() which columns. A search box and the page size appear on top, sort arrows in the header and page numbers at the bottom. On phones the table scrolls sideways in its own box; the page does not overflow.

Demo page

Table for narrow screens

Use it for tables with many columns that must also be readable on phones.

final class ResponsiveOrdersTable extends DataTable
{
    protected function query(): Builder
    {
        return Order::query();
    }

    protected function columns(): array
    {
        return [
            Column::make('Order no.', 'number')->searchable()->sortable()->nowrap(),
            Column::make('Customer', 'customer_name')->searchable('contains')->sortable(),
            Column::make('Amount', 'amount')->renderUsing(fn (Order $order) => $order->formattedAmount())->align('right'),
            // Hidden on phones (below 38rem).
            Column::make('Status', 'status')->view('admin.tables.cells.order-status')->priority(3),
            Column::make('Order date', 'ordered_at')->dateTime()->priority(3),
            // Hidden on tablets and in narrow windows (below 52rem).
            Column::make('Product', 'product')->renderUsing(fn (Order $order) => $order->productName())->priority(4),
            Column::make('City', 'city')->priority(4),
        ];
    }

    // The (+) button only appears while a column is hidden because of the screen width.
    protected function rowDetails(): ?string
    {
        return 'responsive';
    }
}

priority() sets the width at which a column is hidden: priority(3) below 38rem (608 px), priority(4) below 52rem (832 px). As the window gets narrower, Product and City disappear first, then Status and Order date on phones. At that moment a (+) appears at the start of the row; it shows the whole row, hidden fields included, in a dialog.

Demo page

Scrolling table

Use it to show a table with many columns and rows at a fixed height, e.g. inside a panel or on a summary page.

final class ScrollOrdersTable extends DataTable
{
    protected function query(): Builder
    {
        return Order::query();
    }

    protected function columns(): array
    {
        return [/* 14 columns: no., customer, e-mail, product, unit price, quantity, amount, VAT, … */];
    }

    protected function scrollX(): bool
    {
        return true;
    }

    protected function scrollY(): ?string
    {
        return '400px';   // any CSS length: '28rem', 'min(70vh, 42rem)'…
    }
}

scrollX() keeps cells on one line; the wide table scrolls sideways in its own box. scrollY() gives the box a height; the table scrolls down inside it and the header row stays on top. On phones the box scrolls both ways with a finger; the page does not overflow.

To let a long text column (a note, an address) wrap, add wrap() to it: Column::make('Note', 'note')->wrap()->width('20rem').

Demo page

Server-side table

Every DataTable works this way; you do not need to do anything extra as the number of records grows. This example shows it explicitly, with filters and a default order.

final class OrdersTable extends DataTable
{
    public string $title = 'Orders';

    protected function query(): Builder
    {
        return Order::query();
    }

    protected function columns(): array
    {
        return [
            Column::make('Order no.', 'number')->searchable()->sortable(),
            Column::make('Customer', 'customer_name')->searchable('contains')->sortable(),
            Column::make('Amount', 'amount')->renderUsing(fn (Order $order) => $order->formattedAmount())->align('right')->sortable(),
            Column::make('Status', 'status')->view('admin.tables.cells.order-status'),
        ];
    }

    protected function filters(): array
    {
        return [
            Filter::multiSelect('Status', 'status')->options(OrderStatus::options())->inToolbar(),
            Filter::dateRange('Order date', 'ordered_at'),
        ];
    }

    protected function defaultSort(): ?array
    {
        return ['field' => 'ordered_at', 'direction' => 'desc'];
    }
}

When the user types, picks a filter, sorts or changes the page, the browser sends a small Livewire request (Ajax) to the server. The package adds that change to the query() query; the query runs in the database and only the rows of the open page go back to the browser. That is why 5,000 records and 5 million records open just as fast, as long as the search and sort columns have indexes (Search).

The search, sorting, filters and page are written to the address bar; reloading the page or sharing the link opens the same list. The second tab of the demo page builds the same list by hand with WithListing, without DataTable (List pages).

Demo page

Fixed header

Use it for long lists: while the page scrolls down, the user keeps seeing the column names and can change the sort order without scrolling back up.

final class FixedHeaderOrdersTable extends DataTable
{
    protected function query(): Builder
    {
        return Order::query();
    }

    protected function columns(): array
    {
        return [/* … */];
    }

    protected function fixedHeader(): bool
    {
        return true;
    }

    protected function defaultPerPage(): int
    {
        return 50;
    }
}

When the header row reaches the top bar, it stays there; at the end of the table it moves up with the table. The sort arrows and the "select all" box in the fixed header work. It also works in full screen and on phones. This feature needs the PRO JavaScript file (import '@acunsoft/acun-ui-pro' or @acunUiScripts).

Demo page

Fixed columns

Use it for wide tables that scroll sideways: while scrolling, the user does not lose which row they are looking at (e.g. the order no.) or the row's actions.

use Acun\Ui\DataTable\Actions\RowAction;

final class FixedColumnsOrdersTable extends DataTable
{
    protected function query(): Builder
    {
        return Order::query();
    }

    protected function columns(): array
    {
        return [/* 14 columns */];
    }

    protected function actions(): array
    {
        return [
            RowAction::make('detail')->label('Details')->icon('eye')
                ->action(fn (Order $order) => $this->dispatch('show-order', id: $order->id)),
        ];
    }

    protected function scrollX(): bool
    {
        return true;
    }

    // start: the checkbox, the (+) and the first 1 data column on the left; end: the actions column on the right.
    protected function fixedColumns(): array
    {
        return ['start' => 1, 'end' => true];
    }

    // Optional: the header stays fixed on the page too.
    protected function fixedHeader(): bool
    {
        return true;
    }
}

When the table scrolls sideways, the selection, (+) and Order no. columns stay on the left and the actions on the right; the columns in between pass underneath and a light shadow shows on the edge. On phones (below 640 px) only the left columns stay fixed. This feature also needs the PRO JavaScript file.

Demo page

Buttons and export

Use it to let the user download the list as CSV or JSON, save it as PDF, print it or copy it to the clipboard. PDF opens the print page; the browser's "Save as PDF" option creates the file.

use Acun\Ui\DataTable\Actions\ExportAction;
use Acun\Ui\DataTable\Actions\TableAction;
use Acun\Ui\DataTable\Actions\UtilityAction;

final class ButtonsOrdersTable extends DataTable
{
    protected function query(): Builder
    {
        return Order::query();
    }

    protected function columns(): array
    {
        return [/* … */];
    }

    protected function exportActions(): array
    {
        return [
            ExportAction::csv(),
            ExportAction::pdf(),
            ExportAction::json(),
            ExportAction::print(),
            ExportAction::copy(),
        ];
    }

    protected function utilityActions(): array
    {
        return [UtilityAction::refresh(), UtilityAction::columns()];
    }

    protected function headerActions(): array
    {
        return [
            TableAction::make('create')->label('New order')->icon('plus')->url(fn () => route('orders.create')),
        ];
    }
}

The toolbar shows Refresh, the column menu and an "Export" button, with a "New order" button at the far end. In the "Export" menu the user first picks the scope: this page, the filtered records, the selected rows or all of them. The file is built on the server with the table's own query; the search, filters and sort order are the same in the file. Files above 5,000 rows are built in the queue, and a download link arrives when they are ready. On phones the tools are grouped under a single "Actions" button.

Details: Actions and Export.

Demo page

Acun UIDesigned for people.