Next JS 14 + Katex web app
Tags: nextjs, fullstack, javascript, webdev
Published: 2024-01-12 03:12:23

If you are building a STEM web application, there is a certainty that you will need to render formulae, chemical reactions and equations. STEM audiences are used to writing these in LaTeX format. In this post, we will create a React component that can render these LaTeX equations with the help of the KaTeX library.

## Latex equation to render

We will look at quadratic formula text for this blog post. If the text is 

```
Given a general quadratic equation of the form
$$ax^{2} + bx + c = 0$$
with $x$ representing an unknown, with $a$, $b$ and $c$ representing constants, and with $a \ne 0$, the quadratic formula is:
$$x = \frac{-b \pm \sqrt{b^{2} - 4ac}}{2a}$$
```

The above block of text should be rendered as 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/44xhuk76y1wzg0ge6hcv.png)

## ❗️Spoiler❗️
The code for this is available here: https://github.com/kirankulkarni/next-katex

## Setup details

For this post, I have used. 

1. Next JS 14.0.4
    1. Using Javascript
    2. Using the App router
2. Tailwind CSS 3.3.0
3. Katex 0.16.9

## Step 1: Install katex in your NextJS setup
```bash
npm i katex
```

## Step 2: Build a KatexSpan component that can render the equations

### Step 2.1: Understanding Katex Auto Render extension usage.

We will be using the Katex Auto Render extension. This automatically detects equations in the child nodes and converts them into equations. You can find a detailed explanation [here](https://katex.org/docs/autorender). 

The auto render provides the function `renderMathInElement` that takes a DOM node reference and options. The function will recursively search for text nodes inside this element and render the math in them. [refer to API for more info](https://katex.org/docs/autorender#api)

### Step 2.2: Create a KatexSpan component file

Ensure that it’s a client component by mentioning `use client;` as the first line of the file. 

### Step 2.3: Importing `renderMathInElement` function

The Katex library does pack the extension, and hence, we can import it as

```jsx
import renderMathInElement from 'katex/dist/contrib/auto-render';
import 'katex/dist/katex.min.css';
```
The imported `katex.min.css` controls the look & feel of the rendered equations.

### Step 2.4: Create a Ref for the node that will contain the text with equations

The Katex API `renderMathInElement` needs a reference to the DOM node. We can use React [Refs](https://react.dev/learn/manipulating-the-dom-with-refs) to get the reference to the DOM node that will have the text with equations.

```jsx
const katexTextRef = useRef();
```
Ensure that you have imported useRef from React. 

### Step 2.5: Add JSX for the KatexSpan element

Since we want to use this as a span element, we will keep it simple.

```jsx
return (
    <div ref={katexTextRef} {...delegated}>
      {text}
    </div>
  );
```

Note that we have used the `ref` property to ensure that React provides a reference to this DOM node for us to use.

### Step 2.6 Leverage `useEffect` hook to run the KaTeX Autorender `renderMathInElement.`

Now, that we have the reference, we can use `useEffect` that runs the first time after the DOM has been constructed.

```jsx
useEffect(() => {
    if (katexTextRef.current) {
      renderMathInElement(katexTextRef.current, {
        delimiters: [
          { left: '$$', right: '$$', display: true },
          { left: '$', right: '$', display: false },
        ],
      });
    }
  }, []);
```

Notice that we are checking if `KatexTextRef` has been assigned before running the auto render

## Wiring it all up
```jsx
'use client';
import renderMathInElement from 'katex/dist/contrib/auto-render';
import 'katex/dist/katex.min.css';
import { useEffect, useRef } from 'react';

export default function KatexSpan({ text, ...delegated }) {
  const katexTextRef = useRef();
  useEffect(() => {
    if (katexTextRef.current) {
      renderMathInElement(katexTextRef.current, {
        delimiters: [
          { left: '$$', right: '$$', display: true },
          { left: '$', right: '$', display: false },
        ],
      });
    }
  }, [text]);

  return (
    <div ref={katexTextRef} {...delegated}>
      {text}
    </div>
  );
}
```