Custom Fields

Custom fields let you attach additional structured data to any content type (articles, pages, projects). The schema is defined once in Settings; values are filled in per item in the editor.


Defining the schema

Go to Settings → Custom Fields. For each content type, you can add as many fields as needed.

Each field has four properties:

PropertyDescription
LabelThe human-readable name shown in the editor (e.g. "Price")
KeyThe machine identifier used in templates (e.g. price). Lowercase letters, numbers, hyphens and underscores only.
TypeOne of: Short text, Long text, Number, URL, Checkbox, Dropdown
RequiredWhether the field must be filled before publishing

For Dropdown fields, add a comma-separated list of options in the Options field (e.g. Easy, Medium, Hard).

The schema is stored in settings.json under custom_fields_schema:

{
  "custom_fields_schema": {
    "article": [
      { "key": "duration", "label": "Duration", "type": "text", "required": false },
      { "key": "price",    "label": "Price",    "type": "number", "required": true }
    ],
    "project": [],
    "page": []
  }
}

Filling in values

When custom fields are defined for a content type, a Custom Fields tab appears in the editor sidebar alongside Content and SEO. The tab is only visible when at least one field is defined for that type.

Values are saved in the item's JSON file under custom_fields:

{
  "title": "My article",
  "custom_fields": {
    "duration": "45 min",
    "price": "80"
  }
}

Using custom fields in themes

Custom field values are available in all content templates (content-articles.php, content-projects.php, content-pages.php) via the $item array.

render_item_custom_fields(array $item, string $type): string

Renders all non-empty custom fields as a <dl>, respecting schema order and field labels from Settings. Handles field types correctly: URL fields are rendered as links, checkbox fields as checkmarks, text/number fields with nl2br. Returns empty string if no schema is defined or no values are set.

// In content-articles.php — preferred for production templates
echo render_item_custom_fields($item, 'article');

Output example:

<div class="custom-fields-block">
  <dl class="custom-fields">
    <div class="cf-row"><dt>Duration</dt><dd>45 min</dd></div>
    <div class="cf-row"><dt>Price</dt><dd>80</dd></div>
  </dl>
</div>

get_custom_field(array $item, string $key, mixed $default = ''): mixed

Returns the value of a single custom field. Returns $default if the field is absent or empty.

$price = get_custom_field($item, 'price', 'N/A');
echo '<p class="price">' . htmlspecialchars($price) . '</p>';

$duration = get_custom_field($item, 'duration');
if ($duration) {
    echo '<span class="meta-duration">' . htmlspecialchars($duration) . '</span>';
}

// Checkbox field — value is "1" when checked, "" when unchecked
$isOnline = get_custom_field($item, 'is_online');
if ($isOnline) {
    echo '<span class="badge">Online</span>';
}

render_custom_fields(array $item): string

Renders all non-empty fields as a plain <dl> without schema ordering or label resolution — keys are used as-is. Useful for quick debugging; prefer render_item_custom_fields() in production templates.


Accessing raw values directly

$fields = $item['custom_fields'] ?? [];

foreach ($fields as $key =&gt; $value) {
    if ($value === '') continue;
    echo '<div class="cf-' . htmlspecialchars($key) . '">';
    echo htmlspecialchars($value);
    echo '</div>';
}

Notes for theme developers

Custom fields are opt-in. Nothing is displayed automatically — the theme decides if and how to render them.

Schema changes are non-destructive. Removing a field from the schema preserves its stored values in item files — they simply stop appearing in the editor. Re-adding the field with the same key restores access.

No index-level filtering. Custom field values live in individual item files, not in _index.json. Filtering or sorting by a custom field requires sl_load_all_items(), which is expensive. For simple display this is not a concern.

Not available in card partials. partials/article-card.php and partials/project-card.php receive index-level data, which does not include custom_fields. These functions are only meaningful in full single-item views.