Embedding SVG Exports
Every SVG you export from Molkit is a self-contained file: a viewBox in raw canvas coordinates, a root id of the form molkit-xxxxx that is unique per export, an internal style block scoped to that id with :where(#molkit-xxxxx) selectors, and optional data-mk-* chemistry metadata. How you put that file on a page determines what you can do with it afterward. This page covers the three embedding methods, sizing, and the two problems that bite most often: duplicate ids and page-CSS bleed.
If you have not exported anything yet, start with the SVG export reference and the developer overview.
Three ways to embed
<img src="/img/benzene.svg" alt="Benzene ring" width="320">Treat img as a static picture. The browser renders the SVG in an isolated context: scripts never run, your page CSS cannot reach inside, and your JavaScript cannot read the chemistry metadata or drive animations. The dark-mode media query baked into theme-aware exports still applies in most browsers, so you still get automatic light/dark switching. Use img when the drawing is decoration and nothing more.
<object type="image/svg+xml" data="/img/benzene.svg" width="320"> Benzene ring</object>An object loads the SVG as a separate document. SMIL animations play, internal styles work, but your page CSS still cannot cross the boundary, and scripting requires same-origin contentDocument access with load-timing caveats. It is an isolation tool, not an integration tool. Reach for it only when you specifically want the export walled off from the page.
<div class="mk-embed"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="1866.4 1393.9 92.5 100" id="molkit-nknpn" ...> <!-- exported content --> </svg></div>Inlining puts the SVG into your page’s DOM. You can restyle it with CSS theming, query its data-mk-* attributes, attach event listeners, and control animation timelines. Everything else in this guide assumes inline embedding. The wrapper div with a known class is not optional decoration; you will need it for sizing and style scoping below.
Inlining: build time vs runtime
At build time, the simplest approach is to paste the file’s contents into your HTML, or use whatever include mechanism your site generator offers (partials, shortcodes, raw imports). The export is a single file with no external dependencies, so a verbatim include works.
At runtime, fetch and inject:
async function embedMolkitSvg(url, target) { const res = await fetch(url); if (!res.ok) throw new Error(`Failed to load ${url}: ${res.status}`); const text = await res.text(); const doc = new DOMParser().parseFromString(text, 'image/svg+xml'); const svg = doc.documentElement; if (svg.nodeName !== 'svg') throw new Error(`${url} is not an SVG document`); target.replaceChildren(svg); return svg;}
embedMolkitSvg('/img/benzene.svg', document.querySelector('.mk-embed'));One security note: inline SVG runs in your page’s context, including any script elements or event-handler attributes it contains. Only inline SVGs you exported yourself or otherwise trust. If the source is user-supplied, run it through a sanitizer first or fall back to img.
Sizing
A fixed export carries explicit width and height attributes (content bounds times the export scale) plus a viewBox. Checking Responsive (remove width/height) in the export dialog strips the two attributes and keeps only the viewBox, so the SVG fills whatever container you give it while preserving its aspect ratio.
For fixed exports that should still shrink on small screens:
.mk-embed svg { max-width: 100%; height: auto;}For responsive exports, control size from the container. The aspect-ratio should match the viewBox width and height (the third and fourth numbers):
.mk-figure { max-width: 320px; aspect-ratio: 92.5 / 100; /* viewBox width / height */}.mk-figure svg { width: 100%; height: 100%;}Multiple copies of the same export on one page
Each export gets fresh ids: the random molkit-xxxxx root id and suffixed internal ids such as bond-grad-bond_..._0nyq30 on gradient definitions. Two different exports therefore never collide. But if you paste the same file twice, every id now appears twice in one document, and that breaks rendering in a specific way:
- Gradient, clip-path, and mask definitions are referenced by
url(#id). The browser resolves a duplicated id to the first match in the document, so the second copy silently uses the first copy’s defs. - Bond gradients use
gradientUnits="userSpaceOnUse"with absolute coordinates. If the two copies sit at different rendered positions, the second copy’s gradient strokes sample the wrong coordinate space and can render shifted, solid, or invisible. - The internal
styleblock is scoped with:where(#molkit-xxxxx)selectors, so id rewrites must also touch style text or the second copy loses its styling.
The fix is to suffix every id in each inlined instance and rewrite all three reference forms:
function dedupMolkitIds(svg, suffix) { const idMap = new Map(); for (const el of svg.querySelectorAll('[id]')) { const next = el.id + suffix; idMap.set(el.id, next); el.id = next; } const rewriteUrls = (text) => text.replace(/url\(["']?#([^"')]+)["']?\)/g, (m, id) => (idMap.has(id) ? `url(#${idMap.get(id)})` : m)); for (const el of svg.querySelectorAll('*')) { for (const attr of [...el.attributes]) { if (attr.value.includes('url(#')) attr.value = rewriteUrls(attr.value); if (/^(xlink:)?href$/.test(attr.name) && attr.value.startsWith('#')) { const id = attr.value.slice(1); if (idMap.has(id)) attr.value = '#' + idMap.get(id); } } } for (const style of svg.querySelectorAll('style')) { style.textContent = rewriteUrls(style.textContent) .replace(/#([A-Za-z][\w-]*)/g, (m, id) => (idMap.has(id) ? '#' + idMap.get(id) : m)); } return svg;}
// One unique suffix per inlined copy:dedupMolkitIds(svgEl, '-' + Math.random().toString(36).slice(2, 8));The hex-color lookalikes in style text (#ffffff) pass through unchanged because they never appear in the id map. The simplest alternative, if you control the exports: export the drawing once per placement. Each export gets its own ids and the problem never arises.
Page-CSS bleed into inline SVGs
Inline SVG text inherits typographic properties from the host page. A site-wide line-height: 1.7 or letter-spacing: 0.02em will shift atom labels and formula subscripts out of position, because the export was laid out with the browser defaults. Reset them on the wrapper you already added:
.mk-embed,.mk-embed text,.mk-embed tspan { line-height: normal; letter-spacing: normal; word-spacing: normal; text-transform: none;}Make the wrapper a habit: one div with a known class around every embed gives you a sizing hook, a reset boundary, and a stable selector for theming overrides and the recipes in the cookbook.
See also
- Developer overview for the three-layer anatomy of an export
- CSS theming for restyling the
.mk-*classes - Chemistry metadata for reading
data-mk-*attributes - Controlling animations for animated exports
- Cookbook for copy-paste interaction recipes
- SVG export reference for the export dialog options