Custom Listing Template Is Not a Doctemplate
type: custom listing template has no doctemplate directives or still contains Quarto 1 EJS markup, so Quarto 2 skipped the listing rather than copy the file into the page.
Q-12-24 — Custom Listing Template Is Not a Doctemplate
A
type: customlisting template has no doctemplate directives or still contains Quarto 1 EJS markup, so Quarto 2 skipped the listing rather than copy the file into the page.
What this means
Quarto 2 renders custom listings with doctemplates — the same
$variable$ / $if(…)$ / $for(…)$ syntax Pandoc templates use.
The template you named in template: compiled, but either
- it contains no directives at all — every byte is literal text, so rendering it would just paste the file into your page, or
- it contains
<% … %>— Quarto 1 EJS markup, which Quarto 2 does not execute. Left in place, it would appear verbatim to readers (and link checkers would reporthref="<%= item.link %>").
Rather than publish the raw file, Quarto 2 emits this warning and leaves the listing out of the page.
Why this happens
- A Quarto 1 custom listing template carried over unchanged. Q1
listing templates are EJS (
welcome-card.ejs,item.ejs.md). Quarto 2 does not run EJS; see alsoQ-12-9, which fires on the.ejs/.ejs.mdextension at configuration time. - A partially ported template — some
<%= … %>expressions were rewritten to$…$, others were missed. - A static file named by mistake —
template:points at a plain markdown or HTML file with nothing to interpolate.
How to fix
Rewrite the template as a doctemplate. The common mappings:
| Quarto 1 (EJS) | Quarto 2 (doctemplate) |
|---|---|
<%= item.title %>
|
$it.title$ (inside $for(items)$)
|
<% for (const item of items) { %> … <% } %>
|
$for(items)$ … $endfor$
|
<% if (item.image) { %> … <% } %>
|
$if(it.image)$ … $endif$
|
<%= item.myfield %> (custom field)
|
$it.myfield$ or $it.extra.myfield$
|
<a href="<%- item.path %>">
|
[$it.title$]($it.path$) — a markdown link, not a raw anchor
|
<img src="<%= item.image %>">
|
 — a markdown image, not a raw <img>
|
| JavaScript expressions | Pre-compute the value into a listing field |
Before:
<div class="cards">
<% for (const item of items) { %>
<a href="<%= item.link %>"><%= item.title %></a>
<% } %>
</div>
After:
::: {.cards}
$for(items)$
[$it.title$]($it.path$)
$endfor$
:::
Two changes in that example are not cosmetic. The <div> became a
fenced div, whose contents Quarto parses as markdown; and the raw
<a href> became a markdown link. That second change is what makes the
href work. A template’s output is markdown, and Quarto rewrites .qmd
link targets only after parsing it — so a raw-HTML anchor keeps the
source path and ships a dead href, with no warning. Images behave the
same way, and cost more: an image written as raw HTML is never copied
into the site at all.
Listing templates covers both rules and works through a full port. The Listings guide documents the values a template can read.