Svelte
Use Triiiceratops in Svelte 5 as a native component \u2014 no custom element and no wrapper layer, source-distributed and driven by runes.
triiiceratops/svelte is the native Svelte 5 component — no custom element, no wrapper layer. Core ships the viewer as a source-distributed component, so your application compiles it inside its own Svelte runtime: it tree-shakes normally and never bundles a second copy of Svelte.
This is also the only entry point that needs the optional svelte peer installed. It is a superset of the root entry — everything triiiceratops exports is re-exported here, so a Svelte app imports from this one specifier and nothing else.
Install
pnpm add triiiceratops sveltenpm install triiiceratops sveltebun add triiiceratops svelteYour first viewer
<script lang="ts">
import { TriiiceratopsViewer } from 'triiiceratops/svelte';
// Import the design tokens + themes exactly once, anywhere in your app.
import 'triiiceratops/style.css';
</script>
<!-- The container must have a height; the viewer fills it. -->
<div style="height: 600px;">
<TriiiceratopsViewer manifestId="https://example.org/manifest.json" />
</div>Reading and commanding state
Svelte needs no handle abstraction and no selector API. ViewerState is a rune class, so its members are already reactive: read them in your markup or in a $derived and Svelte tracks the dependency for you. There are two ways to get the instance.
bind:viewerState — when the state belongs to this component:
<script lang="ts">
import { TriiiceratopsViewer, type ViewerState } from 'triiiceratops/svelte';
import 'triiiceratops/style.css';
let viewerState = $state<ViewerState | undefined>();
// Reactive: recomputes when the viewer navigates.
const canvasId = $derived(viewerState?.canvasId ?? 'No canvas yet');
</script>
<p>{canvasId}</p>
<button onclick={() => viewerState?.nextCanvas()}>Next canvas</button>
<div style="height: 600px;">
<TriiiceratopsViewer
bind:viewerState
manifestId="https://example.org/manifest.json"
/>
</div>getContext(VIEWER_STATE_KEY) — inside any descendant, with no prop threading:
<script lang="ts">
import { getContext } from 'svelte';
import { VIEWER_STATE_KEY, type ViewerState } from 'triiiceratops/svelte';
const state = getContext<ViewerState>(VIEWER_STATE_KEY);
</script>
<span>{state.currentCanvasIndex + 1} / {state.sequenceCount}</span>The component sets that context, so this is the same instance bind:viewerState exposes — the one the plugin SDK and every other host read. ViewerState is per-viewer: two viewers on a page are two independent instances.
The framework-neutral triiiceratops/selectors runtime exists for hosts that need memoized, equality-gated projections. Svelte's reactivity already does that work, so you do not need it here.
Props
Prop | Type |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Plain Svelte props — objects and functions pass straight through, so none of the attribute-versus-property care the custom element needs applies. manifestId and canvasId are uncontrolled: one-way instructions to the viewer, not enforced bindings. Re-asserting the current canvasId writes nothing, so your markup never fights a user who navigated internally; read viewerState to follow where the viewer actually is.
Errors
The two callback props receive the same structured objects the custom element dispatches as pluginerror and viewererror events:
<script lang="ts">
import { TriiiceratopsViewer } from 'triiiceratops/svelte';
import 'triiiceratops/style.css';
</script>
<TriiiceratopsViewer
manifestId="https://example.org/manifest.json"
onpluginerror={(error) => console.warn(error.pluginName, error.phase)}
onviewererror={(error) => console.error(error.code, error.message)}
/>PluginError carries a retry(), so a host can offer the user a second attempt rather than only reporting the failure.
Multiple manifests
manifestsState (and its ManifestsState class) is exported from this entry for apps that coordinate several manifests — a collection browser, say — outside any single viewer instance.
import { manifestsState } from 'triiiceratops/svelte';Live example
The Svelte example is a Vite + Svelte 5 application that imports triiiceratops/svelte and triiiceratops/style.css from the published package. It is published beside these pages and runs this release's bundles, so what it does is what the version documented here does.
Where to go next
Everything below is framework-neutral and has a Svelte tab on every example:
Configuration & state — panels, layout, gallery, and the full
ViewerStatesurface.Theming —
theme,themeConfig, and CSS variables.Content Security Policy — Svelte hosts use the light-DOM recipe, whose component styles are nonce-addressable.
Verified against the packed package
CI runs packed-consumer fixtures across Chromium, Firefox, and WebKit: svelte-vite (the native component under Vite) and sveltekit-ssr (server-rendered, then hydrated, with no mismatch warnings). Every code sample on this site is type-checked against those same tarballs.