Column types
Every column type the CMS understands and the editor it gets.
On this page
The CMS picks a field's editor from the column's type, from metadata registered in _collection_fields, and in a few cases from the values already in the column. This page lists what you get for each.
Every new site runs on the newer database, which has only three column types, so the metadata the AI registers decides most of its editors. On the newer database covers that first, and On the older database lists the Postgres types of a site that still runs on the older database.
How the editor is chosen
The checks run in order, and the first match wins:
- A multi reference field registered in
_collection_fields. - A real foreign key column.
- An enum type, which only the older database has.
- An
hstorecolumn, which only the older database has. - An explicit
ui_typeofcolor,richtext,file,code,json,boolean,date,datetime,imageorimagearray. Aui_typeofarraymarks a list, which then gets the list editor in the next step. - The column's type group.
- For plain text columns with no metadata, the shape of the values already stored.
The editor is picked from what the row held when you opened it, so a field never swaps to a different editor while you type in it.
On the newer database
A site on the newer database has only three column types: text, integer and real. text gets the string editors, and integer and real get the number input. There is no real uuid, boolean, jsonb, timestamptz, array or enum type, so the AI registers what such a column holds in _collection_fields when it creates it:
ui_type |
Column type | Editor |
|---|---|---|
boolean |
integer holding 1 or 0 |
Dropdown of true and false |
date |
text holding a calendar date |
Date picker |
datetime |
text holding an ISO 8601 UTC timestamp |
Date and time picker |
json |
text holding a JSON object |
JSON editor that validates as you type |
array |
text holding a JSON array |
Chip input, one value at a time |
imagearray |
text holding a JSON array |
Gallery upload, an ordered list of images each with its own alt text |
The five in Metadata driven fields, color, richtext, code, file and image, work here too. A gallery is imagearray rather than image, because there is no text[] type to mark it, and its <column>_alt companion is registered as array. Without this metadata a boolean column shows a number input, and a date, JSON or list column a plain text box.
On the older database
A site on the older database runs Postgres, whose many column types choose most editors on their own, by group:
| Group | Postgres types | Editor | Table cell |
|---|---|---|---|
| String | text, varchar, character varying, char, character, bpchar, name, citext |
Single line input, or a six row textarea for long text | Text, truncated |
| UUID | uuid |
Text input, hinted 00000000-0000-0000-0000-000000000000 |
Text |
| Number | integer, bigint, smallint, numeric, real, double precision, decimal, serial, bigserial, smallserial, int, int2, int4, int8, float4, float8, oid |
Number input | Tabular figures |
| Money | money |
Text input with a $ prefix and the hint "Enter a dollar amount, e.g. 1000.00" |
Text |
| Boolean | boolean, bool |
Dropdown of true and false |
true or false |
| JSON | json, jsonb |
JSON editor that validates as you type | The JSON, stringified |
| Date | date, timestamp, timestamptz, timestamp with time zone, timestamp without time zone |
Date and time picker | Formatted date, tabular figures |
| Time | time, timetz, time with time zone, time without time zone |
Hour, minute and second selects | Formatted time |
| Interval | interval |
Text input, hinted "e.g. 2 hours 30 minutes" | Text |
| Array | array (any <type>[]) |
Chip input, one value at a time, each validated against the base type | Up to six pills, then +n |
| Binary | bytea |
Two mode editor, text which encodes as you type, or hex for a raw \x literal |
Text |
| Enum | any Postgres enum (user-defined) |
Dropdown of the enum's values, with None when the column is nullable |
Text |
| Network | cidr, inet, macaddr, macaddr8 |
Text input with a format hint and validation | Text |
| Geometric | point, line, lseg, box, path, polygon, circle |
Shape aware numeric fields, x and y per point | Rendered back to its literal |
| Bit | bit, bit varying, varbit |
Text input accepting only 0 and 1 |
Text |
| Range | int4range, int8range, numrange, tsrange, tstzrange, daterange |
A from and a to field, numeric, date or date and time depending on the range | Text |
| Key value | hstore |
Rows of key and value inputs with an Add pair button | Text |
| Text search | tsvector, tsquery |
Text input with a syntax hint | Text |
| XML | xml |
Text input validated as well formed XML | Text |
Anything the CMS does not recognise falls back to a plain text input.
Metadata driven fields
These five are declared by the AI in _collection_fields at the same time the column is created, on either database. The column type stays text, or text[] for a gallery on the older database, and only the metadata decides the widget.
ui_type |
Column type | Editor |
|---|---|---|
color |
text |
Color swatch with a native color picker, validated as a hex color |
richtext |
text |
WYSIWYG editor: bold, italic, underline, strikethrough, paragraph and headings 1 to 6, quote, bullet and numbered lists, links, inline images, code blocks and tables. Horizontal rules, subscript and superscript already in the text are kept. See Rich text fields |
code |
text |
Code editor with HTML, CSS and JavaScript highlighting and a Preview tab. Stores the code exactly as written, and your site runs it. See Code fields |
file |
text |
File upload with drag and drop, showing the file name with download and replace actions. Stores the uploaded file's URL |
image |
text |
Image upload with a preview, replace, delete and an alt text popover |
image |
text[], on the older database |
Gallery upload, an ordered list of images each with its own alt text |
On the newer database a gallery is imagearray instead, as On the newer database explains.
The rich text editor keeps formatted text only. When a richtext value holds an embed or code, such as an iframe or a script, a warning above the editor says so: clicking into the field leaves it alone, but editing the text removes those parts. Ask in chat to move the embed into a code field.
An image column always comes with a companion column named <column>_alt of the same type. The CMS hides that companion and edits it from inside the image widget.
Without the metadata, a text column holding a file URL is indistinguishable from any other string, so it renders as a plain text box. That is why the AI registers it up front. An empty code field without its metadata gets a plain text box too.
Detected without metadata
Three things are inferred from the values already in a column, so they can appear on a column nobody registered:
- Code. A value that starts with a tag and contains a
<script>,<style>,<iframe>,<noscript>,<link>,<meta>,<template>or<source>tag, an embedded object, video, audio or canvas, a form control, a whole HTML document, an inline event handler such asonclick, or an<svg>inside other markup opens in the code editor, as long as it passes the code checks under Validation. The rich text editor would drop those parts. Text that only mentions a tag, such asWrap your CSS in a <style> block., keeps its usual editor, and an SVG file stays an image. - Rich text. A value containing other HTML markup opens in the rich text editor rather than a text box.
- Images. A text column whose values look like image URLs is treated as an image field, as is an array column whose elements do. This needs at least one non empty value to go on, which is why a freshly created image column still needs its
imagemetadata.
A hex color value in a plain text column also switches that field to the color picker.
Long versus short text is inferred too. A value with a line break always gets a textarea, so no line is lost. Otherwise, column names like description, content, body, bio, summary and notes get a textarea, and names like name, title, slug, email, url, status and code get a single line input. Failing both, a value longer than 150 characters gets the textarea.
Relationship fields
| Kind | How it is stored | Editor |
|---|---|---|
| Single reference | An ordinary foreign key column, for example author_id text REFERENCES authors(id), or author_id uuid on the older database |
Searchable single select of rows in the target collection, loading twenty at a time |
| Multi reference | A join table plus a multi_reference row in _collection_fields |
Multi select with checkboxes and removable chips, searchable, paged twenty at a time |
Single references are read straight from the database's own foreign key catalog, so they need no metadata. The field label drops a trailing _id, so author_id reads "Author".
Multi reference join tables are hidden from the Collections sidebar. Turn on Show system collections in the CMS Settings sub-tab to see them.
System columns
| Column | Behaviour |
|---|---|
The primary key, usually id |
Never editable. Reads "Auto-generated" on a new row |
created_at, created_on, inserted_at, inserted_on |
Hidden while creating, read only when editing |
updated_at, updated_on, modified_at, modified_on |
Read only. Re-stamped by a database trigger on every edit |
slug |
Slug input with a live preview of the row's public URL, click to copy. Auto filled from title or name while creating, until you edit it |
Format hints
Columns with a fiddly literal syntax show an example as their placeholder. These are all older database types, since the newer database has only text, integer and real.
| Type | Hint |
|---|---|
uuid |
00000000-0000-0000-0000-000000000000 |
money |
$1,000.00 |
inet |
192.168.0.1 |
cidr |
192.168.0.0/24 |
macaddr |
08:00:2b:01:02:03 |
point |
(x,y) |
circle |
<(x,y),r> |
box |
(x1,y1),(x2,y2) |
polygon |
((x1,y1),(x2,y2),...) |
bit |
10101010 |
int4range |
[1,10) |
daterange |
[2026-01-01, 2026-12-31) |
tsquery |
quick & brown (& = and, | = or, ! = not) |
hstore |
"key" => "value", "k2" => "v2" |
bytea |
\x48656c6c6f |
interval |
1 day 02:30:00 |
xml |
<root>…</root> |
Validation
The editor blocks a save and shows the reason under the field. Apart from Required, it only checks the fields you changed, so an older value that would not pass today does not stop you saving the rest of the item.
| Case | Message |
|---|---|
A NOT NULL column left empty |
Required |
| Malformed JSON | Invalid JSON |
| A non numeric value in a number or money column | Must be a number |
| A decimal in an integer column | Must be a whole number (no decimals) |
| An integer outside its type's range | Out of range for integer (-2147483648 to 2147483647) |
A malformed uuid |
Must be a UUID (00000000-0000-0000-0000-000000000000) |
A malformed inet |
Must be an IP address, e.g. 192.168.1.5 or 192.168.1.5/24 |
A cidr with no mask |
Must include a network mask, e.g. 192.168.1.0/24 |
| A malformed MAC address | Must be a MAC address (08:00:2b:01:02) |
| A bit string with anything but 0 and 1 | Only 0 and 1 are allowed |
| Malformed XML | Must be well-formed XML |
A color field that is not a 3, 4, 6 or 8 digit hex color |
Must be a hex color, e.g. #1a2b3c |
A code field with a <script>, <style>, <iframe>, <textarea>, <title>, <noscript>, <noembed>, <noframes> or <xmp> tag that is never closed, outside an <svg> |
Close the <script> tag with </script>, naming the tag |
A code field with an HTML comment that is never closed |
Close the HTML comment with --> |
A code field with a CDATA section inside an <svg> that is never closed |
Close the CDATA section with ]]> |
A code field with a tag cut off before its > |
Finish the <div> tag with >, naming the tag |
A code field with a <! or <? tag, such as a doctype, cut off before its > |
Finish the <! tag with >, or the same with <? |
A code field containing <plaintext> |
Remove the <plaintext> tag, it hides everything after it |
A code field containing <base> |
Remove the <base> tag, it changes where every link on your page points |
A code field longer than 100,000 characters |
Must be 100,000 characters or fewer |
Integer bounds are enforced per type: smallint -32768 to 32767, integer -2147483648 to 2147483647, bigint -9223372036854775808 to 9223372036854775807, and the serial variants from 1 up to the same ceiling.
Next
- The CMS shows these fields in context.
- Site databases explains how columns get created.