Skip to main content
@formepdf/svelte is the Svelte adapter for Forme. It ships the same components with the same props as @formepdf/react, authored as ordinary .svelte files, and serializes to the identical document model. {#each}, {#if}, snippets, and text interpolation just work - templates are plain Svelte 5 components evaluated on the server, with no special template language.

Install

The adapter requires Svelte 5 (^5.30.0) as a peer dependency. @formepdf/core is an optional peer: it is only needed to render PDF bytes locally (renderDocument, the preview helper). If you serialize templates and POST the JSON to the hosted API, skip it - serialize works with zero WASM.

Quickstart

Create a template as a normal Svelte component, src/lib/Invoice.svelte:
Serve it as a PDF from a SvelteKit endpoint, src/routes/invoice/+server.ts:
That is the whole route. renderDocument serializes the template and renders it through the WASM engine in one call. Using the hosted API instead? Serialize without rendering - no @formepdf/core install needed:

Component parity

The adapter is 1:1 with @formepdf/react: the same components with the same props.
  • Layout: Document, Page, View, Text, Image, Fixed, PageBreak
  • Semantics: H1-H6, OrderedList, UnorderedList, ListItem, Strong, Em, Code, Link
  • Tables: Table, Row, Cell
  • Graphics: Svg, QrCode, Barcode, Canvas, Watermark
  • Charts: BarChart, LineChart, PieChart, AreaChart, DotPlot
  • Form fields: TextField, Checkbox, Dropdown, RadioButton
Everything in the components reference and styles reference applies verbatim - document-level props (metadata, lang, pdfUa, pdfa, certification, fonts), CSS string shorthands (border: "1px solid #000", padding: "8 16"), StyleSheet.create(), and the Style type are identical. Only the syntax around the components changes. Nested <Text> spans become styled text runs, so mixed-style lines work exactly as in react:
Compiled templates (forme build --template, the $ref/$each/$if expression system for rendering without a JavaScript runtime) are TSX-only today. Svelte templates always serialize by evaluating the component server-side; to use stored templates with the hosted API, author them in TSX.

Page numbers

The engine substitutes the placeholders {{pageNumber}} and {{totalPages}} in text at render time. In JSX the braces can be typed as a string literal ({'{{pageNumber}}'}), but in a Svelte template they cannot - Svelte parses {{pageNumber}} as an expression containing an object literal, not as text. Interpolate the exported PAGE_NUMBER and TOTAL_PAGES constants instead:

Fonts

Font.register() has the same API as react and feeds the same process-wide store. Register in a <script module> block so registration runs once, not on every render:
Per-document registration via <Document fonts={[...]}> works too. See the fonts guide for sources, weights, and fallback chains.

Tailwind

@formepdf/tailwind’s tw() works unchanged - it returns a plain style object:

Custom graphics

<Canvas> takes a draw callback that records vector operations:
The draw callback runs during server-side serialization, not at PDF render time. It must be synchronous and pure: no await, no side effects, no browser or runtime APIs.

Live preview

formePreview() gives SvelteKit the same in-browser preview (layout overlays, click-to-inspect) that forme dev gives react users, mounted on a catch-all dev route. Create src/routes/dev/pdf/[...forme]/+server.ts:
Open /dev/pdf in the browser. The preview polls for changes and reloads shortly after you save the template (default 1000 ms in dev; polling is disabled when NODE_ENV is production, and the interval is configurable via pollMs). The helper renders through @formepdf/core, so the optional peer must be installed.

Render options

renderDocument and renderDocumentWithLayout forward all @formepdf/core render options unchanged, so features like embedded data and form flattening work exactly as documented for react:

Migrating from TSX

Migration is mechanical: component names, props, and style objects are identical, so most of a template moves over as-is. The react quickstart maps to Svelte like this: Two differences to note. First, react’s renderDocument takes an element (<Document>...), while Svelte components cannot be pre-bound to props - so the Svelte adapter’s renderDocument, serialize, and render take the component plus a props option. Second, whitespace in .svelte templates is normalized to the same rules JSX uses, so indentation never leaks stray spaces into rendered text.