Platform CLI
Learn
Developer docs User guide Quickstart CLI and AI agents Blog
Company
Services About Contact Links Get started

Introduction to templates

Updated

Every public page in Raytha is a Liquid template rendered on the server against one model. This page shows the path from URL to HTML, lists the kinds of template you can write, and points to the page that documents each part.

How a request becomes HTML

Raytha serves the public site from one catch-all route. For a request such as GET /blog/hello, it does this:

  1. It looks the path up in the route table. An empty path means the home page, which is a content item, a list view or a site page (you choose in the admin). Any other path must match one route, and each route points at exactly one of: a content item, a list view, a site page or a Raytha Function.
  2. It loads the target. A content item or site page that is not published returns the 404 error template. A list view that is not published does the same.
  3. It picks a template from the active theme. A content item uses the template bound to it, a list view uses the template bound to the view, a site page uses its own template. When nothing is bound, Raytha falls back to the built-in raytha_html_content_item_detail or raytha_html_content_item_list.
  4. It builds the template source. The template's parent layouts are joined into one string (see Layouts and inheritance).
  5. It renders that string with Fluid, the .NET Liquid engine, against a model that holds Target, CurrentOrganization, CurrentUser, PathBase, QueryParams and a few others (see Template variables).
  6. It writes the result as text/html.

Account pages (/account/login, /account/me and so on) and error pages follow the same last four steps, with a built-in template looked up by developer name in the active theme.

A first template

This is a complete detail template for a content type whose primary field is the title and that has a content field of type Wysiwyg. Put it in a template that has the base layout as its parent.

<article class="container py-5">
  <h1>{{ Target.PrimaryField | escape }}</h1>
  <p class="text-muted">
    {{ Target.CreationTime | organization_time: "%B %e, %Y" }}
    {% if Target.CreatorUser %}by {{ Target.CreatorUser.FullName | escape }}{% endif %}
  </p>
  {% if Target.PublishedContent.content.HasValue %}
    {{ Target.PublishedContent.content.Value }}
  {% endif %}
  <p><a href="{{ PathBase }}/{{ ContentType.DeveloperName }}">Back to {{ ContentType.LabelPlural | escape }}</a></p>
</article>

Three habits in that snippet apply everywhere:

  • Field values are objects. Target.PublishedContent.content has .Value, .Text and .HasValue. Test .HasValue, not the field itself, because the field object is always truthy.
  • Output is not HTML-escaped. Escape anything an editor or visitor typed with | escape. Only output a Wysiwyg field raw, because it is meant to contain HTML.
  • Prefix internal links with {{ PathBase }}. It is empty when Raytha is hosted at the root of its domain and /something when it is not.

Template kinds

KindUsed forTarget is
Base layoutThe shared page shell. Contains {% renderbody %} where the child goes.Whatever the child page's Target is
Content item detailOne content item at its route.A content item
Content item listA list view of one content type, with paging.A list result with Items
Site pageA page made of widgets in named sections.A site page (Title, RoutePath, ...)
Widget templateOne block on a site page, with its own settings form.Not available. Use widget.settings.
Login, registration, profileBuilt-in account pages (raytha_html_login_*, raytha_html_changeprofile ...).A form model with ValidationFailures and SuccessMessage
Error pagesraytha_html_error_403 and raytha_html_error_404.An error model (ErrorId, ErrorMessage, ...)
Email templatesSubject and body of system emails. Not part of a theme.A user-specific model

Detail and list templates are covered in Detail views vs list views. Site pages and widgets are covered in Site pages and sections and Widget templates. Emails are covered in Email templates.

Liquid and Fluid

Raytha renders with Fluid 2.31, a .NET implementation of Shopify's Liquid. The language basics ({{ output }}, {% if %}, {% for %}, {% assign %}, {% capture %}, {% case %}, filters with |) work as in any Liquid reference. Fluid differs from Shopify in a few ways that matter here:

  • Member names are case-sensitive and use the .NET names: Target.PrimaryField, item.RoutePath, CurrentUser.IsAuthenticated. A misspelled name renders as nothing. It does not raise an error.
  • Empty strings and the number 0 are truthy. Only false and a missing value are falsy. Compare with blank to catch both an empty string and nothing: {% if x != blank %}.
  • {% include %} and {% render %} parse, but Raytha configures no file provider, so they fail when the page renders. Reuse markup with layouts and widgets instead.
  • There is no {% layout %} tag. Inheritance is set in the template's settings, not in its source.
  • The {% liquid %} tag does not work reliably in Fluid 2.31. Avoid it.
  • Raytha adds functions (get_content_items, render_section and others) and filters (organization_time, attachment_public_url and others). They are listed in Functions and filters.

Templates are cached by their source text after the first parse, so a repeat request costs only the render.

What happens when a template is wrong

  • A syntax error is rejected when you save a web template. The same check runs for raytha theme push.
  • A runtime error happens when a visitor loads the page. The response is HTTP 500 with an empty body in production. In Development mode the body is a plain-text message that names the template. Raytha does not show your 500 error template for these.
  • An invalid Filter passed to get_content_items (or a bad ?filter= on a list view) returns HTTP 400 with the parser's message as plain text, in every environment.
  • A typo in a variable name is not an error. Check the HTML you get back, not only the status code.

Where to go next in these docs

You want toRead
See every variable and what it holdsTemplate variables
Share a header and footer between templatesLayouts and inheritance
Fetch other content from inside a templateFunctions and filters and Filtering and sorting
Build pages from blocksSite pages and sections, Widget templates
Work in files, in gitThemes and the command line tool
Copy a working snippetTemplate recipes

Next steps