Frontmatter

Data contract

Solo uses a fixed namespace for all template variables. Understanding this contract is essential for wiring backend data to the generated templates.

The fm namespace

Every variable in every generated template lives under a fixed namespace with four keys:

KeyRead byContains
fm.sitePages, layoutsSite-wide data — name, URL, global config
fm.pagePages, layoutsPage-level data — title, description, canonical
fm.dataPages, layoutsTemplate-specific data — arrays, collections, content
fm.propsPartials onlyComponent props — passed when including a partial

In Twig

{# Layout — reads fm.site and fm.page #}
<!doctype html>
<html lang="en">
  <head>
    <title>{{ fm.page.title }}</title>
    <meta name="description" content="{{ fm.page.description }}" />
  </head>
  <body>
    {% block content %}{% endblock %}
  </body>
</html>
{# Page — reads fm.data #}
{% extends 'layouts/base.html.twig' %}

{% block content %}
  {% include 'partials/hero.html.twig' with {
    fm: { props: fm.data.hero }
  } %}

  {% for item in fm.data.products %}
    {% include 'partials/card.html.twig' with {
      fm: { props: item }
    } %}
  {% endfor %}
{% endblock %}
{# Partial — reads fm.props only #}
<section class="hero">
  <h1>{{ fm.props.headline }}</h1>
  {% if fm.props.sub %}
    <p>{{ fm.props.sub }}</p>
  {% endif %}
</section>

In PHP

The same contract, accessed as a PHP array:

<?php // Layout ?>
<!doctype html>
<html lang="en">
  <head>
    <title><?= htmlspecialchars($fm['page']['title']) ?></title>
  </head>
  <body>
    <?php // content included by page ?>
  </body>
</html>
<?php // Page — you populate $fm, then include partials
$fm['props'] = $fm['data']['hero'];
include __DIR__ . '/../partials/hero.php';

foreach ($fm['data']['products'] as $item) {
  $fm['props'] = $item;
  include __DIR__ . '/../partials/card.php';
}
<?php // Partial — reads $fm['props'] only ?>
<section class="hero">
  <h1><?= htmlspecialchars($fm['props']['headline']) ?></h1>
  <?php if (!empty($fm['props']['sub'])): ?>
    <p><?= htmlspecialchars($fm['props']['sub']) ?></p>
  <?php endif; ?>
</section>

Your responsibility

Solo generates the template structure. Your backend is responsible for populating the fm namespace from your data source — a CMS, a database, an API, or hardcoded values.

INTEGRATION.md documents exactly what each template expects under each key. The backend developer reads it once and knows what to wire.

Partials never fetch their own data. A partial only renders what it receives via fm.props. Data fetching always happens in the page or layout, never inside a partial.

Prop types in the contract

Since Solo only accepts literal props, the value types in fm.props are always one of:

  • string
  • number (float or integer)
  • boolean
  • null — for explicitly absent optional values

On this page