Template Partials

Partials are reusable template fragments. Include them with {{> name}} syntax. They live in the _shared/ directory at the notifications root.

Basic partials

Place shared fragments in notifications/_shared/ as .md files:

notifications/
  _shared/
    email-header.md
    email-footer.md
    slack-footer.md
  transactional/
    order-shipped/
      email.md
      slack.md

Include a partial by name (without the .md extension):

{{> email-header}}
 
# Your order has shipped
 
Body content here.
 
{{> email-footer}}

{{> email-header}} loads _shared/email-header.md and inserts its content inline. The partial can contain any valid template syntax -- directives, expressions, images, buttons.

If a partial is not found, the {{> name}} tag is left as-is in the output.

Parameterized partials

Pass named parameters to make partials reusable across different data contexts:

{{> product-card name=item.name price=item.price title="Featured"}}

Inside the partial, reference parameters with the {{@param}} prefix:

<!-- _shared/product-card.md -->
**{{@name}}** — {{@price | currency "USD"}}
 
_{{@title}}_

Path vs literal parameters

Parameters come in two forms:

Form Syntax Behavior
Path name=item.name {{@name}} becomes {{item.name}} -- resolved at compile time from the data context
Literal title="Featured" {{@title}} becomes the string Featured directly

Path parameters use bare unquoted values. Literal parameters use double or single quotes.

{{> card
    name=product.name
    price=product.price
    label="New Arrival"
}}

After expansion, the partial content contains standard {{expressions}} that the compiler resolves against the data object.

Pipes with parameters

Parameters work with pipe chains. Inside a partial:

Price: {{@price | currency "USD"}}

When called with a path parameter (price=item.cost), this expands to:

Price: {{item.cost | currency "USD"}}

When called with a literal parameter (price="49.99"), this expands to:

Price: {{"49.99" | currency "USD"}}

The pipe chain is preserved in both cases -- only the @ reference is substituted.

Nesting

Partials can include other partials. A product card might include a price tag partial:

<!-- _shared/product-card.md -->
**{{@name}}**
{{> price-tag price=@price}}
<!-- _shared/price-tag.md -->
{{@price | currency "USD"}}

Norq detects cycles and prevents infinite recursion. If partial a includes b which includes a, the cycle is broken with an HTML comment:

<!-- partial cycle: a -->

Missing parameters

If a partial references a parameter that wasn't passed by the caller, the {{@unknown}} tag is left as-is. This is not an error -- it allows partials to have optional parameters.

<!-- _shared/card.md -->
**{{@title}}**
{{@subtitle}}
{{> card title=product.name}}

Result: {{@subtitle}} remains in the output unchanged.

Example: reusable product card

_shared/product-card.md:

::: callout
**{{@name}}**
 
{{@price | currency "USD"}}
 
::: if @description
_{{@description}}_
:::
 
[View Details]({{@url}}){button.secondary}
:::

Caller in email.md:

::: each order.items as item
{{> product-card name=item.name price=item.price url=item.detail_url description=item.summary}}
:::

Caller with literal fallback:

{{> product-card name=item.name price=item.price url=item.detail_url description="No description available"}}

LSP support

The Norq language server provides completions for partials:

  • {{> triggers partial name completions -- lists all .md files in _shared/
  • {{@ triggers parameter completions inside partial files -- lists known parameter names

See the LSP page for editor setup.