Obsidian uses Pebble as its templating engine — a Java template engine inspired by Twig.
<!-- views/articles/index.html -->
{% extends "layout.html" %}
{% block title %}{{ title }}{% endblock %}
{% block content %}
<h1>{{ title }}</h1>
{% for article in articles %}
<article>
<h2>{{ article.title }}</h2>
<p>{{ article.content }}</p>
</article>
{% endfor %}
{% endblock %}
return render("articles/index.html", Map.of(
"title", "My articles",
"articles", articles
));
Define a base layout and extend it in every view.
<!-- views/layout.html -->
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}My App{% endblock %}</title>
</head>
<body>
<nav>...</nav>
{% block content %}{% endblock %}
{{ flash() | raw }}
{{ flow_scripts | raw }}
</body>
</html>
These functions are available in all Pebble templates automatically — no import needed.
<form method="POST" action="/articles">
{{ csrf_field() | raw }}
<input type="text" name="title">
<button type="submit">Create</button>
</form>
<!-- Or just the token for AJAX -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<a href="{{ route(name='articles.index') }}">All articles</a>
<a href="{{ route(name='articles.show', params={'id': article.id}) }}">View</a>
<form action="{{ route(name='articles.store') }}" method="post">...</form>
{{ flash() }}
Renders the flash notification if one exists. Place it in your base layout. See Flash messages.
<input
type="text"
name="username"
value="{{ old('username') }}"
class="{{ error('username') ? 'border-red-500' : '' }}"
>
{% if error('username') %}
<p class="text-red-400 text-sm">{{ error('username') }}</p>
{% endif %}
{{ flow_scripts | raw }} <!-- Client-side navigation -->
Include these at the bottom of your base layout. The | raw filter is required — without it Pebble escapes the script tags.
<!-- Variables -->
{{ variable }}
{{ object.property }}
<!-- Conditions -->
{% if user != null %}
Hello, {{ user.username }}!
{% else %}
Hello, guest!
{% endif %}
<!-- Loops -->
{% for item in items %}
<li>{{ item.name }}</li>
{% else %}
<li>No items found.</li>
{% endfor %}
<!-- Filters -->
{{ title | upper }}
{{ content | truncate(100) }}
{{ date | date("dd/MM/yyyy") }}
For the complete list of tags, filters, and functions, refer to the official Pebble documentation →