When a blog gets a request, first, we match its path to a Route (let's assume the route post for /hello-world). Then, we fetch required data from our database, then we call the Twig template file defined in that route (post.twig).

Inside this file, you can include other files or even use inheritance. You can even call our Data API to fetch more data (More on that below)!


We use Twig 3.0 for templating. It is a powerful language with a plenty of in-built tags, filters, and functions. Twig also has nice, easy-to-follow documentation, which was one reason we chose Twig over other template languages. If you haven't used it ever, go through the Twig for Template Designers page, and you will get an idea of how it works. Basically, it's HTML with superpowers.


This folder contains templates files. There are several types of template files.

Type Description Examples
Main These template files are rendered directly. index.twig post.twig
Partial These templates are not rendered directly but included in main template files. They start with an underscore (_) _footer.twig
Route These templates are used to define custom routes for a blog. The file name starts with route-. See custom routes below route-authors.twig
Component These templates are used to define new HTML structures for complex components like link previews. See Embed: Link. component-rich-link.twig

Route Variables

  • The theme developer (you) creates the theme
  • The blogger creates the content (data)
  • HB combines the theme and data and generates the blog.

When rendering the twig templates, we send data into your template file as objects. You will use this data to generate a beautiful UI.

There are 4 main objects in HB: Blog , Post , Tag , and Author. These objects are explained in the Data API page.

Variable name Available Routes Description
_blog (all) A Blog object, that includes all blog-level data/settings.
_lang all A Language Object for the current language. Should also be placed in <html lang="{{ _lang.code }}">
_config (all) Theme config (config.yaml) as an object
_route (all) Current route name.
_posts (all) An array of Post objects, filtered by the route's filter value
_featured_posts index An array of Posts objects (all featured posts).
_post post and page A Post object
_tag tag A Tag object (the current tag)
_author author An Author object (the current author)

Each Route gets different variables. We prefix each variable with _ so that it won't conflict with the variables you define inside the theme files (Obviously, you shouldn't prefix _ your variables inside the Twig template)


You are required to put some placeholders in your theme to make a few things work.

Placeholder Scopes Description
_head all place before </head>. We automatically add SEO tags, styles.css link, and code_head set by the blogger.
_foot all place before </body>. We place the code_foot set by the blogger.
_comments post and page to embed the commenting system
_comment_count (optional) post and page to render the comment count of that page. For example, some themes have comment count at the top with a link to the comments section to encourage more comments. Only works when Hyvor Talk is connected.
_newsletter post and page to embed the newsletter subscription form

Sending all placeholders (except _lang) through the template filter is absolutely required to make them render as templates.

{{ _head | template }}

{{ _head | template }} is equal to {{ include(template_from_string(_head)) }} in Twig. We defined the custom template filter to make it easier for you to write it, as it is used frequently in HB templates.

Twig Helpers

We provide a few custom Twig functions and filters to make writing templates easier.


  • data - a function to call the Data API. See Fetching data below.

    {% set posts = data(endpoint="posts", filter="author.slug=user") %}
  • icon - a function to get an icon.

    {{ icon('bootstrap', 'arrow-down', 20, 20) }}

    Function definition: icon(iconLibrary, iconName, width, height)

    • All icon names are lowercase, and words are separated by - (arrow-down).
    • These icon libraries are supported
      • bootstrap
      • fontawesome (Free icons only)
        • append -regular to regular icons (calendar-regular)
        • append -solid to solid icons (calendar-solid)
        • Do not append anything for brand icons (github)
      • ionicons
      • heroicons
        • append -solid to solid icons (archive-solid)
        • append -outline to outline icons (archive-outline)
      • octicons
      • css.gg

        Under the hood, we use the php-svg-icons open-source library. If you need to add more icon libraries, please send a PR there.


  • asset_url - a filter to link assets

    • Turns an asset filename into its absolute URL.
    • Adds last updated timestamp as a query param (to bypass browser cache on updates)
    {{ 'script.js' | asset_url }}
    <script src="{{ 'script.js' | asset_url }}"></script>
    // changes to:
    <script src="https://subdomain.hyvorblogs.io/assets/script.js?v=12931923993"></script>
  • asset - a filter to directly print assets (only for text assets like SVGs)

    {{ 'beauty.svg' | asset }}
  • pagination_page_url - a filter to convert a page number to full URL

    <a href="{{ _pagination.page_prev | pagination_page_url }}">Previous Page</a>
  • lang - a filter for translations. Learn more in internationalization.

  • lang_by_number - See conditional strings based on a number

  • language_variant_url - See language switcher

The difference between functions and filters can be quite confusing in Twig. Our general rule is to use functions when multiple inputs are taken (data and icon) and use filters when only a single input matters (asset_url, asset, etc.).

Fetching Data

Use the data function to fetch data from our Data API.

<!-- Fetch data -->
{% set recent_posts = data(endpoint="posts", sort="published_at DESC", limit="5") %}

<!-- Render UI -->
<div id="recent-posts">
    {% for post in recent_posts.data %}
        {% include '_recent-post-card.twig' with post  %}  
    {% endfor %}

Use the endpoint named argument to set the API endpoint. You can set all other parameters by just sending them as named arguments to the data Twig function (Ex: sort="published_at DESC").

Custom Routes

There are two ways to add custom routes:

  • The blogger can add custom routes from the console (See docs).
  • Theme developers can define custom routes by adding files named route-{route}.twig to the templates folder.

The first option is more robust, and it providers easier way to automatically set input variables _posts (by filtering), _tag, _author, etc so you can access them without calling the Data API. But, as a theme developer, you will need to use the second option.

For example, let's say you decide that your theme want a page to list all authors of the blog. You can add a route-authors.twig to the templates folder. If the blog gets a request to /authors, this template will be rendered automatically.