{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.