diff --git a/examples/svelte-invoice-form/.gitignore b/examples/svelte-invoice-form/.gitignore
new file mode 100644
index 00000000..d70bb9cc
--- /dev/null
+++ b/examples/svelte-invoice-form/.gitignore
@@ -0,0 +1,4 @@
+node_modules
+dist
+.DS_Store
+*.local
diff --git a/examples/svelte-invoice-form/README.md b/examples/svelte-invoice-form/README.md
new file mode 100644
index 00000000..282f21c5
--- /dev/null
+++ b/examples/svelte-invoice-form/README.md
@@ -0,0 +1,39 @@
+# Dynamic Invoice Form — Formisch + Svelte 5
+
+A minimal, standalone [Vite](https://vite.dev) + [Svelte 5](https://svelte.dev) example that builds a dynamic invoice form with [Formisch](https://formisch.dev) and [Valibot](https://valibot.dev).
+
+It demonstrates the ideas from the guide [_Building a Dynamic Invoice Form in Svelte 5 with Formisch_](https://formisch.dev/blog/dynamic-invoice-form-svelte/):
+
+- A single Valibot schema as the source of truth for structure, validation and typed output
+- Nested fields (`client.name`, `client.email`) wired with `Field`
+- Dynamic line items with `FieldArray`, plus `insert` / `remove`
+- Live totals derived from the form input with Svelte 5's `$derived`
+- Typed, validated output on submit and one-call `reset`
+
+## Getting started
+
+```bash
+npm install
+npm run dev
+```
+
+Then open the printed local URL.
+
+## Scripts
+
+| Command | Description |
+| ----------------- | ---------------------------------------- |
+| `npm run dev` | Start the Vite dev server |
+| `npm run build` | Build for production into `dist/` |
+| `npm run preview` | Preview the production build |
+| `npm run check` | Type-check the project with svelte-check |
+
+## Project structure
+
+```
+src/
+├── App.svelte # The invoice form (fields, array, totals, submit)
+├── schema.ts # Valibot InvoiceSchema + inferred InvoiceOutput type
+├── main.ts # App entry point
+└── app.css # Minimal styling
+```
diff --git a/examples/svelte-invoice-form/index.html b/examples/svelte-invoice-form/index.html
new file mode 100644
index 00000000..880414fc
--- /dev/null
+++ b/examples/svelte-invoice-form/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+ Dynamic Invoice Form
+ Built with Formisch, Valibot and Svelte 5 runes.
+
+
+
+ {#if submittedInvoice}
+
+
Submitted invoice
+
+ Typed, validated schema output (also logged to the console).
+
+
{JSON.stringify(submittedInvoice, null, 2)}
+
+ {/if}
+
diff --git a/examples/svelte-invoice-form/src/app.css b/examples/svelte-invoice-form/src/app.css
new file mode 100644
index 00000000..fe8c6e42
--- /dev/null
+++ b/examples/svelte-invoice-form/src/app.css
@@ -0,0 +1,213 @@
+:root {
+ --border: #e2e8f0;
+ --muted: #64748b;
+ --text: #0f172a;
+ --primary: #6d28d9;
+ --error: #dc2626;
+ --bg: #f8fafc;
+ --surface: #ffffff;
+
+ color-scheme: light;
+ font-family:
+ system-ui,
+ -apple-system,
+ 'Segoe UI',
+ Roboto,
+ sans-serif;
+ color: var(--text);
+ background-color: var(--bg);
+}
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ padding: 2rem 1rem;
+}
+
+.page {
+ max-width: 720px;
+ margin: 0 auto;
+}
+
+h1 {
+ font-size: 1.5rem;
+ margin: 0 0 0.25rem;
+}
+
+.subtitle {
+ margin: 0 0 2rem;
+ color: var(--muted);
+}
+
+fieldset {
+ border: 1px solid var(--border);
+ border-radius: 12px;
+ padding: 1.25rem;
+ margin: 0 0 1.5rem;
+ background: var(--surface);
+}
+
+legend {
+ font-weight: 600;
+ padding: 0 0.5rem;
+}
+
+.grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 1rem;
+}
+
+.field {
+ display: flex;
+ flex-direction: column;
+ min-width: 0;
+}
+
+label {
+ display: flex;
+ flex-direction: column;
+ gap: 0.35rem;
+ font-size: 0.875rem;
+ font-weight: 500;
+}
+
+input,
+textarea {
+ font: inherit;
+ padding: 0.55rem 0.7rem;
+ border: 1px solid var(--border);
+ border-radius: 8px;
+ background: var(--surface);
+ color: inherit;
+}
+
+input:focus,
+textarea:focus {
+ outline: 2px solid var(--primary);
+ outline-offset: -1px;
+}
+
+.error {
+ color: var(--error);
+ font-size: 0.8rem;
+ margin: 0.25rem 0 0;
+}
+
+.line-item {
+ border: 1px solid var(--border);
+ border-radius: 10px;
+ padding: 1rem;
+ margin-bottom: 1rem;
+}
+
+.line-item-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 0.75rem;
+}
+
+.line-item-header strong {
+ font-size: 0.95rem;
+}
+
+.line-item-fields {
+ display: grid;
+ grid-template-columns: 2fr 1fr 1fr;
+ gap: 0.75rem;
+}
+
+/* Stack the multi-column sections on narrow screens so inputs stay usable. */
+@media (max-width: 640px) {
+ .grid,
+ .line-item-fields {
+ grid-template-columns: 1fr;
+ }
+}
+
+.line-total {
+ text-align: right;
+ color: var(--muted);
+ font-size: 0.85rem;
+ margin: 0.5rem 0 0;
+}
+
+.totals {
+ display: grid;
+ gap: 0.35rem;
+ margin-left: auto;
+ margin-top: 1.5rem;
+ max-width: 260px;
+}
+
+.totals-row {
+ display: flex;
+ justify-content: space-between;
+}
+
+.totals-row.grand {
+ font-weight: 700;
+ font-size: 1.05rem;
+ border-top: 1px solid var(--border);
+ padding-top: 0.5rem;
+ margin-top: 0.25rem;
+}
+
+button {
+ font: inherit;
+ cursor: pointer;
+ border-radius: 8px;
+ border: 1px solid transparent;
+ padding: 0.55rem 1rem;
+ font-weight: 600;
+}
+
+button:disabled {
+ cursor: not-allowed;
+ opacity: 0.55;
+}
+
+.btn-primary {
+ background: var(--primary);
+ color: white;
+}
+
+.btn-secondary {
+ background: var(--surface);
+ border-color: var(--border);
+ color: var(--text);
+}
+
+.btn-ghost {
+ background: transparent;
+ color: var(--error);
+ padding: 0.35rem 0.6rem;
+ font-size: 0.85rem;
+}
+
+.actions {
+ display: flex;
+ gap: 0.75rem;
+ margin-top: 1rem;
+}
+
+.submitted {
+ margin-top: 2rem;
+ border: 1px solid var(--border);
+ border-radius: 12px;
+ padding: 1.25rem;
+ background: var(--surface);
+}
+
+pre {
+ overflow-x: auto;
+ background: #0f172a;
+ color: #e2e8f0;
+ padding: 1rem;
+ border-radius: 8px;
+ font-size: 0.8rem;
+}
diff --git a/examples/svelte-invoice-form/src/main.ts b/examples/svelte-invoice-form/src/main.ts
new file mode 100644
index 00000000..c049299c
--- /dev/null
+++ b/examples/svelte-invoice-form/src/main.ts
@@ -0,0 +1,9 @@
+import { mount } from 'svelte';
+import './app.css';
+import App from './App.svelte';
+
+const app = mount(App, {
+ target: document.getElementById('app')!,
+});
+
+export default app;
diff --git a/examples/svelte-invoice-form/src/schema.ts b/examples/svelte-invoice-form/src/schema.ts
new file mode 100644
index 00000000..71bb987f
--- /dev/null
+++ b/examples/svelte-invoice-form/src/schema.ts
@@ -0,0 +1,69 @@
+import * as v from 'valibot';
+
+/**
+ * Input pipe for a monetary amount. The value comes from the input as a string,
+ * gets transformed to a number and must not be negative.
+ */
+const moneyInput = (message: string) =>
+ v.pipe(
+ v.string(),
+ v.nonEmpty(message),
+ v.toNumber(),
+ v.minValue(0, 'Amount cannot be negative')
+ );
+
+/**
+ * Input pipe for a positive number (e.g. a quantity). The value comes from the
+ * input as a string, gets transformed to a number and must be at least 1.
+ */
+const positiveNumberInput = (message: string) =>
+ v.pipe(
+ v.string(),
+ v.nonEmpty(message),
+ v.toNumber(),
+ v.minValue(1, 'Value must be at least 1')
+ );
+
+/**
+ * The invoice form model. This single schema is the source of truth for the
+ * form structure, its validation rules and the typed output produced on submit.
+ */
+export const InvoiceSchema = v.object({
+ invoiceNumber: v.pipe(v.string(), v.nonEmpty('Invoice number is required')),
+ issueDate: v.pipe(v.string(), v.nonEmpty('Issue date is required')),
+ dueDate: v.pipe(v.string(), v.nonEmpty('Due date is required')),
+ client: v.object({
+ name: v.pipe(v.string(), v.nonEmpty('Client name is required')),
+ email: v.pipe(
+ v.string(),
+ v.nonEmpty('Client email is required'),
+ v.email('Enter a valid email address')
+ ),
+ }),
+ lineItems: v.pipe(
+ v.array(
+ v.object({
+ description: v.pipe(v.string(), v.nonEmpty('Description is required')),
+ quantity: positiveNumberInput('Quantity is required'),
+ unitPrice: moneyInput('Unit price is required'),
+ })
+ ),
+ v.minLength(1, 'Add at least one invoice item'),
+ v.maxLength(10, 'You can only add up to 10 invoice items')
+ ),
+ taxRate: v.pipe(
+ v.string(),
+ v.nonEmpty('Tax rate is required'),
+ v.toNumber(),
+ v.minValue(0, 'Tax cannot be negative'),
+ v.maxValue(100, 'Tax cannot be more than 100%')
+ ),
+ discount: moneyInput('Discount is required'),
+ notes: v.optional(v.string()),
+});
+
+/**
+ * The validated and transformed invoice data produced when the form is
+ * submitted. Numeric fields have already been converted from strings to numbers.
+ */
+export type InvoiceOutput = v.InferOutput