Platform
Learn
Developer docs User guide Quickstart Blog
Company
Services About Contact Links Get started

Layouts and inheritance

Updated

A base layout holds the markup every page shares, such as the head, navigation and footer. A child template supplies only its own part. Raytha joins them as text before the Liquid engine runs, which explains both how it works and the few ways it surprises people.

The mechanism

A base layout is a web template marked as a base layout. It must contain the tag {% renderbody %} exactly where the child belongs. Any other template can name a layout as its parent in its settings. Inheritance is configured there, not in the template source: there is no {% layout %} tag.

Before rendering a page, Raytha takes the template's source and replaces the {% renderbody %} tag in the parent's source with it. If the parent has a parent too, the result replaces the tag in that one, and so on up the chain. The final string is parsed and rendered once, against one model.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>{% assign page_title = Target.PrimaryField | default: Target.Title | default: CurrentOrganization.OrganizationName %}{{ page_title | escape }}</title>
</head>
<body>
  <header>
    <a href="{{ PathBase }}/">{{ CurrentOrganization.OrganizationName | escape }}</a>
    <nav>
      {% assign menu = get_main_menu() %}
      {% for menuItem in menu.MenuItems %}
        <a href="{{ menuItem.Url }}">{{ menuItem.Label | escape }}</a>
      {% endfor %}
    </nav>
  </header>
  <main>
    {% renderbody %}
  </main>
  <footer>&copy; {{ CurrentOrganization.OrganizationName | escape }}</footer>
</body>
</html>

A child template then contains only the <main> part:

<article>
  <h1>{{ Target.PrimaryField | escape }}</h1>
  {{ Target.PublishedContent.content.Value }}
</article>

Raytha renders the child as if you had pasted it between <main> and </main>.

Setting it up

  1. Create a web template, tick Base layout that other templates can inherit from and put {% renderbody %} in the markup. Raytha refuses to save a base layout without the tag.
  2. Create or edit another template and pick the layout in Parent template.
  3. Open a page that uses the child and check the HTML.

With the command line tool, a pulled theme records the relationship in a sidecar file next to the template: web-templates/post_detail.json with "parent": "my_base_layout", and "isBaseLayout": true on the layout. See Themes and the command line tool.

Multi-level chains

A layout can have a parent. A new theme already has this:

TemplateParent
raytha_html_base_layoutnone
raytha_html_base_login_layoutraytha_html_base_layout
raytha_html_login_emailandpassword and the other account templatesraytha_html_base_login_layout
Home, page, list and detail templatesraytha_html_base_layout

So a login page is the login form, inside the narrow login column, inside the site layout. You can build your own chain the same way, for example a site layout, then a blog layout that adds a sidebar, then the individual blog templates.

Raytha rejects a parent chain that loops back on itself ("A circular dependency was detected"), requires the parent to be in the same theme, and will not let you clear the base layout flag of a template that still has children. The old limit of five levels does not apply in 2.0. Raytha loads chains up to about a hundred levels.

What the substitution means in practice

Variables flow downward, in source order

Because the child's text is pasted in where the tag is, a variable assigned in the layout above {% renderbody %} is visible to the child:

{%- comment -%} in the layout, before the tag {%- endcomment -%}
{% assign page_class = "wide" %}
<body class="{{ page_class }}">{% renderbody %}</body>

The reverse does not work. A variable the child assigns lands after the layout's head, so the head cannot read it:

{%- comment -%} in the child; the layout's <title> has already been rendered {%- endcomment -%}
{% assign page_title = "About us" %}

The <title> above renders empty. Instead of passing values up from the child, let the layout compute them from the model, as the layout at the top of this page does: Target.PrimaryField for items, Target.Title for site pages, Target.Label for list views.

The tag is replaced everywhere, blindly

  • A parent with two {% renderbody %} tags renders the child twice.
  • A parent with none drops the child without any error. (A base layout cannot be saved this way, but a template that is not flagged as a base layout can.)
  • A tag inside an HTML comment or {% comment %} block is replaced too.
  • The tag is recognised in the forms {% renderbody %}, {%renderbody%} and {% RenderBody %}. The whitespace-control form {%- renderbody -%} is not recognised, so Raytha rejects the template when you save it, with "Unknown tag 'renderbody'".

Dollar signs in a child template get rewritten

The substitution uses .NET's regular-expression replacement, so special sequences in the child's source are interpreted. Raytha's own assembly code gives these results:

Child source containsThe page gets
$$$
$& or $0the text {% renderbody %}, which then fails to parse
$'the part of the layout after the tag
$_the whole layout
$1, $5, ${x}, a lone $unchanged

The rewrite happens once per level, so two levels turn $$$$ into $. In HTML, write a dollar sign as &#36; when two can sit together, for example in a price range such as &#36;&#36;. In an inline script use \u0024. A single $ followed by a letter or digit is safe.

Syntax checks see one template at a time

When you save, Raytha parses the template you edited, not the assembled string. A child saved alone can look fine and still fail once joined, for example when it refers to a variable that the layout was supposed to assign. After every change, load a real page and read the HTML.

Gotchas

  • Putting {% renderbody %} in a template that is not flagged as a base layout saves without complaint, but the missing-tag check only runs for base layouts. Flag the template so Raytha enforces it.
  • Site pages and login pages choose their template differently, but both go through the same assembly. A broken layout breaks them all, including the account pages that visitors need to sign in.
  • Widgets are not part of the chain. A widget template is rendered separately, with its own variables.
  • Email templates have no layouts. The subject and body are each a single template.

Next steps