formats

Timestamps are stored as unix epoch and numbers as plain numbers — good for sorting and filtering, unreadable on a page. A view's formats map turns them into text at render time (inside tables.<t>.views):

"listing_cards": {
  "select": ["id", "title", "price", "created_at"],
  "formats": {
    "price": "num 1 000",
    "created_at": "date dd.mm.yyyy"
  },
  "limit": 20
}

The admin sections use the same mechanism — their menu items point at views too, so an admin list view with formats gives managers readable dates and prices. The edit form always shows raw values — a formatted number would not survive a round-trip. The built-in Users and Media lists format their timestamps as yyyy-mm-dd hh:nn out of the box.

Date masksdate plus tokens; everything else in the mask is printed literally. Time is UTC.

tokenmeaning
yyyyyear
mmmonth, 2 digits
ddday, 2 digits
hhhours 00–23
nnminutes
ssseconds

"date dd.mm.yyyy hh:nn"11.07.2026 14:32

Number masksnum plus a sample of how a thousand should look. The sample shows the group separator (space, comma, dot, apostrophe or none) and the decimal part; the engine dresses any value the same way. The group and decimal separators must differ — num 1.000.00 would be ambiguous to read and is rejected.

mask1234567.891 →
num 1 0001 234 568
num 1,0001,234,568
num 1000.01234567.9
num 1 000.001 234 567.89
num 1.000,001.234.567,89

A broken mask never reaches production: cms check and server start reject it with the exact path (formats column must also be listed in select).

What formats are not: the JSON API always returns raw values — code consuming it does its own formatting. And there is no conditional logic in masks on purpose: mapping a value to a picture is a template job. Five stars from a rating of 5 is just a file name:

<img src="/static/img/stars-{{rating}}.png" alt="{{rating}} of 5">

← All articles in this group