How to Add Math Equations and Formulas to FluentCommunity Courses Using KaTeX

If you create mathematics courses with FluentCommunity, you may quickly discover a problem: writing mathematical notation inside lessons is not as easy as writing normal text.

A formula like:

x² + y² = z²

looks acceptable in plain text, but it is not the same as a properly typeset mathematical expression.

For educational content, especially mathematics courses, clean notation matters.

Students should see formulas in the same way they appear in textbooks.

In this tutorial, I will show how to add LaTeX-style mathematical formulas to FluentCommunity using KaTeX.

Why KaTeX?

KaTeX is a fast mathematical typesetting library created by Khan Academy.

It converts LaTeX formulas into beautifully formatted mathematics directly in the browser.

Advantages:

  • fast rendering,
  • no need to create images,
  • works well on mobile devices,
  • supports most common LaTeX notation,
  • keeps your lessons editable.

Instead of uploading hundreds of screenshots of formulas, you can simply write:

$a^2+b^2=c^2$

and FluentCommunity will display:

\(a^2+b^2=c^2\)

Why normal WordPress methods may not work

FluentCommunity works differently from a traditional WordPress page.

Lessons are loaded dynamically as part of a Vue application.

Because of this:

  • scripts added directly inside lesson content are not executed,
  • some WordPress snippets may run before the lesson appears,
  • mathematical rendering must wait until FluentCommunity loads the lesson.

The solution is to add the JavaScript globally using FluentCommunity Custom JavaScript.

Adding KaTeX support

Open:

FluentCommunity → Settings → Custom JavaScript

Add the KaTeX integration script.

The script loads the KaTeX library, watches for newly loaded lessons and automatically converts mathematical expressions.

Supported formula formats

After installation you can use several formats.

Inline formulas

Write:

The solution of $2x+3=7$ is $x=2$.

This creates an inline mathematical expression.

Block formulas

For larger equations use:

$$
\frac{-b\pm\sqrt{b^2-4ac}}{2a}
$$

The formula will appear as a centered mathematical block.

FluentCommunity-friendly tags

You can also use:

[math]a^x=b[/math]

or:

[math-block]
P=\pi r^2
[/math-block]

These formats are useful if your existing lessons already contain mathematical notation.

Using shortcuts

The integration can also include custom macros.

For example:

Instead of:

\mathbb{R}

you can write:

\RR

and get the set of real numbers.

Useful shortcuts:

\NN

natural numbers

\ZZ

integers

\QQ

rational numbers

\RR

real numbers

\CC

complex numbers

Why this matters for online mathematics education

Images of formulas are not a good long-term solution.

They create several problems:

  • they are harder to edit,
  • they are not searchable,
  • they may look blurry,
  • they slow down content creation.

With KaTeX, your mathematics remains text-based, editable and scalable.

For a mathematics course platform, this is a much more sustainable approach.

The script

<script>
(function () {

    const DEBUG = false;
    const KATEX_VERSION = "0.16.9";

    const macros = {
        "\\RR": "\\mathbb{R}",
        "\\NN": "\\mathbb{N}",
        "\\ZZ": "\\mathbb{Z}",
        "\\QQ": "\\mathbb{Q}",
        "\\CC": "\\mathbb{C}",
        "\\dd": "\\mathrm{d}",

        "\\red": "\\color{red}{#1}",
        "\\blue": "\\color{blue}{#1}",
        "\\green": "\\color{green}{#1}",
        "\\orange": "\\color{orange}{#1}",
        "\\purple": "\\color{purple}{#1}",
        "\\gray": "\\color{gray}{#1}",
        "\\black": "\\color{black}{#1}"
    };


    let equationCounter = 0;
    let currentLessonSignature = "";


    function log(...args) {
        if (DEBUG) {
            console.log("[PM Math]", ...args);
        }
    }


    function loadCSS(url) {

        return new Promise(resolve => {

            if (document.querySelector(`link[href="${url}"]`)) {
                resolve();
                return;
            }

            const link = document.createElement("link");

            link.rel = "stylesheet";
            link.href = url;

            link.onload = resolve;

            document.head.appendChild(link);

        });

    }



    function loadScript(url) {

        return new Promise(resolve => {

            if (document.querySelector(`script[src="${url}"]`)) {
                resolve();
                return;
            }

            const script = document.createElement("script");

            script.src = url;

            script.onload = resolve;

            document.head.appendChild(script);

        });

    }



    async function loadKatex() {

        await loadCSS(
            `https://cdn.jsdelivr.net/npm/katex@${KATEX_VERSION}/dist/katex.min.css`
        );


        await loadScript(
            `https://cdn.jsdelivr.net/npm/katex@${KATEX_VERSION}/dist/katex.min.js`
        );


        await loadScript(
            `https://cdn.jsdelivr.net/npm/katex@${KATEX_VERSION}/dist/contrib/auto-render.min.js`
        );


        addStyles();

        initRenderer();

        log("KaTeX loaded");

    }


    function addStyles() {

        if (document.getElementById("pm-math-styles")) {
            return;
        }

        const style = document.createElement("style");

        style.id = "pm-math-styles";


        style.innerHTML = `

        .math-equation {
            display:flex;
            align-items:center;
            justify-content:center;
            position:relative;
            width:100%;
            margin:1.5em 0;
        }

        .eq-formula {
            text-align:center;
        }

        .equation-number {
            position:absolute;
            right:10px;
            font-size:.9em;
            opacity:.7;
        }

        .math-box {
            margin:1.5em 0;
            padding:1em 1.2em;
            border-radius:8px;
            line-height:1.6;
        }

        .math-box-title {

            font-weight:700;
            margin-bottom:.5em;

        }

        .math-step {
            background:rgba(28,36,75,.08);
            border-left:4px solid #1c244b;
        }

        .math-idea {
            background:#eef6ff;
            border-left:4px solid #2563eb;
        }

        .math-definition {
            background:#f0fdf4;
            border-left:4px solid #16a34a;
        }

        .math-example {
            background:#eff6ff;
            border-left:4px solid #2563eb;
        }

        .math-key {
            background:#eef2ff;
            border-left:4px solid #4338ca;
        }

        .math-hint {
            background:#fff8dc;
            border-left:4px solid #d97706;
        }

        .math-think {
            background:#ecfeff;
            border-left:4px solid #0891b2;
        }

        .math-note {
            background:#f3f4f6;
            border-left:4px solid #6b7280;
        }

        .math-warning {
            background:#fff7ed;
            border-left:4px solid #ea580c;
        }

        .math-mistake {
            background:#fff1f2;
            border-left:4px solid #dc2626;
        }

        .math-strategy {
            background:#faf5ff;
            border-left:4px solid #9333ea;
        }

        .math-summary {
            background:rgba(28,36,75,.12);
            border-left:4px solid #1c244b;
        }

        .math-mark {
            background:rgba(28,36,75,.12);
            border-left:4px solid #1c244b;
            font-weight:600;
        }
        `;

        document.head.appendChild(style);

        log("Styles added");
    }

    function processBoxes(root) {
        root.querySelectorAll(".feed_md_content").forEach(el => {
            if (el.dataset.boxesProcessed) {
                return;
            }

            const boxes = [
                {
                    tag: "step",
                    cls: "math-step",
                    title: "Krok"
                },

                {
                    tag: "idea",
                    cls: "math-idea",
                    title: "Najważniejsza idea"
                },

                {
                    tag: "definition",
                    cls: "math-definition",
                    title: "Definicja"
                },

                {
                    tag: "example",
                    cls: "math-example",
                    title: "Przykład"
                },

                {
                    tag: "key",
                    cls: "math-key",
                    title: "Zapamiętaj"
                },

                {
                    tag: "hint",
                    cls: "math-hint",
                    title: "Wskazówka"
                },

                {
                    tag: "think",
                    cls: "math-think",
                    title: "Pomyśl"
                },

                {
                    tag: "note",
                    cls: "math-note",
                    title: "Uwaga"
                },

                {
                    tag: "warning",
                    cls: "math-warning",
                    title: "Uwaga"
                },

                {
                    tag: "mistake",
                    cls: "math-mistake",
                    title: "Częsty błąd"
                },

                {
                    tag: "strategy",
                    cls: "math-strategy",
                    title: "Strategia"
                },

                {
                    tag: "summary",
                    cls: "math-summary",
                    title: "Podsumowanie"
                }
            ];

            boxes.forEach(box => {
                const regex = new RegExp(
                    "\\[" + box.tag + "(?::([^\\]]+))?\\]([\\s\\S]*?)\\[\\/" + box.tag + "\\]",
                    "gi"
                );

                el.innerHTML = el.innerHTML.replace(
                    regex,
                    function(match, customTitle, content) {
                        const title =
                            customTitle || box.title;
                        return `

                        <div class="math-box ${box.cls}">
                            <div class="math-box-title">
                                ${title}
                            </div>

                            <div>
                                ${content}
                            </div>
                        </div>
                        `;
                    }
                );
            });

            el.innerHTML = el.innerHTML.replace(
                /\[mark(?::([^\]]+))?\]([\s\S]*?)\[\/mark\]/gi,

                function(match, customTitle, content) {


                    return `

                    <div class="math-box math-mark">

                        <div class="math-box-title">
                            ${customTitle || "Najważniejsze"}
                        </div>

                        <div>
                            ${content}
                        </div>
                    </div>
                    `;
                }
            );


            el.dataset.boxesProcessed = "true";
        });
    }
   function resetEquationCounterIfNeeded() {

        const lesson =
            document.querySelector(".feed_md_content");

        if (!lesson) {
            return;
        }

        const signature =
            lesson.innerText.substring(0, 500);

        if (signature !== currentLessonSignature) {
            equationCounter = 0;
            currentLessonSignature = signature;
            log("Equation counter reset");
        }
    }

    function processEquations(root) {

        root.querySelectorAll(".feed_md_content").forEach(el => {

            if (el.dataset.eqProcessed) {
                return;
            }

            el.innerHTML = el.innerHTML.replace(
                /\[eq\]([\s\S]*?)\[\/eq\]/gi,

                function(match, formula) {
                    equationCounter++;
                    return `

                    <div class="math-equation">
                        <span class="eq-formula">
                            ${formula}
                        </span>

                        <span class="equation-number">
                            (${equationCounter})
                        </span>
                    </div>
                    `;
                }
            );
            el.dataset.eqProcessed = "true";
        });
    }

    function renderEquations(root) {
        root.querySelectorAll(".eq-formula").forEach(el => {
            if (el.dataset.rendered) {
                return;
            }

            try {
                katex.render(
                    el.textContent,
                    el,
                    {
                        macros: macros,
                        throwOnError: false,
                        strict: "ignore",
                        trust: true
                    }
                );
            }
            catch(error) {
                console.warn(
                    "[PM Math] Equation error:",
                    error
                );
            }
            el.dataset.rendered = "true";
        });
    }

    function renderInlineMath(root) {

        if (
            typeof renderMathInElement !== "function"
        ) {
            return;
        }

        root.querySelectorAll(".feed_md_content").forEach(el => {

            if (el.dataset.mathRendered) {
                return;
            }
            renderMathInElement(
                el,
                {
                    delimiters: [

                        {
                            left: "$$",
                            right: "$$",
                            display: true
                        },

                        {
                            left: "$",
                            right: "$",
                            display: false
                        },

                        {
                            left: "[math]",
                            right: "[/math]",
                            display: false
                        },

                        {
                            left: "[math-block]",
                            right: "[/math-block]",
                            display: true
                        }
                    ],
                    macros: macros,
                    throwOnError: false,
                    strict: "ignore",
                    trust: true
                }
            );
            el.dataset.mathRendered = "true";
        });
    }

    function renderMath(root) {
        resetEquationCounterIfNeeded();
        processBoxes(root);
        processEquations(root);
        renderEquations(root);
        renderInlineMath(root);
    }


    function initRenderer() {
        renderMath(document);

        const observer = new MutationObserver(
            mutations => {
                mutations.forEach(mutation => {
                    mutation.addedNodes.forEach(node => {

                        if (
                            !(node instanceof HTMLElement)
                        ) {
                            return;
                        }

                        renderMath(node);
                    });
                });
            }
        );

        observer.observe(
            document.body,
            {
                childList: true,
                subtree: true
            }
        );
        log("Observer started");
    }
    loadKatex();
})();
</script>

Final thoughts

FluentCommunity is a powerful platform for online communities, but mathematical education has special requirements.

Adding KaTeX support makes it much better suited for mathematics teachers and course creators.

Once installed, you can focus on creating lessons instead of preparing hundreds of formula images.


Read more on how to use the script here.

Zostaw komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Przewijanie do góry