Adding article references to the Blog.
2026-08-23 · 3 min · Jasper Dijt
My previous post Avoiding Tailscale's DERP relays in my Homelab. was my first long post where I used references, so I added functionality for that to my site. Because at its core a Roq site is just another Quarkus/Java/Maven project it is really easy to include a bit of custom functionality.
At least, if you know Java, Quarkus and Qute.
The design is as follows:
- References are declared in the front matter of the post;
- References are cited via a custom tag;
- The template automatically renders all references from the front-matter at the end of the post.
Declaring references
A sample declaration:
references:
- id: bgp-with-cilium-and-opnsense
url: "https://jasperdijt.eu/posts/avoiding-tailscale-s-derp-relays-in-my-homelab/"
title: "Avoiding Tailscale's DERP relays in my Homelab."
accessed: "2026-08-16"
Using references
A reference is cited via the custom tag:
{#ref page id="refid" /}
Which is defined as:
{@io.quarkiverse.roq.frontmatter.runtime.model.DocumentPage it}
{@java.lang.String id}
<span class="ref">
<a href="#ref-{id}">[{it.refNumber(id)}]</a>
</span>
Unfortunately I could not avoid the need to pass the page as well, because Qute tags do not have implicit access to it.
Rendering references
This partial is included at the end of every blog post:
{@io.quarkiverse.roq.frontmatter.runtime.model.DocumentPage page}
{#if page.sortedReferences.size > 0}
<section class="references">
<h2>References</h2>
<ol>
{#for ref in page.sortedReferences}
<li id="ref-{ref.id}">
<a href="{ref.url}">{ref.title ?: ref.url}</a>
{#if ref.accessed??} – accessed {ref.accessed}{/if}
</li>
{/for}
</ol>
</section>
{/if}
Helper class
To make all of this work a small template extension [1] was needed. This little helper enforces consistent sorting of the references and therefore ensures that the anchors work properly.
@TemplateExtension
public class ReferencesExtension {
public static List<JsonObject> sortedReferences(DocumentPage page) {
return sortedReferences(page.data().getJsonArray("references"));
}
public static int refNumber(DocumentPage page, String id) {
return refNumber(page.data().getJsonArray("references"), id);
}
static List<JsonObject> sortedReferences(JsonArray references) {
if (references == null) {
return List.of();
}
return references.stream()
.map(JsonObject.class::cast)
.sorted(Comparator.comparing(
ReferencesExtension::sortKey,
String.CASE_INSENSITIVE_ORDER
))
.toList();
}
static int refNumber(JsonArray references, String id) {
var sorted = sortedReferences(references);
for (int i = 0; i < sorted.size(); i++) {
if (id.equals(sorted.get(i).getString("id"))) {
return i + 1;
}
}
throw new IllegalArgumentException("Unknown reference id: " + id);
}
private static String sortKey(JsonObject reference) {
var title = reference.getString("title");
if (isNotNullOrBlank(title)) {
return title;
}
var url = reference.getString("url");
if(isNotNullOrBlank(url)){
return url;
}
throw new IllegalArgumentException("Reference must have title or url set");
}
private static boolean isNotNullOrBlank(String s){
return !(s == null || s.isBlank());
}
}
Conclusion
With just a tiny bit of code I was able to add a great quality of life improvement for the blog. Now for every post I have a central overview of references in the source, and references are always rendered in order on the site.
References
- Qute Templating Engine - Template Extension Methods – accessed 2026-08-16