{datatable}

Rows as a complete HTML table - formatted numbers, a totals row, sorting and paging on the server.

Widgets Either form

Syntax

{datatable data='name', columns='a, b, c', labels='A, B, C', totals='c'}
{datatable data='name', format="c:currency('EUR')", sortable, striped, caption='Caption'}
{datatable data='name', rows=10, query='prefix_'}
{datatable totals='amount'} month,amount ... {/datatable}

Options

data
The rows: a {data} store, a page array or a _data file. As a pair the content is the rows instead - JSON, YAML, XML or CSV, told apart on sight.
columns
The fields shown, in their order. Every field of the first row when not given.
labels
The header of each column, in the order of columns. The field name as a headline when not given (order_date is Order Date).
format
A pipe per column, column:pipe separated by commas - total:money, date:date('j M').
totals
Columns summed in a totals row - over every row, not only the page shown.
sortable
Bare option: every header is a link that sorts on the server, ?sort=total&dir=desc.
rows
Rows per page, with page links below (?page=2).
query
A prefix for the request names sort, dir and page - for two tables on one page.
striped
Bare option: every other row shaded.
caption
The table's <caption>.

Description

{datatable} writes a whole table from rows of data: a header from the field names, a body, an optional totals row, header links that sort and page links that page - all on the server, without JavaScript. The rows come from data= (a {data} store, a page array or a _data file, as for {chart}), or, as a pair, from the content: JSON, YAML, XML or CSV, told apart on sight. The tag writes the table once - it does not iterate the rows like a data tag does.

Columns and numbers

columns= picks the fields and their order, labels= gives their headers; otherwise every field of the first row shows, its name made a headline. A column whose values are all numbers is right-aligned, written with the most decimals any of its values has and grouped by thousands - unless its name says it identifies (id, number, code, year ...), then the digits stand as they are. format= gives a column a pipe instead, run as {echo $value | pipe}. totals= sums columns in a last row, over all rows. Everything from the data is escaped.

Sorting and paging

With sortable each header is a link to the same page with ?sort=column&dir=asc or desc. Only a column the table shows is accepted - anything else leaves the rows as they came - and the sort is stable; the sorted column carries aria-sort. rows= shows that many rows a page with page links below, built from the {pager}'s items. The links keep the request's other values. query= prefixes the three request names, so two tables on one page sort and page apart.

A table without rows answers nothing, and its @else@ part shows. Under the strict check these are errors: no data at all, a column no row has, a totals= or format= naming a column the table does not show, a total of a column that is not numbers, a number of labels that differs from the number of columns, and rows= below 1.

Examples

Columns, a format and totals

Columns picked, a date pipe on one, the numbers aligned and summed in a totals row.

Templateexamples/datatable/1-orders.pad
{datatable data='orders', columns='number, date, customer, items, total', totals='items, total', format="date:date('j M Y')", striped, caption='Orders this week'}
PHPexamples/datatable/1-orders.php
<?php

  $orders = [
    [ 'number' => 10412, 'date' => '2026-10-05', 'customer' => 'Atelier Graphique',  'items' => 3,  'total' => 1245.5  ],
    [ 'number' => 10413, 'date' => '2026-10-05', 'customer' => 'Signal Gift Stores', 'items' => 1,  'total' => 89.9    ],
    [ 'number' => 10414, 'date' => '2026-10-06', 'customer' => 'Mini Wheels Co.',    'items' => 12, 'total' => 4310    ],
    [ 'number' => 10415, 'date' => '2026-10-07', 'customer' => 'Land of Toys Inc.',  'items' => 5,  'total' => 642.25  ] ];

?>
Resultopen alone
Orders this week
Number Date Customer Items Total
10412 5 Oct 2026 Atelier Graphique 3 1,245.50
10413 5 Oct 2026 Signal Gift Stores 1 89.90
10414 6 Oct 2026 Mini Wheels Co. 12 4,310.00
10415 7 Oct 2026 Land of Toys Inc. 5 642.25
Total 21 6,287.65

Rows as content

As a pair the content is the data - here CSV - with a currency pipe and a totals row.

Templateexamples/datatable/2-csv.pad
{datatable totals='budget, spent', format="budget:currency('EUR'), spent:currency('EUR')", caption='Marketing budget'}
  channel,budget,spent
  Search ads,12000,11380.40
  Social media,8500,9120.15
  Newsletter,1500,960
{/datatable}
Resultopen alone
Marketing budget
Channel Budget Spent
Search ads €12,000.00 €11,380.40
Social media €8,500.00 €9,120.15
Newsletter €1,500.00 €960.00
Total €22,000.00 €21,460.55

Sortable and paged

Four rows a page with page links, headers that sort - plain links, under their own prefix.

Templateexamples/datatable/3-paged.pad
{datatable data='cities', labels='City, Country, Population', rows=4, sortable, query='city_'}
PHPexamples/datatable/3-paged.php
<?php

  $cities = [
    [ 'city' => 'Shanghai', 'country' => 'China',     'population' => 24870895 ],
    [ 'city' => 'Beijing',  'country' => 'China',     'population' => 21893095 ],
    [ 'city' => 'Kinshasa', 'country' => 'DR Congo',  'population' => 16315534 ],
    [ 'city' => 'Istanbul', 'country' => 'Türkiye',   'population' => 15655924 ],
    [ 'city' => 'Tokyo',    'country' => 'Japan',     'population' => 14094034 ],
    [ 'city' => 'Moscow',   'country' => 'Russia',    'population' => 13010112 ],
    [ 'city' => 'Mumbai',   'country' => 'India',     'population' => 12442373 ],
    [ 'city' => 'Jakarta',  'country' => 'Indonesia', 'population' => 10562088 ] ];

?>
Resultopen alone
City Country Population
Tokyo Japan 14,094,034
Moscow Russia 13,010,112
Mumbai India 12,442,373
Jakarta Indonesia 10,562,088

See also

  • {pager} Page links for a tag written with the page option: first and last, a window round the current page, previous and next.
  • {chart} Draws a chart as inline SVG on the server - 46 kinds, from bars and lines to sankeys, maps of a hierarchy and org charts.
  • {data} Turns its content - JSON, XML, YAML, CSV, a list or a range - into a named data set for a later tag to iterate.