Rendering LaTeX as a compile step?

I know that you can use for example KaTeX to convert LaTeX markup client-side, however I am wondering if there exists a way to pre-render LaTeX while building the website with Hugo?

According to the KaTeX website, it’s possible to use it server-side in a Node environment, but as far as I can tell in Hugo there’s currently no way to just plug it in as a compile step.

Server-side or CLI rendering with KaTeX produces HTML, not an image, so you would still need to include CSS on the client side. If this is acceptable, roll your own local server, then use Hugo’s resources.GetRemote to send the tex and retrieve the HTML. The results are cached (getresource).

You cannot produce an SVG with KaTex, but you can with MathJax.

See https://discourse.gohugo.io/t/show-hugo-compile-time-latex-svg/15770.

Although the hosted app is no longer active, you can clone the repository and run it locally.

Follow-up…

The topic I referenced above was written by @atishay, the author of Hugo In Action. This is an excellent resource for every site and theme developer.

He describes this approach in detail in Chapter 11, Section 2 (Adding LaTeX rendering to our website).

This is the code repository for the examples in the book:
https://github.com/hugoinaction/hugoinaction/

And here’s the branch that corresponds with Chapter 11, Section2:
https://github.com/hugoinaction/hugoinaction/tree/chapter-11-02

1 Like

@jmooring As of Chrome 109, all major browsers (Opera requiring special config) supper MathML syntax without requiring CSS. Much like SVG it’s got it’s own syntax and rendering style. The Hugo In Action way is to have a Netlify function that outputs MathJax’s SVG in a JSON file.

KaTeX can produce the markup that would be great as a server-side render.

@kamov I’ve been exploring this too. It doesn’t appear that Hugo has a render injection point, even with a shortcode that can render these appropriately (I’m still working this though…) another option would be to ask the contributors to bring the KaTeX extension into goldmark (but it’s not an official extension). If I find a way of doing it sans the goldmark extension, I’ll try to post here.

The code below works for me in all my browsers, without CSS:

<math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
  <semantics>
    <mrow>
      <msub>
        <mi>y</mi>
        <mi>t</mi>
      </msub>
      <mo>=</mo>
      <msub>
        <mi>β</mi>
        <mn>0</mn>
      </msub>
      <mo>+</mo>
      <msub>
        <mi>β</mi>
        <mn>1</mn>
      </msub>
      <msub>
        <mi>x</mi>
        <mi>t</mi>
      </msub>
      <mo>+</mo>
      <msub>
        <mi>ϵ</mi>
        <mi>t</mi>
      </msub>
    </mrow>
    <annotation encoding="application/x-tex">
      y_t=\beta_0+\beta_1 x_t + \epsilon_t
    </annotation>
  </semantics>
</math>

This code block render hook renders an SVG image from LaTeX mathematical markup using the math.vercel.app service. This is performed when you build your site; no CSS or JS required.

layouts/_default/_markup/render-codeblock-latex.html
{{- /*
Renders an SVG image from LaTeX mathematical markup.

This render hook passes the contents of the fenced code block to a remote
service (math.vercel.app) which converts the LaTeX to SVG using MathJax.

References:

  - https://math.vercel.app/
  - https://github.com/uetchy/math-api
  - https://docs.mathjax.org/

@param {string} color The foreground color specified as a CSS hex color or CSS named color.
*/}}

{{- /* Initialize. */}}
{{- $apiEntryPoint := "https://math.vercel.app" }}
{{- $color := .Attributes.color | default "" }}
{{- $latex := trim .Inner "\n\r" }}

{{- /* Determine display mode. */}}
{{- $displayMode := true }}
{{- if or (strings.HasPrefix $latex `$$`) (strings.HasPrefix $latex `\[`) }}
  {{- $displayMode = true }}
{{- else if or (strings.HasPrefix $latex `$`) (strings.HasPrefix $latex `\(`) }}
  {{- $displayMode = false }}
{{- end }}

{{- /* Strip display mode indicators. */}}
{{- $latex = trim $latex `$` }}
{{- $latex = $latex | strings.TrimPrefix `\[` }}
{{- $latex = $latex | strings.TrimPrefix `\(` }}
{{- $latex = $latex | strings.TrimSuffix `\]` }}
{{- $latex = $latex | strings.TrimSuffix `\)` }}

{{- /* Create query string. */}}
{{- $mode := "inline" }}
{{- if $displayMode }}
  {{- $mode = "from" }}
{{- end }}
{{- $qs := querify $mode $latex "color" $color }}

{{- /* Get SVG. */}}
{{- $url := printf "%s?%s" $apiEntryPoint $qs }}
{{- with resources.GetRemote $url }}
  {{- with .Err  }}
    {{- errorf "%s" . }}
  {{- else }}
    {{- if $displayMode }}
      <div class="math math-block"><img src="{{ .RelPermalink }}" alt=""></div>
    {{- else }}
      <span class="math math-inline"><img src="{{ .RelPermalink }}" alt=""></span>
    {{- end }}
  {{- end }}
{{- else  }}
  {{- errorf "Unable to convert LaTex to SVG. See %s" $.Position }}
{{- end }}


markdown

```latex
$$\frac{1}{\Gamma(s)}\int_{0}^{\infty}\frac{u^{s-1}}{e^{u}-1}\mathrm{d}u$$
```

result

image

The foreground and background are inverted when the browser is in dark mode.

The result of the remote call is cached; you are not hitting the service on every build.

This example site includes the render hook and a shortcode. You can use the shortcode to produce inline equations.

git clone --single-branch -b hugo-forum-topic-42145 https://github.com/jmooring/hugo-testing hugo-forum-topic-42145
cd hugo-forum-topic-42145
hugo server
1 Like