Math
The @samplekit/preprocess-katex npm package and samplekit.svelte-pp-katex VS Code extension allow you to use KaTeX directly within your Svelte template.
Feature Overview
- Write beautiful math in Svelte templates. This preprocessor wraps KaTeX, a package
that supports a subset of TeX macros and some higher order LaTeX macros found in packages like amssymb. If
you're not familiar with TeX, it's a typesetting system commonly used in STEM fields. These macros let you
easily write math like this . That equation is written like
this:
.\( x = {-b \pm \sqrt{b^2 - 4ac} \over 2a} \)
- Eliminate flashes of unstyled content. By using a preprocessor directly on the server, delays and flashes of unstyled content are avoided. The page can also be statically generated if desired.
- Work with existing tooling. There's no need to invent a new file extension or disable other tools. By using regular HTML comments in
.svelte
files we ensure thatPrettier, ESLint, svelte-check, svelte.svelte-vscode
, etc. leave our code alone. The code is processed into something Svelte understands, but if the preprocessor isn't installed yet, nothing breaks – the code simply turns into an HTML comment. - Code with TextMate support. The VS Code extension provides you with full highlighting support. It injects the LaTeX TextMate grammar scope between the delimiters to work with your VS Code theme.
Installation
- Install the preprocessor
pnpm add -D @samplekit/preprocess-katex
- Add to
svelte.config.js
import { processKatex, createKatexLogger } from '@samplekit/preprocess-katex'; import adapter from '@sveltejs/adapter-auto'; import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; const preprocessorRoot = `${import.meta.dirname}/src/routes/`; const formatFilename = (/** @type {string} */ filename) => filename.replace(preprocessorRoot, ''); /** @type {import('@sveltejs/kit').Config} */ const config = { preprocess: [ processKatex({ include: (filename) => filename.startsWith(preprocessorRoot), logger: createKatexLogger(formatFilename), }), vitePreprocess(), ], kit: { adapter: adapter(), }, }; export default config;
- Install the VS Code extension (for snippets and syntax highlighting) samplekit.svelte-pp-katex
Svelte Template
Display
There are four wrappers around math:
- Display math in Svelte templates:
<!-- \[ ... \] -->
- Inline math in Svelte templates:
<!-- \( ... \) -->
- Display math via JavaScript:
const eq = LaTeX`...`;
- Inline math via JavaScript:
const eq = LaTeX`...`;
Author with the highlighting extension:
<!-- \[
\begin{align}
\dot{x} & = \sigma(y-x) \\
\dot{y} & = \rho x - y - xz \\
\dot{z} & = -\beta z + xy
\end{align}
\] -->
Author without the highlighting extension:
<!-- \[
\begin{align}
\dot{x} & = \sigma(y-x) \\
\dot{y} & = \rho x - y - xz \\
\dot{z} & = -\beta z + xy
\end{align}
\] -->
After preprocessing:
Inline
With highlighting extension:
<!--\( V={4 \over 3}\pi r^{3} \) -->
Without highlighting extension:
<!-- \( V={4 \over 3}\pi r^{3} \) -->
After preprocessing:
JavaScript Template Literal
The VS Code extension also highlights LaTeX
template literals. To use it, it's helpful to make a couple simple
wrapper components
<script lang="ts">
import { katex } from '..';
const { eq }: { eq: string } = $props();
</script>
{@html katex.renderToString(eq)}
<script lang="ts">
import { katex } from '..';
const { eq }: { eq: string } = $props();
</script>
<div class="overflow-x-auto">{@html katex.renderToString(eq, { displayMode: true })}</div>
- Create an equation with the
LaTeX
template tagged literal. - Pass the string to either the
Display
orMath
components. - Enjoy regular variable substitution directly in !
With highlighting extension:
const eq1 = LaTeX`
x = {-b \pm \sqrt{b^2-4ac} \over 2a}
`;
Without highlighting extension:
const eq1 = LaTeX`
x = {-b \pm \sqrt{b^2-4ac} \over 2a}
`;
After preprocessing:
Reactivity
This is Svelte, so let's write reactive LaTeX directly in the Svelte template!
With Template Literals
With template literals, all the backslashes must be escaped. Considering the amount that LaTeX uses, this can be tedious. It also has the drawback of destroying the syntax highlighting.
<script lang="ts">
let r = $state(1);
const A = $derived((Math.PI * r ** 2).toFixed(2));
const eq = $derived(`\\begin{align*}
r &= ${r} \\\\
r^2 &= ${r ** 2} \\\\
A = \\pi r^2 &= ${A} \\\\
\\end{align*}`);
</script>
<Display {eq}></Display>
To get around this, we can use a special LaTeX command directly in the template.
In the Markup
Use \s
to define a reactive Svelte variable.
With hardcoded values.
\begin{align*}
r &= 2 \\
r^2 &= 4 \\
A = \pi r^2 &= 12.57 \\
\end{align*}
With Svelte values.
\begin{align*}
r &= \s{r} \\
r^2 &= \s{r ** 2} \\
A = \pi r^2 &= \s{A} \\
\end{align*}
Hardcoded
Sveltiful!
Behind the scenes
(If you care about how it works under the hood).
Because neither Svelte nor KaTeX can handle each other's syntax we can't simply use regular Svelte handlebar
substitution. KaTeX would choke on the syntax. Instead, the preprocessor plucks out the Svelte content defined in `\s{}`
, stores it, passes the Svelte free content to KaTeX, and then puts the Svelte
content back in when KaTeX has finished.
// the nuts and bolts of it
const { svelteFreeString, extractedSvelteContent } = replaceSvelteAndStore(rawInput);
const mathString = katex.renderToString(svelteFreeString)
const parsed = restoreSvelte(mathString, extractedSvelteContent);