Theme markers
A marker is one line in a theme file that Tovu fills with real content: the header, a menu, the page body, your latest posts. This page lists every marker type and key, with the HTML each one renders.
A marker is an ordinary HTML element with a data-embed-config attribute. Tovu finds it and fills it in, or swaps it out, with real content. One attribute, JSON inside, single quotes outside:
<nav class="main-nav" data-embed-config='{"type":"menu","id":"menu-header-nav"}'>…</nav>
Rules that apply to every marker:
typeis required. It says what goes here.- Most types also take an
id: which menu, which partial, which item. - Wrap the JSON in single quotes, so the JSON's own double quotes need no escaping.
- Fallback content: whatever you put inside the marker shows when there is nothing to fill it with, such as a menu that doesn't exist yet. Put sensible links there, not "loading…".
- Markers inside
<!-- comments -->,<script>, or<style>are ignored. Commenting a marker out really does turn it off. - A marker needs a closing tag.
<div data-embed-config='…' />is not a marker.
Every marker type
type | What it puts there | Keys | Fills or replaces? |
|---|---|---|---|
partial | A shared file from render/partials/ | id, current, variant | Replaces the marker |
menu | A menu you edit in Admin → Menus | id, variant | Fills the marker, keeps its tag |
content | The body of the page or post being viewed | id, slug, header | Fills the marker |
post-previews | A list of your latest posts | limit | Fills the marker |
collection | Entries from a collection | id, where, sort, limit, columns, layout, fields | Fills the marker |
widget | A widget | id or slug | See Embeds |
media | An image, video, or file | id or slug, variant (size) | See Embeds |
post | Another post, inline | id or slug | See Embeds |
widget, media, post, and collection also work inside page bodies. The Embeds and Collections pages cover them in full.
partial: shared header and footer
<div data-embed-config='{"type":"partial","id":"nav","current":"docs"}'></div>
id: which partial.theme.json'sslotsmaps each id to a file (see theme.json). Without aslotsblock,navandfootermap tonav.htmlandfooter.html.current(optional): marks one link in the partial as the current page. It finds<a href="…" data-nav-id="docs">in the partial and addsaria-current="page". Write the link withhreffirst anddata-nav-idsecond, and nothing after, or it won't match. Once a real menu fills the nav, the menu marks the current page itself, socurrentonly affects the partial's fallback links.variant(optional): use a different version of the partial."variant":"minimal"loads the fileslotsnames for it, orfooter-minimal.htmlby convention.
Before and after:
<!-- page source -->
<div data-embed-config='{"type":"partial","id":"footer","variant":"minimal"}'></div>
<!-- rendered: the marker is gone, the partial is in its place -->
<footer class="site-footer">…</footer> (contents of footer-minimal.html)
If you give the marker any other attribute (a class, say), the element stays and wraps the partial. An id that slots doesn't know leaves the marker untouched.
menu: a menu from the admin
<nav class="main-nav" data-embed-config='{"type":"menu","id":"menu-header-nav"}'>
<a href="/docs">Docs</a>
</nav>
id: the menu's slug or id, as shown in Admin → Menus.variant(optional):"tree"renders nested items. Leave it out for a flat row of links.
The marker's own tag and attributes stay (<nav class="main-nav"> survives). Only what's inside changes. Deleted or unpublished targets are left out, never shown as dead links.
Flat (default). Top-level items only, as bare links. Child items are not shown at all.
<nav class="main-nav" data-embed-config='{"type":"menu","id":"menu-footer-nav"}'><a href="/docs">Docs</a><a href="/about" aria-current="page">About</a></nav>
Tree. Add "variant":"tree" to get every level, as nested lists, with classes to style against:
<nav class="docs-nav" data-embed-config='{"type":"menu","id":"docs-section","variant":"tree"}'>
<ul class="menu-list depth-0">
<li class="menu-item depth-0 has-children is-active">
<a href="/docs#get-started">Get started</a>
<ul class="menu-list depth-1">
<li class="menu-item depth-1 is-current is-active"><a href="/quickstart" aria-current="page">Your first site</a></li>
</ul>
</li>
</ul>
</nav>
| Class | Means |
|---|---|
menu-list depth-N | A <ul> at nesting level N (0 = top) |
menu-item depth-N | An <li> at level N |
has-children | This item has a submenu |
is-current | This item is the page being viewed |
is-active | This item or something under it is the page being viewed. Use it to open the right section. |
| your own class | Whatever you typed in the menu item's CSS class field |
Why tree isn't the default: most nav CSS styles nav > a. A tree wraps links in <ul><li>, so switching an existing nav to tree breaks its layout until you add CSS for the lists. Opt in one marker at a time. A menu item with a description or icon gets <span class="menu-item-desc"> / <span class="menu-item-icon" data-icon="…"> in tree mode.
Built-in docs menus. Three menu ids don't name a stored menu. They are computed from your header menu's Docs item, the top-level item that links to /docs:
id | Renders |
|---|---|
docs-section | The Docs item's groups and pages, as a tree. For a docs sidebar. |
docs-prev-next | The previous and next docs page, tagged docs-pager-prev / docs-pager-next. |
docs-current-page-sidebar | Older convention: a menu with slug docs-<page-slug>-sidebar. Still works. |
Add a docs page to the Docs item in Admin → Menus, and the sidebar and pager pick it up.
content: the page or post being viewed
<article class="post-detail">
<div data-embed-config='{"type":"content"}'></div>
</article>
- No
id: the page or post at the current URL. This is what every template uses. idorslug: show a different page or post here instead.header(optional):falsehides the title and date header Tovu adds to posts written in the editor. Use it on landing pages. Only the JSON valuefalseworks, not the string"false".
A template (see theme.json) must contain at least one content marker.
post-previews: latest posts
<section class="blog-grid" data-embed-config='{"type":"post-previews","limit":6}'>
<p>No posts yet.</p>
</section>
limit(optional): how many. Default 6, max 24.
Renders one card per post, newest first:
<article class="post-card"><h3><a href="/hello">Hello</a></h3><div class="post-meta"><time datetime="2026-09-03T…">Sep 3, 2026</time></div></article>
With no published posts, your fallback content stays.
See also: Embeds for markers inside page bodies, and Create a theme.