Verbatim environments
Carve out custom tags whose content SvelTeX preprocesses specially before the Svelte compiler ever sees it.
Four processing modes:
'tex'compiles a LaTeX snippet to SVG,'code'runs the body through the code backend,'escape'HTML- and brace-escapes the body so Svelte leaves it alone,'noop'skips SvelTeX entirely and passes the body straight to Svelte.Attribute forwarding: Allowlist or blocklist which attributes flow from the source tag through to the rendered component, so build-time options (
preamble,caption, …) can sit alongside runtime props without leaking.Aliases: Several tag names can resolve to the same environment — handy for keeping
<tex>/<latex>/<tikz>interchangeable in your source.
A verbatim environment is a Svelte-style element whose contents SvelTeX recognises by name and hands off to a dedicated handler.
The canonical use is tex-type environments that compile a LaTeX snippet to an SVG at build time (e.g. for TikZ diagrams), but verbatim covers four processing modes in total.
The four processing types
type | Behaviour |
|---|---|
'tex' | Compile the contents as a LaTeX document, convert the output to SVG, and import the SVG as a Svelte component. See the TeX page for the full pipeline. |
'code' | Run the contents through the configured code backend (Shiki, starry-night, highlight.js, or escape-only) and wrap the result. |
'escape' | HTML- and brace-escape the contents (so curly braces don't get interpreted as Svelte mustache tags) and emit them verbatim. |
'noop' | Don't process the body at all — no Markdown, math, code, or TeX handling. SvelTeX is effectively switched off for the region; the body passes through to Svelte unchanged. |
Configuration
Verbatim environments are registered under the top-level verbatim key of your SvelTeX configuration. Each entry's key is the tag name SvelTeX will recognise; the value is the environment's configuration, with type selecting the processing mode.
// sveltex.config.js
import { sveltex } from '@nvl/sveltex';
export default await sveltex({}, {
verbatim: {
Tex: {
type: 'tex',
aliases: ['tex', 'tikz'],
documentClass: { name: 'standalone', options: [] },
preamble: '\\usepackage{tikz, microtype}',
preset: [{ name: 'tikz', libraries: { babel: true, calc: true } }],
},
Code: {
type: 'code',
},
Verbatim: {
type: 'escape',
escape: { braces: true, html: true },
},
Raw: {
type: 'noop',
},
},
});In your .sveltex files, every registered tag and alias is then valid markup:
<Tex ref="dependency-graph">
\begin{tikzpicture}
\node (a) {A};
\node[right=of a] (b) {B};
\draw[->] (a) -- (b);
\end{tikzpicture}
</Tex>
<Code lang="ts">
const x: number = 42;
</Code>
<Verbatim>
Curly braces { won't } be parsed by Svelte here.
</Verbatim>Common options
These apply to every verbatim type:
aliases— extra tag names that resolve to this environment. The primary tag (the object key) and every alias are interchangeable in markup.defaultAttributes— attributes merged into every instance of the tag before the handler runs.attributeForwardingAllowlist/attributeForwardingBlocklist— decide which of the tag's attributes flow through to the rendered output. The allowlist defaults to'all'; the blocklist applies on top of it.respectSelfClosing— whentrue(the default forcode,escapeandnooptypes),<Tag />is treated as self-closing; whenfalse(the default fortex, since a TeX snippet always needs a body), SvelTeX expects an opening + closing pair.selfCloseOutputWith—'auto'|' />'|'/>': controls whether the rendered output emits a self-closing tag (/>or/>) or omits the slash;'auto'mirrors whatever the source used.component—'this'|'none'|string: pick the Svelte component the rendered output is mounted as.'this'keeps the original tag;'none'strips the wrapper entirely; a string names a specific component to swap in.transformers— apreandpostarray of regex/string-replace pairs or callback functions, run before and after the handler.
Editor syntax highlighting
SvelTeX processes verbatim environments at build time — the body is turned into HTML and handed to Svelte's compiler regardless of editor support. The SvelTeX editor extensions additionally try to colour the body of a verbatim block as you type, and how well they manage depends on the editor.
VS Code
The VS Code extension regenerates its TextMate grammar from your sveltex.config.js, so each configured environment's body is highlighted according to its type:
'tex'→ highlighted as LaTeX;'escape'/'code'→ highlighted as a plain fenced-code block;'noop'→ treated as Svelte markup.
Because the grammar is rebuilt from your config, custom tag names work — a <Diagram> of type: 'tex' gets LaTeX highlighting just like <tex>.
The grammar is regenerated when the extension loads — not live as you edit the config. After you add, remove, or rename a verbatim environment in sveltex.config.js, run Developer: Reload Window (or restart VS Code) so the regenerated grammar takes effect; until then the editor keeps highlighting the previous set of tags.
Zed
Zed compiles the tree-sitter grammar from a pinned commit and cannot read your config, so only a fixed set of hard-coded tag names is recognised:
- LaTeX body highlighting applies to environments named
tex,latexortikz, plus their proper-form (TeX,LaTeX,TikZ) and title-case (Tex,Latex,Tikz) variants. Matching is case-sensitive — any other casing (TEX,LATEX, …) is not recognised. verb/verbatim(andVerb/Verbatim) bodies are rendered as plain text.
Semantic highlighting
In Zed, semantic highlighting is what lets the language server colour escape- and code-type verbatim bodies — the static grammar can't, since it can't read your config. Zed has it off by default, so turn it on: set semantic_tokens to "combined" (LSP tokens layered on tree-sitter), either globally or under "languages": { "SvelTeX": { … } }. In VS Code the regenerated TextMate grammar already covers those bodies and the extension emits no SvelTeX semantic tokens, so the editor's editor.semanticHighlighting.enabled (default configuredByTheme) doesn't change .sveltex highlighting either way.
The editor highlighting (in both extensions) intentionally diverges from CommonMark/GFM in the same ways SvelTeX's own Markdown parsing does — see Markdown implementation. For SvelTeX this is a feature: what you see highlighted reflects how SvelTeX will actually parse the source. If you're used to mdsvex or a stock CommonMark/GFM renderer, though, the highlighting may not match how those parse the same input — don't read it as a preview of their output.
For code blocks specifically, fenced backticks — ```ts … ``` — give you language-aware editor highlighting as you type, on top of the same build-time syntax highlighting a <Code>…</Code> environment would produce. If you want every code block wrapped in a custom Svelte component, do it after the fact with code.transformers.post — the post-transformer can replace the rendered <pre> with <MyCode> (or whatever) without forcing you off the standard fenced syntax.
Examples
A TikZ environment
// sveltex.config.js
import { sveltex } from '@nvl/sveltex';
export default await sveltex({}, {
verbatim: {
Tex: {
type: 'tex',
aliases: ['tex', 'tikz'],
documentClass: 'standalone',
preamble: '\\usepackage{tikz, microtype}',
preset: [{
name: 'tikz',
libraries: { babel: true, calc: true, arrows: { meta: true } },
}],
},
},
});<tikz ref="hello" caption="A first TikZ diagram.">
\begin{tikzpicture}
\draw[thick, ->] (0, 0) -- (2, 1) node[right] {hi};
\end{tikzpicture}
</tikz>See the TeX page for the full set of TeX-specific options (compilation, conversion, optimization, overrides, handleAttributes, postprocess, …).
A code environment
// sveltex.config.js
import { sveltex } from '@nvl/sveltex';
export default await sveltex({ codeBackend: 'shiki' }, {
verbatim: {
Code: {
type: 'code',
defaultAttributes: { lang: 'ts' },
attributeForwardingBlocklist: ['lang', 'inline', 'metaString'],
},
},
});<Code>
const greet = (name: string): string => `Hello, ${name}!`;
</Code>
<Code lang="diff">
- a
+ b
</Code>An escape-only environment
// sveltex.config.js
import { sveltex } from '@nvl/sveltex';
export default await sveltex({}, {
verbatim: {
Verbatim: {
type: 'escape',
escape: { braces: true, html: true },
},
},
});<Verbatim>
<script> tags, { braces }, and <em>html</em> all kept literal here.
</Verbatim>A pass-through environment
// sveltex.config.js
import { sveltex } from '@nvl/sveltex';
export default await sveltex({}, {
verbatim: {
Raw: {
type: 'noop',
},
},
});The point of noop is to switch SvelTeX off for the region: the Raw tag is recognised so the region is carved out, but then no processing runs on its body — no Markdown, no math, no code or TeX handling. The body is passed through to the Svelte compiler exactly as written.