Skip to content

Partial Templates

Partial templates let you define reusable UI chunks once and render them anywhere. This is especially powerful with HTMX, Turbo, or any AJAX-style partial response pattern.

Table of Contents


Defining Partials

Use {% partialdef %} to define a named partial inside any template:

<!-- views/home.html -->

{% partialdef card %}

  <div class="card">

    <h3>{{ title|default:"Untitled" }}</h3>

    <p>{{ body|truncatewords:30 }}</p>

    {% if featured %}<em>Featured</em>{% endif %}

  </div>

{% endpartialdef %}



{% for entry in entries %}

  {% partial card with title=entry.title body=entry.body featured=entry.featured %}

{% endfor %}

Inline Partials

A {% partialdef %} block renders its body inline where it is defined and registers itself for later use:

{% partialdef notice %}

  <div class="alert">{{ message }}</div>

{% endpartialdef %}

This means you get immediate output and a reusable partial in one declaration.

Use the inline option explicitly:

{% partialdef greeting inline %}

  Hello {{ name }}!

{% endpartialdef %}

<!-- Above line ALSO outputs "Hello World!" when rendered -->

Rendering Partials by Name

Once defined, you can render a partial by name from your routes using the view#partial syntax:

const express = require('express');

const miki = require('miki-template');



const app = express();

miki.setupExpress(app, { extension: 'html', views: './views' });



app.get('/card/:id', (req, res) =>

  res.render(`home#card`, { title: 'Hello', body: 'World...', featured: true })

);
import express from 'express';

import miki from 'miki-template';



const app = express();

miki.setupExpress(app, { extension: 'html', views: './views' });



app.get('/card/:id', (req, res) =>

  res.render(`home#card`, { title: 'Hello', body: 'World...', featured: true })

);

The syntax is viewName#partialName. The engine resolves the file, extracts the named partial, and renders only that block.

Real-world HTMX example:

<!-- views/products.html -->

{% partialdef product_card %}

  <div class="product-card" id="product-{{ product.id }}">

    <img src="{{ product.image|static }}" alt="{{ product.name }}">

    <h3>{{ product.name|capfirst }}</h3>

    <p class="price">${{ product.price|floatformat:2 }}</p>

    <button hx-post="/cart/add/{{ product.id }}" hx-swap="outerHTML">

      Add to Cart

    </button>

  </div>

{% endpartialdef %}



{% for product in products %}

  {% partial product_card with product=product %}

{% endfor %}
// The entire page renders all cards

app.get('/shop', (req, res) =>

  res.render('products', { products: catalog })

);



// HTMX swaps just one card after an action

app.post('/cart/add/:id', (req, res) =>

  res.render('products#product_card', {

    product: catalog.find(p => p.id == req.params.id)

  })

);

Nested Partials

Partials can call other partials:

{% partialdef header %}

  <div class="card-header">

    <h3>{{ title }}</h3>

  </div>

{% endpartialdef %}



{% partialdef card %}

  <div class="card">

    {% partial header with title=title %}

    <p>{{ body }}</p>

  </div>

{% endpartialdef %}

Partials with Context

By default, partials inherit the parent context. Use with to pass explicit values:

{% partial card with title="Hello" body="World" %}

You can also pass context variables:

{% partial card with title=entry.title body=entry.body %}

Partials with Include

You can include a partial from another template file using the #partialName syntax:

{% include "header.html#partial_name" %}

This loads header.html, registers all its partials, and renders only the named one.

Real-world navigation include:

<!-- views/nav.html -->

{% partialdef navigation %}

  <nav>

    {% for link in links %}

      <a href="{{ link.url }}" class="{% if link.active %}current{% endif %}">{{ link.label }}</a>

    {% endfor %}

  </nav>

{% endpartialdef %}
<!-- In any template -->

{% include "nav.html#navigation" with links=nav_links %}

Rendering Partials Programmatically

renderPartialFromSource

Render a named partial from a template source string:

const { renderPartialFromSource } = require('miki-template');



const source = `{% partialdef card %}<div>{{ title }}</div>{% endpartialdef %}`;

const html = renderPartialFromSource(source, 'card', { title: 'Hello' });
import { renderPartialFromSource } from 'miki-template';



const source = `{% partialdef card %}<div>{{ title }}</div>{% endpartialdef %}`;

const html = renderPartialFromSource(source, 'card', { title: 'Hello' });

renderPartialFromFile

Render a named partial from a template file:

const { renderPartialFromFile } = require('miki-template');



const html = renderPartialFromFile('home', 'card', { title: 'Hello' }, { views: './views' });
import { renderPartialFromFile } from 'miki-template';



const html = renderPartialFromFile('home', 'card', { title: 'Hello' }, { views: './views' });

compiled.renderPartial

Render a partial from a compiled template:

const { compile } = require('miki-template');



const compiled = compile('<h1>{{ title }}</h1>', { views: './templates' });

const html = compiled.renderPartial('card', { title: 'Hello' });
import { compile } from 'miki-template';



const compiled = compile('<h1>{{ title }}</h1>', { views: './templates' });

const html = compiled.renderPartial('card', { title: 'Hello' });

compiled.renderBlock

Render a single block from a compiled template — useful for AJAX responses:

const { compile } = require('miki-template');



const compiled = compile(childTemplate, { views: './templates' });

const html = compiled.renderBlock('content', context);
import { compile } from 'miki-template';



const compiled = compile(childTemplate, { views: './templates' });

const html = compiled.renderBlock('content', context);

Express Partial Rendering

res.render with #partial

When using setupExpress(), you can render partials directly:

app.get('/card/:id', (req, res) =>

  res.render(`home#card`, { title: 'Hello', body: '...' })

);
app.get('/card/:id', (req, res) =>

  res.render(`home#card`, { title: 'Hello', body: '...' })

);

res.renderPartial middleware

If you don't want to patch res.render, add the partial renderer middleware instead:

const express = require('express');

const miki = require('miki-template');



const app = express();

app.use(miki.expressPartialRenderer());



app.get('/card', (req, res) =>

  res.renderPartial('home#card', { user: req.user })

);
import express from 'express';

import miki from 'miki-template';



const app = express();

app.use(miki.expressPartialRenderer());



app.get('/card', (req, res) =>

  res.renderPartial('home#card', { user: req.user })

);

Partial API Reference

renderPartialFromSource(fileContent, partialName, contextObj, options, filePath?)

Render a named partial from a template source string.

| Parameter | Type | Description |

|-----------|------|-------------|

| fileContent | string | Template source string |

| partialName | string | Name of the partial to render |

| contextObj | object | Variables to inject |

| options | object | Options |

| filePath | string? | Optional file path for error messages |

renderPartialFromFile(fileName, partialName, contextObj, options)

Render a named partial from a template file.

| Parameter | Type | Description |

|-----------|------|-------------|

| fileName | string | Template file name (without extension) |

| partialName | string | Name of the partial to render |

| contextObj | object | Variables to inject |

| options | object | Options including views directories |

Common Pitfalls

| Issue | Symptom | Fix |

|-------|---------|-----|

| Missing partial name | {% partial %} renders nothing | Ensure the name matches a defined partialdef. |

| Variable not found | Appears empty | Variables are resolved in the current context; use with to pass explicit values. |

| Inline vs non-inline confusion | Duplicate output | Use inline only when you want immediate rendering at the declaration site. |

| Partial leaks across includes | Unexpected partials available | include "file#partial" isolates partials; include "file" (full) makes all partials available. |

Next Steps