Create a Custom Template

Build a custom template from scratch using Kida syntax

4 min read 868 words

Learn how to create a custom template using Kida syntax. This guide walks through building a blog post template from scratch.

Goal

Create a custom blog post template that:

  • Extends the base layout
  • Displays post metadata (title, date, author)
  • Shows tags and categories
  • Includes a reading time estimate

Prerequisites

  • Bengal site initialized

  • Kida enabled inbengal.toml:

      site:
        template_engine: kida
    
  1. 1

    Create Template Directory

    Create the template file in your project:

    mkdir -p templates/blog
    touch templates/blog/single.html
    

    Bengal searches templates in this order:

  2. 2

    Extend Base Layout

    Start by extending the base layout:

    {# templates/blog/single.html #}
    {% extends "baseof.html" %}
    
  3. 3

    Define Template Variables

    Use{% let %}for template-wide variables:

    {% extends "baseof.html" %}
    
    {% let post = page %}
    {% let reading_time = post.content | reading_time %}
    {% let author = site.authors[post.author] ?? {} %}
    
  4. 4

    Override Content Block

    Override thecontentblock to display your post:

    {% extends "baseof.html" %}
    
    {% let post = page %}
    {% let reading_time = post.content | reading_time %}
    {% let author = site.authors[post.author] ?? {} %}
    
    {% block content %}
      <article class="blog-post">
        <header>
          <h1>{{ post.title }}</h1>
    
          <div class="post-meta">
            <time datetime="{{ post.date | dateformat('%Y-%m-%d') }}">
              {{ post.date | dateformat('%B %d, %Y') }}
            </time>
    
            {% if author.name %}
              <span class="author">By {{ author.name }}</span>
            {% end %}
    
            <span class="reading-time">{{ reading_time }} min read</span>
          </div>
        </header>
    
        <div class="post-content">
          {{ post.content | safe }}
        </div>
    
        {% if post.tags %}
          <footer class="post-footer">
            <div class="tags">
              {% for tag in post.tags %}
                <a href="{{ tag_url(tag) }}" class="tag">{{ tag }}</a>
              {% end %}
            </div>
          </footer>
        {% end %}
      </article>
    {% end %}
    
  5. 5

    Add Pattern Matching for Post Types

    Use pattern matching to handle different post types:

    {% block content %}
      {% match post.type %}
        {% case "blog" %}
          <article class="blog-post">
            {# Blog post template #}
            <h1>{{ post.title }}</h1>
            {{ post.content | safe }}
          </article>
        {% case "tutorial" %}
          <article class="tutorial">
            {# Tutorial template #}
            <h1>{{ post.title }}</h1>
            <div class="tutorial-content">{{ post.content | safe }}</div>
          </article>
        {% case _ %}
          <article>
            {# Default template #}
            <h1>{{ post.title }}</h1>
            {{ post.content | safe }}
          </article>
      {% end %}
    {% end %}
    
  6. 6

    Use Pipeline Operator for Data Processing

    Process collections with the pipeline operator:

    {% let recent_posts = site.pages
      |> where('type', 'blog')
      |> where('draft', false)
      |> sort_by('date', reverse=true)
      |> take(5) %}
    
    {% block content %}
      <article class="blog-post">
        {# Post content #}
      </article>
    
      {% if recent_posts %}
        <aside class="related-posts">
          <h2>Related Posts</h2>
          <ul>
            {% for related in recent_posts %}
              <li><a href="{{ related.href }}">{{ related.title }}</a></li>
            {% end %}
          </ul>
        </aside>
      {% end %}
    {% end %}
    
  7. 7

    Add Fragment Caching

    Cache expensive operations:

    {% block content %}
      <article class="blog-post">
        {# Post content #}
      </article>
    
      {% cache "related-posts-" ~ post.id %}
        {% let related = site.pages
          |> where('type', 'blog')
          |> where('tags', post.tags[0])
          |> where('id', '!=', post.id)
          |> take(3) %}
    
        {% if related %}
          <aside class="related-posts">
            <h2>Related Posts</h2>
            <ul>
              {% for item in related %}
                <li><a href="{{ item.href }}">{{ item.title }}</a></li>
              {% end %}
            </ul>
          </aside>
        {% end %}
      {% end %}
    {% end %}
    

Complete Example

Here's a complete blog post template:

{# templates/blog/single.html #}
{% extends "baseof.html" %}

{% let post = page %}
{% let reading_time = post.content | reading_time %}
{% let author = site.authors[post.author] ?? {} %}
{% let related_posts = site.pages
  |> where('type', 'blog')
  |> where('tags', post.tags[0] ?? '')
  |> where('id', '!=', post.id)
  |> sort_by('date', reverse=true)
  |> take(3) %}

{% block content %}
  <article class="blog-post">
    <header>
      <h1>{{ post.title }}</h1>

      <div class="post-meta">
        <time datetime="{{ post.date | dateformat('%Y-%m-%d') }}">
          {{ post.date | dateformat('%B %d, %Y') }}
        </time>

        {% if author.name %}
          <span class="author">By {{ author.name }}</span>
        {% end %}

        <span class="reading-time">{{ reading_time }} min read</span>
      </div>
    </header>

    <div class="post-content">
      {{ post.content | safe }}
    </div>

    {% if post.tags %}
      <footer class="post-footer">
        <div class="tags">
          {% for tag in post.tags %}
            <a href="{{ tag_url(tag) }}" class="tag">{{ tag }}</a>
          {% end %}
        </div>
      </footer>
    {% end %}
  </article>

  {% cache "related-posts-" ~ post.id %}
    {% if related_posts %}
      <aside class="related-posts">
        <h2>Related Posts</h2>
        <ul>
          {% for related in related_posts %}
            <li>
              <a href="{{ related.href }}">{{ related.title }}</a>
              <span>{{ related.date | days_ago }} days ago</span>
            </li>
          {% end %}
        </ul>
      </aside>
    {% end %}
  {% end %}
{% end %}

Testing

Test your template:

# Build the site
bengal build

# Or run dev server
bengal serve

Visit a blog post page to see your custom template in action.

Next Steps

Seealso