Intermediate May 3, 2026 · 2 min read

Every field type Loom understands, with its Go type, MySQL column type, HTML input element, and notes on when to use it.

The full table

type: Go MySQL HTML Notes
text string VARCHAR(255) <input type="text"> Short strings; the default
textarea string TEXT <textarea> Multi-line, no formatting
email string VARCHAR(255) <input type="email"> Adds validate:"email"
phone string VARCHAR(50) <input type="tel"> No format validation; user-facing format flexible
url string VARCHAR(500) <input type="url"> Adds validate:"url"
integer int64 BIGINT <input type="number" step="1">
decimal float64 DECIMAL(15,4) <input type="number" step="0.01"> Money-safe precision
boolean bool TINYINT(1) <input type="checkbox">
date time.Time DATE <input type="date"> Calendar date, no time
datetime time.Time DATETIME <input type="datetime-local">
select string VARCHAR(100) <select> Requires options
radio string VARCHAR(100) radio group Requires options
multiselect []string JSON multi-select Requires options; stored as JSON array
link *uuid.UUID CHAR(36) autocomplete Requires target; FK is documented in SQL but not enforced
attach string VARCHAR(500) <input type="file"> Stores file path / URL
image string VARCHAR(500) <input type="file"> Same as attach, with thumbnail rendering in UI
json map[string]any JSON JSON editor Use sparingly — schema-less
uuid uuid.UUID CHAR(36) <input type="text"> Auto-generated; rare (every record already has an id)
markdown string LONGTEXT markdown editor Long-form content with formatting
code string LONGTEXT code editor Snippets with syntax highlighting

Required vs optional → Go type adjustment

For non-required fields, the generated Go uses pointer types so nil cleanly represents "no value":

- name: status
  type: text          # not required
Status *string `db:"status" json:"status"`

For required fields, the type is the bare value:

CompanyName string `db:"company_name" json:"company_name" validate:"required,max=255"`

The exception: types that are already pointer-like (link, multiselect, json) use the same type whether required or not — Loom never emits double-pointers.

Audit columns Loom adds for free

Every Thread's table has these columns automatically. You don't declare them; Loom owns them:

Column MySQL Purpose
id CHAR(36) NOT NULL DEFAULT (UUID()) Primary key
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP Insertion time
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP Last-update time
created_by CHAR(36) NOT NULL UUID of the user who created the row (from JWT)

The runtime sets id and created_by on Create. MySQL maintains the timestamp columns. You can read them via the API but you cannot write them.

See also

  • Concepts → The Five Field Types Worth Knowing — practical guidance on which to use when
  • Reference → Thread YAML Schema — every field option, not just types
Was this article helpful?