Troubleshooting
This page lists common TikZJax problems, their likely causes, and the recommended diagnostic steps.
When investigating a diagram, start with this local debugging configuration:
<script
type="text/tikz"
data-disable-cache="true"
data-show-console="true"
data-debug-timings="true"
data-render-timeout="45000"
>
\begin{tikzpicture}
\draw (0,0) circle (1);
\end{tikzpicture}
</script>
This configuration:
- bypasses the persistent SVG cache;
- shows TeX console output;
- shows rendering timings;
- gives the diagram a longer timeout.
Remove the debugging attributes after the problem is solved.
Quick diagnostic checklist
Before investigating a package-specific problem, verify:
tikzjax.config.jsis loaded beforetikzjax.min.js.fonts.min.cssloads successfully.tikzjax.min.jsloads successfully.run-tex.jsis reachable.tex.wasm.gzis reachable.core.dump.gzis reachable.tex_files/is reachable.- All runtime files come from the same TikZJax release.
- The browser Console contains no JavaScript or TeX error.
- The Network panel contains no
404, CORS, or CSP failure. - Cache is disabled while reproducing the problem.
- The required package or TikZ library is declared in the correct category.
- The diagram is complete and syntactically valid.
- A fenced block is not relying on undeclared local dependencies.
- The active Light or Dark theme is detected correctly.
- Adaptive-color and contrast options have not been disabled unintentionally.
- The issue still occurs with one worker.
Recommended loading order
The configuration file is optional, but when present it must be loaded before TikZJax.
<script src="tikzjax.config.js"></script>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@rod2ik/tikzjax@1.5.0/dist/fonts.min.css"
>
<script src="https://cdn.jsdelivr.net/npm/@rod2ik/tikzjax@1.5.0/dist/tikzjax.min.js"></script>
For debugging, use the non-minified assets:
<script src="tikzjax.config.js"></script>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@rod2ik/tikzjax@1.5.0/dist/fonts.css"
>
<script src="https://cdn.jsdelivr.net/npm/@rod2ik/tikzjax@1.5.0/dist/tikzjax.js"></script>
Nothing is displayed
Symptoms
- the original source disappears;
- no loader is shown;
- no SVG appears;
- no fallback image appears;
- TikZJax seems inactive.
Checks
Open the browser developer tools.
Console
Look for:
- JavaScript syntax errors;
- an exception while loading TikZJax;
- an invalid configuration object;
- a CSP error;
- a failed worker constructor;
- an IndexedDB error.
Network
Verify that these resources return successful responses:
tikzjax.min.js
fonts.min.css
run-tex.js
tex.wasm.gz
core.dump.gz
Also verify that requests under:
tex_files/
are not blocked.
Source format
Confirm that the source is one of the recognized forms:
<script type="text/tikz">
\begin{tikzpicture}
\draw (0,0) circle (1);
\end{tikzpicture}
</script>
or a recognized fenced block:
```tikzjax
\begin{tikzpicture}
\draw (0,0) circle (1);
\end{tikzpicture}
```
The loading indicator never disappears
Possible causes include:
- worker initialization never completed;
- a runtime request is stalled;
- TeX entered a long-running state;
- the timeout is too high or disabled incorrectly;
- worker messages are blocked by a JavaScript error;
- the page was replaced during rendering.
Use a finite timeout:
window.TikzJaxOptions = {
renderTimeout: 30000
};
Test the affected block with:
<script
type="text/tikz"
data-disable-cache="true"
data-show-console="true"
data-render-timeout="30000"
>
...
</script>
Check the Network panel for requests that remain pending.
The fallback error image is displayed
This means that TikZJax detected the source but could not produce a valid SVG after the permitted attempts.
Common causes include:
- invalid TeX syntax;
- undefined commands;
- incomplete environments;
- missing packages;
- missing TikZ libraries;
- missing runtime
.gzfiles; - a render timeout;
- worker failure;
- blocked WebAssembly or worker assets;
- a DVI conversion failure.
Enable TeX console output:
<script
type="text/tikz"
data-disable-cache="true"
data-show-console="true"
>
\begin{tikzpicture}
\ThisCommandDoesNotExist
\end{tikzpicture}
</script>
A complete but invalid document is usually better for testing fallback behavior than an unclosed TeX environment.
An unclosed environment may cause TeX to wait for more input until the render timeout expires.
Configuration appears to be ignored
Initial configuration loaded too late
Incorrect:
<script src="tikzjax.min.js"></script>
<script src="tikzjax.config.js"></script>
Correct:
<script src="tikzjax.config.js"></script>
<script src="tikzjax.min.js"></script>
Several assignments before TikZJax loads
Before TikZJax installs its configuration API, normal JavaScript assignment rules apply.
This replaces the first object:
window.TikzJaxOptions = {
renderTimeout: 30000
};
window.TikzJaxOptions = {
brokenImageSrc: "/images/error.svg"
};
Use one complete initial object:
window.TikzJaxOptions = {
renderTimeout: 30000,
brokenImageSrc: "/images/error.svg"
};
After TikZJax has loaded, use:
window.TikzJaxConfigure({
brokenImageSrc: "/images/error.svg"
});
Local value overrides global value
A local attribute has higher priority:
<script
type="text/tikz"
data-render-timeout="45000"
>
...
</script>
This diagram does not use the global timeout.
See Global and Local Configuration.
Inspecting the active configuration
Global configuration:
window.TikzJaxOptions
Worker-pool configuration:
window.TikzJaxOptions?.workerPool
Global TeX packages:
window.TikzJaxOptions?.tex?.texPackages
Global TikZ libraries:
window.TikzJaxOptions?.tex?.tikzLibraries
Theme configuration:
window.TikzJaxOptions?.theme
Adaptive colors:
window.TikzJaxOptions?.theme?.adaptiveColors
Contrast configuration:
window.TikzJaxOptions?.theme?.adaptiveColors?.contrast
Explicitly configured very-light fill options:
window.TikzJaxOptions?.theme?.adaptiveFills
adaptiveFills may be undefined here when no override was supplied, even though the runtime still uses its built-in fallback values.
Apply and inspect a partial update:
const options = window.TikzJaxConfigure({
renderTimeout: 45000
});
console.log(options);
Local effective configuration is created internally for each diagram and does not mutate these global objects.
Invalid local JSON
Attributes such as:
data-tikzjax-options
data-options
data-tex
data-tex-packages
data-tkz-tab
may contain JSON.
Incorrect:
data-tikzjax-options='{
renderTimeout: 30000,
}'
Problems:
- the property name is not quoted;
- the final property has a trailing comma.
Correct:
data-tikzjax-options='{
"renderTimeout": 30000
}'
Use single quotes around the HTML attribute and double quotes inside the JSON.
Dedicated attributes override local JSON
This source defines the timeout twice:
<script
type="text/tikz"
data-tikzjax-options='{
"renderTimeout": 30000
}'
data-render-timeout="45000"
>
...
</script>
The dedicated attribute wins:
45000 milliseconds
Avoid defining the same option in two places unless the override is intentional.
A TikZ library is missing
Symptoms may include:
I do not know the key ...
or:
Undefined control sequence
Load a library locally:
<script
type="text/tikz"
data-tikz-libraries="decorations.pathreplacing"
>
\begin{tikzpicture}
\draw[
decorate,
decoration={
brace,
amplitude=6pt
}
]
(0,0) -- (4,0);
\end{tikzpicture}
</script>
Or globally when nearly every diagram needs it:
window.TikzJaxOptions = {
tex: {
tikzLibraries: [
"decorations.pathreplacing"
]
}
};
A LaTeX package is missing
Load one package locally:
<script
type="text/tikz"
data-tex-packages="physics"
>
\begin{tikzpicture}
\node {$\vb{F}=m\vb{a}$};
\end{tikzpicture}
</script>
For several packages or package options, use JSON:
<script
type="text/tikz"
data-tex-packages='{
"physics": "",
"xcolor": "dvipsnames"
}'
>
...
</script>
Global package configuration:
window.TikzJaxOptions = {
tex: {
texPackages: {
physics: "",
xcolor: "dvipsnames"
}
}
};
Use an empty string when a package has no options.
Package or TikZ library?
A LaTeX package is normally loaded with:
\usepackage{package-name}
Declare it with:
data-tex-packages="package-name"
A TikZ library is normally loaded with:
\usetikzlibrary{library-name}
Declare it with:
data-tikz-libraries="library-name"
For example, braids is a TikZ library:
data-tikz-libraries="braids"
It is not:
data-tex-packages="braids"
A runtime package file returns 404
The browser Console or Network panel may show:
GET .../tex_files/chemfig.sty.gz 404
or another missing file ending in:
.sty.gz
.tex.gz
.def.gz
.code.tex.gz
This means the worker requested a runtime dependency that is not available at the resolved path.
Check:
- the file exists under the deployed
tex_files/directory; - the filename matches the TeX request exactly;
- the JavaScript bundle and
tex_files/directory use the same release; - the CDN package version is correct;
- the server preserves filename case;
- the request is not rewritten to an HTML fallback page;
- the file is served with a successful HTTP status.
Use the exact failed URL from the Network panel. Do not guess the missing filename.
A custom command is undefined
Global macro:
window.TikzJaxOptions = {
tex: {
addToPreamble: String.raw`
\newcommand{\R}{\mathbb{R}}
`
}
};
Local macro:
<script
type="text/tikz"
data-add-to-preamble="\newcommand{\localR}{\mathbb{R}}"
>
\begin{tikzpicture}
\node {$f:\localR\to\localR$};
\end{tikzpicture}
</script>
Local preamble replacement
data-add-to-preamble is a scalar override.
A local value replaces the global custom tex.addToPreamble value for that diagram.
Suppose the global preamble defines:
\newcommand{\R}{\mathbb{R}}
This local source:
<script
type="text/tikz"
data-add-to-preamble="\newcommand{\N}{\mathbb{N}}"
>
...
</script>
does not automatically retain the global \R definition.
Include all required definitions in the local value:
<script
type="text/tikz"
data-add-to-preamble="\newcommand{\R}{\mathbb{R}}\newcommand{\N}{\mathbb{N}}"
>
...
</script>
tkz-tab configuration appears to be ignored
First verify that the LaTeX package is loaded. The tkzTab configuration does not load tkz-tab by itself.
Global loading:
window.TikzJaxOptions = {
tex: {
texPackages: {
"tkz-tab": ""
}
}
};
Local loading:
<script
type="text/tikz"
data-tex-packages="tkz-tab"
>
...
</script>
Check automatic application
Automatic native defaults require:
window.TikzJaxOptions = {
tkzTab: {
autoApply: true
}
};
When autoApply is enabled, supported values such as lineWidth, font, lgt, firstColumnWidth, espcl, init, setup, and colors are applied without requiring the source to reference the public helper macros.
Inspect the active global object:
window.TikzJaxOptions?.tkzTab
For a local override, verify that data-tkz-tab contains valid JSON:
<script
type="text/tikz"
data-tex-packages="tkz-tab"
data-tkz-tab='{
"autoApply": true,
"lineWidth": "1.4pt",
"firstColumnWidth": 5,
"espcl": 3,
"init": {
"deltacl": 0.8
}
}'
>
...
</script>
Check option priority
Explicit TeX options have higher priority than configured defaults.
For example:
\tkzTabInit[
lw=0.8pt,
lgt=4
]
overrides the configured native lw and lgt defaults for that table.
Within the merged tkzTab object, values in init override the corresponding values automatically derived from lineWidth, lgt, firstColumnWidth, or espcl.
Check row heights
Row-height values are not applied automatically. They remain explicit helper macros because TikZJax cannot infer the semantic type of each required label/height row:
\tikzjaxTkzTabVariableRowHeight
\tikzjaxTkzTabSignRowHeight
\tikzjaxTkzTabVariationRowHeight
\tikzjaxTkzTabImageRowHeight
\tikzjaxTkzTabAntecedentRowHeight
Force a fresh render
A cached SVG can hide a changed tkzTab configuration.
Use:
<script
type="text/tikz"
data-tex-packages="tkz-tab"
data-disable-cache="true"
data-show-console="true"
>
...
</script>
or clear IndexedDB:
indexedDB.deleteDatabase("TikzJax");
location.reload();
See the tkz-tab examples and the API Reference.
A fenced block works as HTML but not as Markdown
Fenced tikzjax blocks cannot carry local HTML attributes.
This fenced block:
```tikzjax
\chemfig{H_3C-CH_2-OH}
```
works only when chemfig is loaded globally.
For local loading, use an HTML block:
<script
type="text/tikz"
data-tex-packages="chemfig"
>
\chemfig{H_3C-CH_2-OH}
</script>
Use fenced blocks only when all required dependencies are already present in the global configuration.
MkDocs fenced blocks are not detected
Configure pymdownx.superfences:
markdown_extensions:
- pymdownx.superfences:
custom_fences:
- name: tikzjax
class: language-tikzjax
format: !!python/name:pymdownx.superfences.fence_code_format
Use:
```tikzjax
\begin{tikzpicture}
\draw (0,0) circle (1);
\end{tikzpicture}
```
The generated <pre> element should contain one of these classes:
language-tikzjax
tikzjax
language-tikz
tikz
Inspect the generated HTML in DevTools when unsure.
Blocks inside tabs or admonitions are not rendered
Check Markdown indentation carefully.
Admonition:
!!! success "Example"
```tikzjax
\begin{tikzpicture}
\draw (0,0) circle (1);
\end{tikzpicture}
```
Content tab:
=== "Solution"
```tikzjax
\begin{tikzpicture}
\draw (0,0) circle (1);
\end{tikzpicture}
```
If the source is present in the final DOM but remains unprocessed:
- check for a JavaScript error that stopped DOM observation;
- check whether the source class is recognized;
- activate the tab and inspect the element;
- test the same source as an HTML
<script>block; - verify that client-side navigation did not load a second incompatible TikZJax bundle.
Dynamic sources use the same central queue and worker pool as initial sources.
Dynamically inserted content is not rendered
TikZJax detects newly inserted sources through a central DOM observer.
Verify that the inserted element:
- is connected to the active document;
- uses
type="text/tikz"or a recognized<pre>class; - contains non-empty source text;
- is not immediately removed or replaced;
- is not inside an iframe with a separate JavaScript context;
- is inserted after TikZJax has loaded.
Example:
const source = document.createElement("script");
source.type = "text/tikz";
source.textContent = String.raw`
\begin{tikzpicture}
\draw (0,0) circle (1);
\end{tikzpicture}
`;
document.body.appendChild(source);
No manual render call should be required for normal document insertion.
A diagram does not change after editing
TikZJax may be inserting a cached SVG.
Temporarily use:
data-disable-cache="true"
Complete example:
<script
type="text/tikz"
data-disable-cache="true"
>
\begin{tikzpicture}
\draw (0,0) -- (2,2);
\end{tikzpicture}
</script>
Or clear the complete cache:
indexedDB.deleteDatabase("TikzJax");
location.reload();
Also check the Network panel to confirm that the browser loaded the expected version of tikzjax.min.js.
A cached diagram hides a current runtime failure
A previously stored SVG can continue to appear even if a package file is now missing.
Force a fresh render:
<script
type="text/tikz"
data-disable-cache="true"
data-show-console="true"
>
...
</script>
This is important when testing:
- a new npm release;
- changed
tex_files/; - CDN propagation;
- a new package catalogue;
- worker changes.
A repeated diagram renders more than once
Pending-job grouping and persistent cache reuse require the exact same rendering identity.
Check for differences in:
- whitespace;
- comments;
- local package declarations;
- TikZ libraries;
- local preamble;
data-tkz-tab;- JSON formatting and values;
- loader dimensions;
- cache behavior.
These sources are different:
\draw (0,0) circle (1);
\draw
(0,0)
circle (1);
Even when their visual output is identical.
Rendering is slow
Check global dependencies
Avoid this unless every diagram needs the packages:
window.TikzJaxOptions = {
tex: {
texPackages: {
physics: "",
chemfig: "",
circuitikz: "",
yquant: ""
}
}
};
Prefer local loading:
<script
type="text/tikz"
data-tex-packages="chemfig"
>
\chemfig{H_3C-CH_2-OH}
</script>
Check cache bypass
Search for diagrams that still contain:
data-disable-cache="true"
Check worker restarts
Repeated worker failures destroy worker-local package caches and runtime state.
Look for repeated initialization or restart messages.
Check worker count
Too many workers can increase memory pressure and CPU contention.
Recommended baseline:
window.TikzJaxOptions = {
workerPool: {
enabled: true,
maxWorkers: 3,
reserveCpuCores: 1,
useDeviceMemory: true
}
};
Measure a fresh render
<script
type="text/tikz"
data-disable-cache="true"
data-debug-timings="true"
>
...
</script>
The first package-heavy diagram is slow
The assigned worker may need to:
- download package files;
- download dependencies;
- decompress runtime files;
- insert them into its virtual filesystem;
- process a larger TeX preamble.
Later diagrams using the same package on the same worker may be faster.
Another worker may still experience its own first-use cost because worker dependency caches are isolated.
This is expected behavior.
The same package is prepared by several workers
Workers do not directly share their decompressed TeX files or virtual filesystems.
Several workers can therefore prepare the same package independently.
The browser HTTP cache may reduce the network transfer, but the worker-local decompression and setup still occur.
This is not a package-loading loop unless the same worker repeatedly requests the file for every job.
Increasing maxWorkers made rendering slower
Possible causes:
- CPU contention;
- memory pressure;
- duplicated TeX runtimes;
- simultaneous decompression;
- duplicated worker-local package caches;
- thermal throttling;
- browser scheduling overhead.
Return to a modest configuration:
window.TikzJaxOptions = {
workerPool: {
enabled: true,
maxWorkers: 3,
reserveCpuCores: 1,
useDeviceMemory: true
}
};
Then compare with a one-worker setup.
Testing with one worker
Use:
window.TikzJaxOptions = {
workerPool: {
enabled: true,
maxWorkers: 1,
reserveCpuCores: 0,
useDeviceMemory: false
}
};
This helps distinguish:
- a general TeX error;
- a package error;
- a cache problem;
- a concurrency-sensitive issue;
- application code that assumes source-order completion.
For normal production use, restore the adaptive pool.
Only some diagrams render
Possible causes:
- one or more workers failed to initialize;
- some diagrams require a missing package;
- only uncached diagrams expose the failure;
- one worker repeatedly fails on a specific dependency;
- a local timeout is too short;
- source blocks were removed during navigation;
- invalid JSON affects only selected blocks.
Test the failing diagram with:
<script
type="text/tikz"
data-disable-cache="true"
data-show-console="true"
data-render-timeout="45000"
>
...
</script>
Then test the page with one worker.
Diagrams finish in an unexpected order
Parallel rendering does not guarantee document-order completion.
For example:
source order:
A, B, C
completion order:
B, C, A
Each SVG is still inserted in the correct document position.
Use the completion event rather than relying on source order:
document.addEventListener(
"tikzjax-load-finished",
function (event) {
console.log(
"Diagram ready:",
event.target
);
}
);
A completion listener does not run
Listen on document, because the event bubbles from the generated SVG:
document.addEventListener(
"tikzjax-load-finished",
function (event) {
const svg = event.target;
console.log(svg);
}
);
Register the listener before the diagram is likely to finish.
The event is emitted for both freshly rendered and cached SVG insertion.
Do not assume events arrive in document order.
A visible diagram renders after a distant diagram
Viewport priority applies to pending work.
A job that is already compiling is not normally interrupted when another source becomes visible.
Other factors can include:
- the distant diagram was cached;
- the distant job had already started;
- the visible diagram needed package initialization;
- an explicit
data-render-priorityvalue changed the order; - the visible source was still hidden or disconnected when priority was calculated.
Remove explicit priority overrides unless they are required.
Worker initialization fails
Possible symptoms include:
- no diagrams render;
- the fallback image appears immediately;
- repeated worker-start messages;
- an error mentioning WebAssembly or worker initialization;
run-tex.jsloads but the worker never becomes ready.
Check:
run-tex.js
tex.wasm.gz
core.dump.gz
Also check:
assetBaseUrl;workerUrl;workerMode;- CORS;
- CSP;
- response MIME types;
- compressed-file integrity;
- release consistency.
Recommended initialization retry:
window.TikzJaxOptions = {
workerPool: {
initializationRetries: 1
}
};
Initialization retries are separate from diagram-render retries.
A single worker repeatedly fails
A worker can become unusable after:
- a fatal WebAssembly error;
- a timeout;
- corrupted runtime state;
- an unexpected worker exception.
Keep restart enabled:
window.TikzJaxOptions = {
restartWorkerOnFail: true
};
TikZJax can terminate the affected worker and initialize a replacement.
Other workers can continue their current jobs.
Frequent restarts indicate an underlying problem and should not be treated as normal operation.
A timeout occurs
Global timeout:
window.TikzJaxOptions = {
renderTimeout: 30000
};
Local timeout:
<script
type="text/tikz"
data-render-timeout="45000"
>
...
</script>
A timeout is a safety limit, not a performance target.
Increasing it does not make the diagram faster.
Before increasing it, check:
- TeX console output;
- missing-file requests;
- unclosed environments;
- package initialization;
- repeated recursion or loops;
- excessive drawing complexity.
A retry does not fix the diagram
Retries are intended for transient worker or runtime failures.
They do not fix:
- invalid TeX syntax;
- an undefined command;
- a missing package;
- a missing runtime file;
- an unsupported package feature.
With:
window.TikzJaxOptions = {
maxRetries: 1
};
TikZJax performs one initial attempt and at most one retry.
Do not use a large retry value to hide deterministic failures.
Worker files are not loaded
Required runtime files include:
run-tex.js
tex.wasm.gz
core.dump.gz
tex_files/
assets/broken-image.svg
When using jsDelivr or unpkg, TikZJax normally resolves them relative to the loaded bundle.
Explicit jsDelivr configuration:
window.TikzJaxOptions = {
assetBaseUrl:
"https://cdn.jsdelivr.net/npm/@rod2ik/tikzjax@1.5.0/dist"
};
Same-origin configuration:
window.TikzJaxOptions = {
assetBaseUrl: "/vendor/tikzjax",
workerMode: "direct"
};
Expected local files:
/vendor/tikzjax/tikzjax.min.js
/vendor/tikzjax/run-tex.js
/vendor/tikzjax/fonts.min.css
/vendor/tikzjax/tex.wasm.gz
/vendor/tikzjax/core.dump.gz
/vendor/tikzjax/tex_files/
/vendor/tikzjax/assets/broken-image.svg
Worker mode problems
Automatic mode
window.TikzJaxOptions = {
workerMode: "auto"
};
TikZJax uses:
- direct worker startup for same-origin URLs;
- Blob worker startup for cross-origin URLs.
Direct mode
window.TikzJaxOptions = {
assetBaseUrl: "/vendor/tikzjax",
workerMode: "direct"
};
Use this for same-origin hosting and CSP policies that do not allow Blob workers.
Blob mode
window.TikzJaxOptions = {
workerMode: "blob"
};
Use this only when the CSP allows Blob workers and the browser can fetch the worker source.
Root workerMode and workerUrl take precedence over nested worker.mode and worker.url.
CSP blocks TikZJax
Browser errors may mention:
Refused to create a worker
Refused to connect
Refused to compile or instantiate WebAssembly
CDN with Blob workers
A typical policy includes:
script-src 'self' https://cdn.jsdelivr.net https://unpkg.com 'wasm-unsafe-eval';
style-src 'self' https://cdn.jsdelivr.net https://unpkg.com 'unsafe-inline';
worker-src 'self' blob:;
connect-src 'self' https://cdn.jsdelivr.net https://unpkg.com;
img-src 'self' https://cdn.jsdelivr.net https://unpkg.com data: blob:;
font-src 'self' https://cdn.jsdelivr.net https://unpkg.com;
Same-origin direct workers
script-src 'self' 'wasm-unsafe-eval';
style-src 'self' 'unsafe-inline';
worker-src 'self';
connect-src 'self';
img-src 'self' data:;
font-src 'self';
Adapt these examples to the complete policy of the site.
CORS blocks a CDN resource
Check the failed request in the Network panel.
Common causes include:
- a worker started in direct mode from another origin;
- the worker source cannot be fetched for Blob mode;
- redirects remove expected CORS headers;
- a proxy modifies the response;
- a local development server blocks cross-origin requests.
For cross-origin CDN usage, keep:
window.TikzJaxOptions = {
workerMode: "auto"
};
For strict same-origin hosting, copy all TikZJax runtime files locally and use direct mode.
A response returns HTML instead of a runtime file
A missing asset may be rewritten by the server to an application HTML page.
For example, a request for:
/vendor/tikzjax/tex.wasm.gz
may return the site's index.html with status 200.
The request looks successful but the worker cannot parse the file.
Inspect:
- the response content type;
- the response body;
- rewrite rules;
- static-file exclusions;
- SPA fallback behavior.
Runtime asset paths must bypass HTML fallback rewrites.
WebAssembly initialization fails
Check:
tex.wasm.gzreturns the expected binary data;- the file is not an HTML error page;
- the gzip data is not corrupted;
- CSP allows WebAssembly execution;
- the bundle and runtime files use the same release;
- a proxy is not recompressing the
.gzfile incorrectly.
Test in a clean browser profile when cached network data may be stale.
Dark mode looks incorrect
TikZJax applies several independent Dark-mode stages:
- ordinary black and white output follows the active page foreground and background conventions;
- very light non-text fills may be converted by
theme.adaptiveFills; - explicit chromatic
fill,stroke, and text colors are adapted bytheme.adaptiveColors, which is enabled by default; - a detected filled background may be darkened when foreground/background contrast is below the configured minimum ratio.
Light mode restores the stored original chromatic SVG values after the normal black/white processing.
Check the theme configuration:
window.TikzJaxOptions = {
theme: {
selector: "body",
attribute: "data-md-color-scheme",
darkValue: "slate",
lightValue: "default",
fallbackTheme: "light"
}
};
Also check:
- the selector matches the element containing the theme state;
- the attribute changes when the site theme changes;
- dark and light values match the actual HTML;
- custom SVG or CSS rules are not overriding TikZJax styles;
theme.adaptiveColorswas not disabled;- the generated SVG uses ordinary parseable colors rather than gradients, patterns, CSS variables, or unsupported paint servers.
Inspect the active options:
window.TikzJaxOptions?.theme
window.TikzJaxOptions?.theme?.adaptiveColors
See Themes.
Explicit colors change unexpectedly
Explicit chromatic colors are adapted automatically in Dark mode by default.
For example, a dark blue may become a brighter sky-blue-family color, while the exact generated blue is restored in Light mode.
To disable both explicit-color adaptation and its nested contrast stage:
window.TikzJaxOptions = {
theme: {
adaptiveColors: false
}
};
To keep explicit chromatic colors unchanged while retaining foreground/background contrast correction:
window.TikzJaxOptions = {
theme: {
adaptiveColors: {
strength: 0,
contrast: {
enabled: true
}
}
}
};
adaptiveColors.strength and adaptiveColors.contrast.strength are independent.
Pure black and pure white use the separate black/white normalization stage rather than adaptiveColors.
Inspect the generated SVG to determine whether the affected value is a fill, stroke, text color, CSS value, gradient, or pattern.
Foreground/background contrast is not corrected
The contrast stage is enabled by default inside theme.adaptiveColors.
It supports three common Dark-mode arrangements:
- SVG
<text>painted over a filled shape; - a bright neutral
strokeon the same element as its backgroundfill; - a small bright-neutral vector detail painted over a larger filled shape.
Confirm the active configuration:
window.TikzJaxOptions = {
theme: {
adaptiveColors: {
enabled: true,
contrast: {
enabled: true,
minimumRatio: 4.5,
strength: 1,
minimumBackgroundLightness: 0.04,
containmentTolerance: 1
}
}
}
};
No additional configuration parameter is required for outline or vector-detail detection.
Inspect the generated SVG. A background changed by the contrast stage receives:
data-tikzjax-contrast-adjusted="true"
If the marker is absent, verify:
- the active theme is actually Dark;
- the foreground and background use parseable SVG colors;
- the background is a common filled shape such as
path,rect,circle,ellipse,polygon, orpolyline; - a separate foreground is painted after the intended background;
- a separate text or vector detail is geometrically associated with the intended background;
- the foreground is not hidden inside a gradient, pattern, filter, clipping path, or complex mask;
- a vector foreground is bright and approximately neutral.
Chromatic vector accents, such as a red target point, deliberately do not trigger the bright-neutral outline or vector-detail heuristic.
For a same-element fill/stroke pair, containmentTolerance has no effect because no geometric association is required.
For small geometric mismatches involving separate text or vector details, try:
window.TikzJaxOptions = {
theme: {
adaptiveColors: {
contrast: {
containmentTolerance: 2
}
}
}
};
If the marker is present but the background remains too light, increase minimumRatio, keep contrast.strength near 1, or lower minimumBackgroundLightness.
A contrast-corrected background is too dark
Reduce the requested ratio:
window.TikzJaxOptions = {
theme: {
adaptiveColors: {
contrast: {
minimumRatio: 3.5
}
}
}
};
Or apply only part of the calculated darkening:
window.TikzJaxOptions = {
theme: {
adaptiveColors: {
contrast: {
strength: 0.7
}
}
}
};
To disable only foreground/background contrast correction while keeping explicit-color adaptation:
window.TikzJaxOptions = {
theme: {
adaptiveColors: {
contrast: {
enabled: false
}
}
}
};
A white or very light fill remains too bright
Very light non-text fills use the separate theme.adaptiveFills stage.
Confirm it is enabled:
window.TikzJaxOptions = {
theme: {
adaptiveFills: {
enabled: true,
lightnessThreshold: 0.82,
darkLightness: 0.23
}
}
};
The stage may not modify gradients, patterns, CSS variables, transparent paints, or unsupported color representations.
Lower lightnessThreshold when a fill is visually light but remains below the default threshold:
window.TikzJaxOptions = {
theme: {
adaptiveFills: {
lightnessThreshold: 0.75
}
}
};
adaptiveFills is independent of adaptiveColors. Disabling adaptiveColors does not disable very-light fill adaptation.
The fallback image path is wrong
Global fallback:
window.TikzJaxOptions = {
brokenImageSrc: "/assets/images/tikz-error.svg"
};
Local fallback:
<script
type="text/tikz"
data-broken-image-src="/assets/images/local-error.svg"
>
...
</script>
For MkDocs, a relative URL is resolved from the generated page location, not necessarily from the Markdown source file.
Prefer:
- a root-relative path;
- a URL produced by the MkDocs template system;
- a versioned CDN URL.
Inspect the final image request in the Network panel.
The local fallback image is ignored
The attribute must be directly on the source element:
<script
type="text/tikz"
data-broken-image-src="/assets/images/local-error.svg"
>
\begin{tikzpicture}
\ThisCommandDoesNotExist
\end{tikzpicture}
</script>
This does not apply the local value:
<div data-broken-image-src="/assets/images/local-error.svg">
<script type="text/tikz">
...
</script>
</div>
The fallback image is used only after the diagram fails.
A successfully rendered diagram never displays it.
The default fallback appears instead of the configured image
Verify:
- the configuration file loads before TikZJax;
brokenImageSrcis spelled correctly;- no later partial update replaces it;
- no local
data-broken-image-srcoverrides it; - the selected image itself loads successfully.
Inspect:
window.TikzJaxOptions?.brokenImageSrc
TeX console output does not appear
Use:
data-show-console="true"
Example:
<script
type="text/tikz"
data-disable-cache="true"
data-show-console="true"
>
...
</script>
A cached SVG does not run TeX again, so no new TeX output is produced.
Do not rely on:
data-show-console="false"
to disable console output. Omit the attribute when it is not needed.
Timing output does not appear
Use:
data-debug-timings="true"
and force a fresh render:
<script
type="text/tikz"
data-disable-cache="true"
data-debug-timings="true"
>
...
</script>
A persistent SVG cache hit bypasses worker compilation and therefore does not produce fresh worker timing stages.
A loader is too small
Set placeholder dimensions:
<script
type="text/tikz"
data-width="620"
data-height="300"
>
...
</script>
The values are interpreted as TeX points for the loading placeholder.
They do not resize the final SVG.
Generated SVG is clipped
Wrap the source in:
<div class="tikzjax-container">
<script type="text/tikz">
...
</script>
</div>
For a full-size responsive container:
<div class="tikzjax-scaled-container">
<script type="text/tikz">
...
</script>
</div>
Also check parent elements for CSS such as:
overflow: hidden;
MathJax processes TikZJax output
TikZJax generated wrappers include:
mathjax_ignore
If MathJax still processes generated SVG text:
- verify the wrapper class remains present;
- check whether another script removes or replaces the wrapper;
- inspect MathJax configuration;
- avoid manually moving generated SVG children outside the wrapper;
- ensure MathJax is not configured to ignore the standard exclusion class.
Different results appear after client-side navigation
Possible causes include:
- TikZJax loaded more than once;
- global configuration was reassigned by the new page;
- old and new runtime versions coexist;
- the theme observer refers to a removed element;
- cached SVGs were generated with another configuration;
- page scripts execute in a different order.
Check the Network panel for duplicate TikZJax bundles.
Load TikZJax once in the site template rather than once per page.
Runtime files come from different releases
Mixed versions can produce subtle failures.
For example:
tikzjax.min.js from release A
run-tex.js from release B
tex.wasm.gz from release A
tex_files/ from release C
Possible symptoms include:
- worker initialization failure;
- missing message fields;
- unavailable package files;
- unexpected TeX errors;
- cache incompatibilities;
- incorrect worker-pool behavior.
Pin one exact package version and use it for every runtime URL.
CDN changes are not visible
CDN and browser caches may still contain a previous immutable version.
Verify:
- the URL contains the intended package version;
- npm shows that exact release;
- the requested response has the expected content;
- the browser is not using a service-worker cache;
- IndexedDB is cleared when testing rendering changes.
Do not overwrite an already published immutable version and expect every cache to update immediately.
Diagnostic configurations
Standard diagnostic mode
window.TikzJaxOptions = {
renderTimeout: 45000,
maxRetries: 0,
restartWorkerOnFail: true,
workerPool: {
enabled: true,
maxWorkers: 1,
reserveCpuCores: 0,
useDeviceMemory: false,
initializationRetries: 1
}
};
Use local diagnostics on the affected block:
<script
type="text/tikz"
data-disable-cache="true"
data-show-console="true"
data-debug-timings="true"
>
...
</script>
Restore recommended production mode
window.TikzJaxOptions = {
renderTimeout: 30000,
maxRetries: 1,
restartWorkerOnFail: true,
workerPool: {
enabled: true,
maxWorkers: 3,
reserveCpuCores: 1,
useDeviceMemory: true,
initializationRetries: 1
}
};
Recommended debugging workflow
Step 1: Test basic TikZ
<script
type="text/tikz"
data-disable-cache="true"
data-show-console="true"
>
\begin{tikzpicture}
\draw (0,0) circle (1);
\end{tikzpicture}
</script>
If this fails, the problem is probably runtime-wide.
Step 2: Add the required dependency
<script
type="text/tikz"
data-tex-packages="physics"
data-disable-cache="true"
data-show-console="true"
>
\begin{tikzpicture}
\node {$\vb{F}$};
\end{tikzpicture}
</script>
Step 3: Add options incrementally
Start with the smallest valid example and add:
- packages;
- libraries;
- preamble commands;
- styles;
- complex paths;
one at a time.
Step 4: Inspect failed network requests
Focus on:
run-tex.js
tex.wasm.gz
core.dump.gz
tex_files/*.gz
Step 5: Test one worker
window.TikzJaxOptions = {
workerPool: {
enabled: true,
maxWorkers: 1
}
};
Step 6: Clear IndexedDB
indexedDB.deleteDatabase("TikzJax");
location.reload();
Step 7: Restore production settings
Remove debugging attributes and restore the normal worker-pool size.
Information to collect for a bug report
Include:
- TikZJax version;
- browser name and version;
- operating system;
- deployment method;
- complete minimal source;
- effective global configuration;
- local
data-*attributes; - Console errors;
- failed Network requests;
- whether cache was disabled;
- the active Light or Dark theme value;
- the effective
theme.adaptiveColorsand contrast configuration; - whether the affected SVG contains
data-tikzjax-contrast-adjusted="true"; - whether the issue occurs with one worker;
- whether the issue occurs on a clean browser profile;
- whether the basic circle example works.
Minimal source template:
<script
type="text/tikz"
data-disable-cache="true"
data-show-console="true"
>
\begin{tikzpicture}
% Minimal failing source
\end{tikzpicture}
</script>
Remove unrelated site code before reporting the problem.