Back

Updating KaTeX for Zola 0.23

2026-09-01

When I set up KaTeX for this Zola-based site, I used a feature called shortcodes to expand the LaTeX code into an environment using KaTeX.

In 0.23, Zola removed shortcodes, replacing them with the component feature of the Tera rendering engine that Zola uses.

Broadly, this change requires minimal updates. Tera components look a lot like the old Zola shortcodes, but with possibly more functionality.

Components

Let's say your old shortcode looks like so.

<script type="math/tex mode=display">
    {{ body | safe }}
</script>

Then, you would just want to add the component start and end matter to make the resulting Tera component.

{% component katex() %}
<script type="math/tex mode=display">
    {{ body | safe }}
</script>
{% endcomponent katex %}

The definition now looks like a function. Tera components also allow for typed arguments, but I didn't need them here.

Usage

Then, changing the usage in place is fairly straightforward. The main switch is from a function notation katex() to a <katex>.

So if the original block uses katex() and end:

{% katex() %}
\begin{align}
  y &= (x + 1)^2 \\
    &= x^2 + 2x + 1
\end{align}
{% end %}

Then you would call it with <katex> and </katex> instead.

{% <katex> %}
\begin{align}
  y &= (x + 1)^2 \\
    &= x^2 + 2x + 1
\end{align}
{% </katex> %}

Strangely, the Tera component uses a function-like definition, but a tag-like call, while the Zola shortcode used a function-like call.

Going Further

The function-like definition of Tera components opens up some optional behavior that you can enable with the function arguments.

Equation Number

We sometimes want to be able to number equations, like so. The numbering should be optional, since we don't always want it if we don't need it, but the numbers should be automatic so we don't have to worry about them.

We'll use CSS counters to make sure the numbers are sequential with no gaps. In this example code, I create a counter called equation-number that starts at 1 for the entire page, then increments at every numbered-equation class. Well, it starts at zero but we'll increment it right before the first usage.

body {
  counter-reset: equation-number;
}

.numbered-equation {
  counter-increment: equation-number;
  position: relative;
}

.numbered-equation::after {
  content: "(" counter(equation-number) ")";
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
}

The remaining CSS just aligns the number to the right side. The numbered-equation position doesn't move it, but does set a reference for the numbered-equation::after block. Then, it's positioned vertically centered and to the far right side.

We'll need to add that to the KaTeX component, with an optional argument for numbering the equation. Since this argument defaults to false, we won't see numbers back in earlier math formulas.

{% component katex(numbered: bool = false) %}
<div
{%- if numbered %} class="numbered-equation"{% endif -%}
>
<script type="math/tex mode=display">{{ body | trim | safe }}</script>
</div>
{% endcomponent katex %}

We can use the if numbered block to include the class if numbered={true} was passed. (Note that it's {true} not true as a quirk of Tera components; all non-string values must be in curly brackets.)

One gotcha is that the <div> can cause some weird whitespace processing, so we need to handle blank lines carefully. In Tera, %- before if absorbs the newline before the numbered block, while the -% after endif absorbs the following newline. You could avoid these by putting the <div> all on one long line. Note that there's whitespace before class since we don't want to absorb all the whitespace and have a divclass!

I've also added trim for the body to keep out leading or trailing newlines.

Linkable Equations

If we need to refer to an earlier equation, we might also want to be able to link to it. For instance, see the normal CDF.

The same idea here applies: add an optional argument that inserts an id into the div so we can link to it.

{% component katex(label: string = "") %}
<div
{%- if label %} id="equation-{{ label }}"{% endif -%}
>
<script type="math/tex mode=display">{{ body | trim | safe }}</script>
</div>
{% endcomponent katex %}

If we defined a formula with <katex label="normal"> (no brackets needed around the string "normal") then we could reference it with the link #equation-normal.

All Together Now

The full component I'm using here is:

{% component katex(label: string = "", numbered: bool = false) %}
<div
{%- if label %} id="equation-{{ label }}"{% endif %}
{%- if numbered %} class="numbered-equation"{% endif -%}
>
<script type="math/tex mode=display">{{ body | trim | safe }}</script>
</div>
{% endcomponent katex %}

There's still a lot you can do: make the numbers linkable, allow for names rather than numbers, etc.