Field Type Matrix vs. the Market
The field catalog is one of the platform's cores: the kind determines the Postgres column, validation, the panel control, the type in OpenAPI/GraphQL, the render variant, and the FLS rule. This document compares our catalog (src/platform/schema/field-types.ts, 56 types + variants in field-variants.ts) with competitor catalogs and points out what is really missing.
Legend: yes = full native support, part. = partial / via another type or variant, plugin = only via an extension, no = missing.
1. Text fields and identifiers
| Type | Us | Strapi 5 | Contentful | Sanity | Payload 3 | Directus 11 | Hygraph | Contentstack |
|---|---|---|---|---|---|---|---|---|
| text (single line) | yes | yes | yes | yes | yes | yes | yes | yes |
| longText | yes | yes | yes | yes | yes | yes | yes | yes |
| richText (structured) | yes | yes | yes | yes | yes | yes | yes | yes |
| markdown | yes | part. | part. | plugin | part. | yes | yes | part. |
| html (raw WYSIWYG) | no | part. | no | no | part. | yes | no | yes |
| code | yes | plugin | no | plugin | plugin | yes | no | plugin |
| slug / uid | yes | yes | yes | yes | yes | part. | yes | yes |
| uuid | yes | part. | no | no | part. | yes | yes | no |
| autoIncrement | yes | part. | no | no | no | yes | no | no |
| email / url / phone / ipAddress | yes | part. | part. | part. | yes | yes | part. | part. |
| password (hashed, never in API) | yes | yes | no | no | yes | yes | no | no |
| secret (reversible encryption) | no | no | no | no | no | no | no | no |
2. Numbers, time, units
| Type | Us | Strapi 5 | Contentful | Sanity | Payload 3 | Directus 11 | Hygraph | Contentstack |
|---|---|---|---|---|---|---|---|---|
| integer / decimal | yes | yes | yes | yes | yes | yes | yes | yes |
| percent / slider / progress / rating | yes | part. | no | part. | part. | part. | no | no |
| money (amount + currency) | yes | no | no | plugin | no | part. | no | no |
| measurement (value + unit) | yes | no | no | no | no | no | no | no |
| currency / country / language / timezone | yes | no | no | plugin | part. | part. | no | no |
| date / dateTime / time / duration | yes | yes | yes | yes | yes | yes | yes | yes |
| dateRange | yes | no | no | no | no | no | no | no |
| numberRange | no | no | no | no | no | no | no | no |
| dateTime with timezone stored in the record | part. | no | part. | no | no | yes | no | no |
3. Choices, taxonomies, relations
| Type | Us | Strapi 5 | Contentful | Sanity | Payload 3 | Directus 11 | Hygraph | Contentstack |
|---|---|---|---|---|---|---|---|---|
| enumeration (single select) | yes | yes | yes | yes | yes | yes | yes | yes |
| multiSelect (checkbox group) | yes | part. | yes | yes | yes | yes | yes | yes |
| tags (free + dictionary) | yes | part. | part. | yes | part. | yes | part. | yes |
| relation 1-1 / 1-n / n-m | yes | yes | yes | yes | yes | yes | yes | yes |
| polymorphic relation (many-to-any) | no | no | part. | yes | yes | yes | part. | no |
| tree self-relation (parent/tree) | part. | part. | part. | part. | part. | yes | no | no |
| userRef | yes | yes | no | no | yes | yes | no | no |
| lookup (mirrored field from a relation) | no | no | no | no | no | part. | no | no |
| rollup / aggregate over a relation | no | no | no | no | no | part. | no | no |
| formula / computed | no | no | no | no | part. | no | no | no |
| sortOrder (manual drag ordering) | no | part. | no | no | no | yes | no | no |
| remoteSelect (options from an external API) | no | plugin | plugin | yes | yes | part. | plugin | plugin |
4. Media and files
| Type | Us | Strapi 5 | Contentful | Sanity | Payload 3 | Directus 11 | Hygraph | Contentstack |
|---|---|---|---|---|---|---|---|---|
| file / image / video / audio | yes | yes | yes | yes | yes | yes | yes | yes |
| assetGallery (multiple, sortable) | yes | yes | yes | yes | yes | yes | yes | yes |
| focal point / crop presets | no | part. | yes | yes | yes | part. | yes | part. |
| externalAsset (Cloudinary/Drive/DAM ref) | part. | plugin | plugin | plugin | plugin | part. | part. | plugin |
| embed (oEmbed/iframe) | yes | no | no | plugin | no | no | yes | no |
| signature | yes | no | no | no | no | no | no | no |
| barcode / QR | yes | no | no | no | no | no | no | no |
5. Complex structures and UI
| Type | Us | Strapi 5 | Contentful | Sanity | Payload 3 | Directus 11 | Hygraph | Contentstack |
|---|---|---|---|---|---|---|---|---|
| component (reusable) | yes | yes | part. | yes | yes | part. | yes | yes |
| dynamicZone / blocks | yes | yes | part. | yes | yes | part. | yes | yes |
| list (repeater) / object (group) | yes | yes | part. | yes | yes | part. | yes | yes |
| json (arbitrary structure) | yes | yes | yes | no | yes | yes | yes | yes |
| json validated against JSON Schema | part. | no | no | no | no | no | no | no |
| table / matrix (row grid) | no | no | no | plugin | no | no | no | yes |
| geolocation (point) | yes | no | yes | yes | yes | yes | no | no |
| geometry (line / polygon) | no | no | no | plugin | no | yes | no | no |
| address (structured) | yes | no | no | no | no | no | no | no |
| color / icon / emoji | yes | part. | no | plugin | no | part. | no | part. |
| presentational fields (divider, notice, button) | no | no | no | no | yes | yes | no | no |
| vector / embedding (pgvector, AI search) | no | plugin | no | part. | no | no | no | no |
6. What is actually missing
Ordered by impact on real-world deployments:
- Polymorphic relation (many-to-any) — a single field pointing at multiple models.
Blocks typical layouts ("promo block links to either an article or a product"). Requires a discriminator column plus a union in GraphQL (we already have the pattern from dynamicZone).
- Derived fields: lookup, rollup, formula — readable, non-writable fields computed
from a relation (e.g. orderTotal, articlesCount). Must pass through FLS as read-only and be computed in the Query Engine, not on the client.
- sortOrder — manual drag-and-drop record ordering; today
autoIncrement
assigns an order but doesn't let you change it.
- Presentational fields (divider, notice, uiButton) — only layout sections exist;
instructional elements with no persistence are missing (Payload UI field, Directus presentation).
- numberRange — a numeric range (price bracket, power range) as a single field
with min <= max validation; we only have dateRange.
- Focal point + crop presets on image — the market standard for responsive delivery;
today we only return a URL and metadata.
- remoteSelect — options fetched from an external API/platform endpoint instead of a
static enumeration (PIM/ERP integrations).
- table / matrix — a simple row-column grid for technical specs,
today worked around with list + object.
- geometry (linestring/polygon) — delivery areas, routes, zones;
geolocation
only covers a point.
- html (raw) — a field for external HTML (newsletters, migrated content) with sanitization
at delivery time, distinct from richText.
- vector / embedding — a pgvector column auto-populated from text fields,
for semantic search and recommendations ("related articles").
- secret (reversible encryption) —
passwordhashes one-way; a field is missing
for integration keys that must be read back on the server side.
- json validated against JSON Schema — today
jsonaccepts any object;
schema validation would give the same contract in REST and GraphQL.
- dateTime with timezone stored in the record — local events need a pair
(instant, IANA tz); today that's two separate fields.
- tree self-relation as a field type — we have hierarchy in taxonomy and DAM,
but not as a content model field with cycle validation.
Our advantages over the market (not to be lost while expanding): money, measurement, address, dateRange, signature, barcode, duration, progress, embed, autoIncrement, country/language/timezone/currency, and per-field-type render variants.