Content Types

Every field in Lean CMS has a content_type that determines how it is stored, edited, and rendered. Nine types are available.


text

A plain single-line string. The most common type for headings, labels, and short copy.

YAML definition:

hero:
  heading:
    type: text
    default: "Welcome to Our Site"

Rendering:

<h1><%= page_content(@page, 'hero', 'heading') %></h1>

Editor experience: Single-line text input. Supports optional max_length.


rich_text

Multi-paragraph HTML content, edited with a Trix rich text editor (Action Text).

YAML definition:

about:
  body:
    type: rich_text
    default: "<p>We've been building things since 2005.</p>"

Rendering:

<div class="prose">
  <%= page_content_html(@page, 'about', 'body') %>
</div>

Use page_content_html (not page_content) so the Action Text HTML is rendered safely without double-escaping.


image

A file attachment managed through ActiveStorage. Can store an uploaded file or fall back to a URL string.

YAML definition:

hero:
  background:
    type: image

Rendering:

<% bg = page_content_image_url(@page, 'hero', 'background') %>
<% if bg %>
  <%= image_tag bg, class: "w-full h-64 object-cover" %>
<% end %>

With a variant:

<% thumb = page_content_image_url(@page, 'team', 'photo', variant: { resize_to_fill: [400, 400] }) %>

Editor experience: File upload with drag-and-drop. Accepts JPG, PNG, GIF, WebP.


boolean

A true/false toggle. Stored as the string "true" or "false".

YAML definition:

about:
  show_team_section:
    type: boolean
    default: "true"

Rendering:

<% if page_content?(@page, 'about', 'show_team_section') %>
  <%= render 'team_section' %>
<% end %>

Use page_content? which returns a Ruby boolean. page_content returns the raw string.


url

A URL string. Displayed as a plain text field with URL validation.

YAML definition:

hero:
  cta_url:
    type: url
    default: "/contact"

Rendering:

<a href="<%= page_content(@page, 'hero', 'cta_url') %>">
  <%= page_content(@page, 'hero', 'cta_label') %>
</a>

color

A CSS color value (hex, rgb, named). Displayed as a color picker in the editor.

YAML definition:

branding:
  primary_color:
    type: color
    default: "#2563eb"

Rendering:

<div style="background-color: <%= page_content(@page, 'branding', 'primary_color') %>">

Or with Tailwind’s JIT arbitrary values:

<div class="bg-[<%= page_content(@page, 'branding', 'primary_color') %>]">

A string value restricted to a predefined list of options. Options are defined in the YAML or in the content record’s options attribute.

YAML definition:

hero:
  layout_style:
    type: dropdown
    options:
      - "centered"
      - "left-aligned"
      - "split"
    default: "centered"

Rendering:

<section class="hero hero--<%= page_content(@page, 'hero', 'layout_style') %>">

Editor experience: A <select> element showing the defined options.


cards

A JSON array of card objects. Each card can have a title, description, icon, background color, image, and link. Rendered via the cards_section component.

YAML definition:

services_preview:
  cards:
    type: cards
    default: []

Rendering with the component (recommended):

<%= cards_section('services_preview', grid_cols: 3) %>

Rendering manually:

<% page_cards(@page, 'services_preview').each do |card| %>
  <div class="card">
    <h3><%= card['title'] %></h3>
    <p><%= card['description'] %></p>
  </div>
<% end %>

Component options:

Option Default Description
grid_cols 3 Number of columns in the grid
gap 8 Tailwind gap value
card_class "bg-white rounded-xl p-8 shadow-sm..." CSS classes for each card
icon_size 12 Icon size (Tailwind w/h value)
scroll_animate false Animate cards in on scroll
stagger_delay 150 Milliseconds between card animations

Editor experience: A drag-to-reorder card manager with fields for title, description, icon, color, image, and link.


bullets

A JSON array of bullet point objects. Each bullet has a title and optional description. Rendered via the bullets_section component.

YAML definition:

why_choose_us:
  bullets:
    type: bullets
    default: []

Rendering with the component (recommended):

<%= bullets_section('why_choose_us') %>

Rendering manually:

<% page_bullets(@page, 'why_choose_us').each do |bullet| %>
  <div class="flex gap-3">
    <span class="text-green-500"></span>
    <div>
      <strong><%= bullet['title'] %></strong>
      <p><%= bullet['description'] %></p>
    </div>
  </div>
<% end %>

Editor experience: Add/remove/reorder bullet points with a title and description per item.