Capture a Svelte component
Export a Svelte 5 card using bind:this, reactive state and tick(), including the latest edit and a browser-only import.
Bind the card with bind:this, apply any state changes, then await tick() before export. In SvelteKit, load SnapDOM inside the click handler so capture runs only in the browser.
Install and use SnapDOM in Svelte
Install the capture library in your existing Svelte project:
npm install @zumer/snapdom
Save this as ExportCard.svelte and render it in your page.
<script>
import { tick } from 'svelte'
let card = $state()
let title = $state('Monthly report')
let saving = $state(false)
let error = $state('')
async function save() {
saving = true
error = ''
try {
title = title.trim() || 'Untitled report'
await tick()
await document.fonts.ready
await Promise.all(Array.from(card.querySelectorAll('img'), img => img.decode()))
const { snapdom } = await import('@zumer/snapdom')
await snapdom.download(card, { format: 'png', filename: 'svelte-card' })
} catch (cause) {
error = cause instanceof Error ? cause.message : 'Capture failed'
} finally {
saving = false
}
}
</script>
<label>Card title <input bind:value={title} /></label>
<article bind:this={card}><h2>{title}</h2><p>Ready to share.</p></article>
<button onclick={save} disabled={saving}>Download PNG</button>
<p role="status">{error}</p>Capture after a Svelte state update
Svelte batches DOM updates. The example normalizes the title and then awaits tick(), so the downloaded card contains the normalized title rather than the previous value. tick() does not wait for a fetch, chart animation or image download; await those separately.
The reference from bind:this becomes available after mount. If the card lives inside an {#if} block, keep the export button disabled while it is absent. Use a click handler rather than an effect that could export again whenever reactive state changes.
Wait for assets without including the controls
The card is the capture root; the title input, button and status message remain outside it. Image decoding and document.fonts.ready settle the card’s assets after the DOM update. Rejected image decoding is surfaced in the status message, so the user can retry after correcting the asset.
Use snapdom.toBlob(card, { format: 'png' }) for an upload and the FormData recipe to send the file to your endpoint. For dashboard layouts containing graphs, use the dashboard export recipe.
Use the same component in SvelteKit
SvelteKit can render this component on the server. The dynamic import and every DOM read are inside save(), which the browser invokes from the button. Do not move capture into module initialization or a server load function.
This example uses Svelte 5 runes and onclick. A legacy-mode component can use ordinary reactive variables, but still needs bind:this and the same wait for pending DOM updates.
Frequently asked questions
Does tick() wait for my API response?
No. Await the application data request, update state, then await tick(). Fonts, image decoding and chart animations have their own readiness checks.
Why is bind:this undefined?
The element is not mounted yet or its conditional block is absent. Trigger export after mount, while the bound card is present.
Capture it in the browser
Install SnapDOM and turn the DOM your users already see into a portable image.
Open the demoInstallation