This skill covers special elements in Svelte templates (prefixed with svelte: ), used for scenarios such as error boundaries, async loading states, window/document/event listeners, dynamic tags, head injection, compiler options, etc.
When to use this skill
Use when the user needs to catch rendering errors or async pending states (<svelte:boundary>), listen to window / document / body events, dynamically render HTML tags (<svelte:element>), inject content into document.head(<svelte:head>), or configure compiler options/custom elements (<svelte:options>).
Critical: <svelte:boundary> (Svelte 5.3+)
Error and async boundaries, "isolating" parts of the app to handle errors and pending states. Boundaries catch synchronous errors thrown during rendering,$effect errors in await expression rejections; errors not in the rendering flow such as event handlers,setTimeoutevent-triggered async work, etc.will not becaught.
Three key properties
| Property | Type | Description |
|---|---|---|
pending |
Snippet |
Shown on initial render until all await resolve (onlyshown on initial render) |
failed |
Snippet<[error, reset]> |
Shown when an error occurs, receives error and a recoverable reset function |
onerror |
(error, reset) => void |
Called when an error occurs, commonly used for reporting to Sentry etc.; triggers in parallel with failed . |
<svelte:boundary onerror={(e) => report(e)}>
<FlakyComponent />
{#snippet pending()}
<p>加载中…</p>
{/snippet}
{#snippet failed(error, reset)}
<button onclick={reset}>oops! try again</button>
{/snippet}
</svelte:boundary>
SSR: transformError (Svelte 5.51+)
By default, error boundaries are on the serverinvalid—once rendering errors, the entire render(...) call fails. Since 5.51, you can pass a render(...) / mount(...) / hydrate(...) to transformError function, which must return a JSON-stringifiable object, used to render within the failed fragment (serialized to HTML during SSR, used for hydration after deserialization on the client).
import { render } from 'svelte/server';
const { head, body } = await render(App, {
transformError: (error) => {
console.error(error); // 保留原始错误用于日志
return { message: 'An error occurred!' }; // 返回脱敏后的安全对象
}
});
In onerror rethrow
If an error is thrown in onerror (or the original error is rethrown), the error will beouterboundary caught—can be used to report the error to a global boundary while preserving the current failed UI.
reset mode
reset is an ordinary function—can be stored in state and called from anywhere (including global UI outside the boundary, keyboard shortcuts, etc.):
<script>
let error = $state(null);
let reset = $state(() => {});
function onerror(e, r) { error = e; reset = r; }
</script>
<svelte:boundary {onerror}>
<FlakyComponent />
</svelte:boundary>
{#if error}
<button onclick={() => { error = null; reset(); }}>oops! try again</button>
{/if}
Full examples and reference:examples/boundary-examples.md、references/boundary-reference.md
Critical: <svelte:window>
Listen to window events and bind window properties:
<svelte:window onkeydown={handleKey} onresize={handleResize} />
<svelte:window bind:scrollX bind:scrollY bind:innerWidth bind:innerHeight />
Bindable properties:
| Property | Type | Read-only | Description |
|---|---|---|---|
innerWidth / innerHeight |
number |
Yes | Viewport width/height (CSS pixels) |
outerWidth / outerHeight |
number |
Yes | Browser window outer dimensions (including chrome) |
scrollX / scrollY |
number |
No | Scroll position (the only writablewindow binding) |
online |
boolean |
Yes | navigator.onLine alias |
devicePixelRatio |
number |
Yes | device pixel ratio of the current display |
Listenable events: all window-level events—keyboard (onkeydown / onkeyup), mouse (onclick / onmousemove etc.), touch (ontouchstart / ontouchmove)、pointer (onpointerdown etc.), wheel (onwheel)、clipboard (oncopy / oncut / onpaste)、drag (ondrag series)、focus (onfocus / onblur)、resource (onload / onerror / onscroll / onresize) etc.
<svelte:window>can only appear at the top level of a component, not inside block-level elements or conditional blocks.On initial mountdoes notscroll the page to
scrollX/scrollY's initial value (for accessibility reasons); only subsequent value changes trigger scrolling. If you need to scroll on mount, call$effectinscrollTo()。
Critical: <svelte:document>
listen for document-level events (window events not supported):
<svelte:document onvisibilitychange={handleVisibility} />
<svelte:document {@attach myAttachment} />
Bindable properties(All readonly):
| Property | Type | Description |
|---|---|---|
activeElement |
Element | null |
Currently focused element |
fullscreenElement |
Element | null |
Currently fullscreen element |
pointerLockElement |
Element | null |
Pointer-locked element |
visibilityState |
'visible' | 'hidden' |
Document visibility |
Most commonly used events:onvisibilitychange(tab switching)、onselectionchange(text selection change)、onreadystatechange、onfullscreenchange、onpointerlockchange、oncopy / oncut / onpaste etc.
<svelte:document>can also only appear at the top level; supports{@attach ...}to attach custom behavior todocument.
Critical: <svelte:body>
Listen for events on the body element (such as mouseenter、mouseleave, which do not fire on window):
<svelte:body onmouseenter={handleMouseenter} onmouseleave={handleMouseleave} use:someAction />
<svelte:body>also supportsuse:action (this is the only clean way to add an action to<body>). It can also only appear at the top level.
Critical: <svelte:head>
Insert content into document.head ; during SSRexposed separatelyoutside the body, the framework will put the head content into the HTML <head> tag.
<svelte:head>
<title>Hello world!</title>
<meta name="description" content="SEO description" />
<meta property="og:title" content="OG title" />
<link rel="canonical" href="/current-url" />
</svelte:head>
Supported elements:
| Element | Purpose |
|---|---|
<title> |
Document title |
<meta> |
SEO(name="description")、Open Graph(property="og:...")、Twitter Card、robots directives |
<link rel="stylesheet" /> |
Per-page/per-theme stylesheets |
<link rel="canonical" /> |
Canonical URL |
<link rel="prefetch" | rel="preload" /> |
Resource prefetch/preload |
<style> |
Inline critical CSS |
<script type="application/ld+json"> |
JSON-LD structured data |
Key behaviors:
- Reactive:
$state/$derivedWhen it changes, the head updates automatically. - Duplicate merging: for elements of the same kind (such as two
<title>) the later rendered one overrides the previous one (not appended repeatedly). - Multiple blocks: a single component can have multiple
<svelte:head>, all merged into the head. - SSR exposed separately:
render()returns{ head, body }, the framework needs to putheadinto the<head>tag.
Complete example:examples/svelte-head-examples.md、references/svelte-head-reference.md
Critical: <svelte:element>
Dynamically render unknown tags (such as from a CMS or database):
<script>
let tag = $state('hr');
</script>
<svelte:element this={tag}>内容</svelte:element>
<svelte:element this={tag} xmlns="http://www.w3.org/2000/svg" />
Behavior rules:
thisisnull/undefined→ element is not renderedthisis a void element (br、hr、imgetc.) but has child elements → runtime error in development mode- Svelte automatically infers the namespace (svg, mathml); you can use
xmlnsto specify it explicitly thismust be a valid DOM tag name (such asdiv/circle);#text、svelte:headetc. are invalid
The only supported binding:bind:this(because generic elements do not support Svelte built-in bindings such as bind:value)
Critical: <svelte:options>
Set compiler options:
<svelte:options runes={true} />
<svelte:options namespace="svg" />
<svelte:options customElement="my-element" />
<svelte:options css="injected" />
| Options | Type | Description |
|---|---|---|
runes={true|false} |
boolean |
Force entering/exiting Runes Mode |
namespace="html|svg|mathml" |
string |
Component namespace (default html) |
customElement={...} |
string | object |
Compile as a custom element (string means tag) |
css="injected" |
string |
Inline style injection (SSR → style tag; CSR → JS) |
<svelte:options>must be placed after<script>and before the template.
Legacy (deprecated) options(ineffective in Runes mode):immutable={true|false}、accessors={true|false}。
Quick Fixes
| Problem | Solution |
|---|---|
Want to listen to visibilitychange / selectionchange |
Use <svelte:document> instead of <svelte:window> |
Want to listen to mouseenter / mouseleave |
Use <svelte:body> |
| Dynamic tag does not render | Check whether this is nullish |
| Styles do not take effect | Use css="injected" to force inlining |
| A render error crashes the entire app | Use <svelte:boundary> + failed fragment to isolate |
| Show a skeleton screen during async loading | Use <svelte:boundary> + pending fragment (onlyfirst render) |
| Want to report to Sentry during rendering | Use <svelte:boundary onerror={...}> |
Want reset to trigger outside the boundary |
Store error / reset in $state |
Dynamically change <title> or <meta> |
Use <svelte:head> to wrap reactive expressions |
Gotchas
- Can only appear at the top level —
svelte:window/document/body/head/elementcannot be inside{#if}or other block-level elements. - SSR behavior differs —
<svelte:head>During SSR, the content is exposed separately and is not inside body;<svelte:boundary>is ineffective for SSR by default (available since 5.51+ withtransformErrorenabled). svelte:elementis a generic binding — does not supportbind:valueand other element-specific bindings.svelte:window'sscrollX/scrollYis the only writable binding — all others are readonly.<svelte:boundary>does not catch errors in event handlers /setTimeouterror in — these are not part of the rendering flow.<svelte:boundary>'spendingis only shown on the initial render — for subsequent async updates use$effect.pending()。
FAQ
Q: <svelte:boundary> and try/catch?
A: svelte:boundary catches synchronous/asynchronous errors during rendering and errors in $effect ; try/catch can only catch synchronous errors. boundary can also work with {#snippet pending} to handle the initial async loading state.
Q: When to use onerror, and when to use failed?
A: failed is UI — determines what todisplay;onerror is a side effect — determines what todo(such as reporting to Sentry). The two are completely independent; you can use only one, or both at the same time.
Q: svelte:element and a regular component?
A: svelte:element renders HTML tags (not Svelte components), suitable for scenarios where the tag is determined at runtime, such as CMS content.
Q: When to use namespace="svg"?
A: When the component will be used as an SVG child element (such as <Icon.svelte> as <svg> inside <g> used).
Q: mount / hydrate / render How to pass transformError?
A: They are all the same signature—pass a function that takes a (error) => JSON-stringifiable-object function:
import { mount, hydrate } from 'svelte';
import { render } from 'svelte/server';
mount(App, { target, transformError: (e) => ({ message: e.message }) });
hydrate(App, { target, transformError: (e) => ({ message: 'Hydration failed' }) });
const { head, body } = await render(App, { transformError: (e) => ({ message: '...' }) });
Q: Can the same <svelte:head> block be used in multiple components?
A: No—<svelte:head> is component-level scoped. But you can split the head content into snippets, import them separately in multiple components, or wrap a <MetaTags> component.
Q: <svelte:window> Do event handlers affect performance?
A: onresize / onscroll For high-frequency events like these, throttling/debouncing is recommended; the overhead of other events is negligible. Svelte automatically removes listeners when the component is destroyed, so no manual cleanup is needed.
Examples
Practical examples demonstrating all svelte: special elements.
| File | Description |
|---|---|
| examples/boundary-examples.md | Comprehensive examples for <svelte:boundary>: pending, failed, onerror, transformError, error reporting, nested boundaries |
| examples/svelte-head-examples.md | <svelte:head> examples: SEO meta, Open Graph, structured data, dynamic stylesheets, SSR head extraction |
| examples/svelte-element-examples.md | Examples for <svelte:window>, <svelte:document>, <svelte:body>, <svelte:element>, <svelte:component>, <svelte:self>, <svelte:options> |
Quick Examples
- svelte:boundary: error fallback, loading skeleton, Sentry reporting, SSR
transformError - svelte:window: keyboard shortcuts, resize handling, scroll position binding
- svelte:document: page visibility, text selection tracking
- svelte:body: mouse enter/leave events
- svelte:head: dynamic
<title>, Open Graph meta, JSON-LD, per-page stylesheets - svelte:element: dynamic heading levels (h1-h6), conditional tags, SVG namespace
- svelte:component: dynamic component switching
- svelte:options: customElement, runes mode, SVG namespace
References
Detailed reference documentation for all special elements and compiler options.
| File | Description |
|---|---|
| references/boundary-reference.md | Full reference for <svelte:boundary> — pending/failed/onerror, transformError (5.51+), nesting, what is/isn't caught |
| references/svelte-head-reference.md | Full reference for <svelte:head> — SSR behavior, supported elements, hydration, reactivity |
| references/svelte-window-document-body-reference.md | All events and bindable properties for <svelte:window> / <svelte:document> / <svelte:body> |
| references/svelte-element-reference.md | Reference for <svelte:element>, <svelte:component>, <svelte:self>, <svelte:fragment> |
| references/svelte-options-reference.md | Full reference for <svelte:options> — customElement, runes, namespace, css, and all compiler options |