From b7f42f69207a22552fe2a4b73570fcb09e73420e Mon Sep 17 00:00:00 2001
From: James Garbutt <43081j@users.noreply.github.com>
Date: Sun, 5 Jan 2025 21:52:23 +0000
Subject: [PATCH 01/17] wip: add svelte-form

Absolutely untested, written without running it or trying it out in any
way. Just a very rough start written off the top of my head with the
help of various bits of docs.
---
 packages/svelte-form/README.md            |   1 +
 packages/svelte-form/eslint.config.js     |   5 +
 packages/svelte-form/package.json         |  56 +++++++
 packages/svelte-form/src/Field.svelte     |  50 +++++++
 packages/svelte-form/src/createField.ts   |  68 +++++++++
 packages/svelte-form/src/createForm.ts    |  41 +++++
 packages/svelte-form/src/index.ts         |   6 +
 packages/svelte-form/tests/simple.svelte  | 175 ++++++++++++++++++++++
 packages/svelte-form/tests/simple.test.ts |  76 ++++++++++
 packages/svelte-form/tsconfig.build.json  |  12 ++
 packages/svelte-form/tsconfig.docs.json   |   9 ++
 packages/svelte-form/tsconfig.json        |   7 +
 packages/svelte-form/vite.config.ts       |  14 ++
 pnpm-lock.yaml                            |  69 +++++++++
 14 files changed, 589 insertions(+)
 create mode 100644 packages/svelte-form/README.md
 create mode 100644 packages/svelte-form/eslint.config.js
 create mode 100644 packages/svelte-form/package.json
 create mode 100644 packages/svelte-form/src/Field.svelte
 create mode 100644 packages/svelte-form/src/createField.ts
 create mode 100644 packages/svelte-form/src/createForm.ts
 create mode 100644 packages/svelte-form/src/index.ts
 create mode 100644 packages/svelte-form/tests/simple.svelte
 create mode 100644 packages/svelte-form/tests/simple.test.ts
 create mode 100644 packages/svelte-form/tsconfig.build.json
 create mode 100644 packages/svelte-form/tsconfig.docs.json
 create mode 100644 packages/svelte-form/tsconfig.json
 create mode 100644 packages/svelte-form/vite.config.ts

diff --git a/packages/svelte-form/README.md b/packages/svelte-form/README.md
new file mode 100644
index 000000000..3e608b1f8
--- /dev/null
+++ b/packages/svelte-form/README.md
@@ -0,0 +1 @@
+TODO - add a readme
diff --git a/packages/svelte-form/eslint.config.js b/packages/svelte-form/eslint.config.js
new file mode 100644
index 000000000..8ce6ad05f
--- /dev/null
+++ b/packages/svelte-form/eslint.config.js
@@ -0,0 +1,5 @@
+// @ts-check
+
+import rootConfig from '../../eslint.config.js'
+
+export default [...rootConfig]
diff --git a/packages/svelte-form/package.json b/packages/svelte-form/package.json
new file mode 100644
index 000000000..892f427f0
--- /dev/null
+++ b/packages/svelte-form/package.json
@@ -0,0 +1,56 @@
+{
+  "name": "@tanstack/svelte-form",
+  "version": "0.0.1",
+  "description": "Powerful, type-safe forms for Svelte.",
+  "author": "James Garbutt (https://github.com/43081j)",
+  "license": "MIT",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/TanStack/form.git",
+    "directory": "packages/svelte-form"
+  },
+  "homepage": "https://tanstack.com/form",
+  "scripts": {
+    "clean": "premove ./build ./coverage",
+    "test:eslint": "eslint ./src ./tests",
+    "test:types": "pnpm run \"/^test:types:ts[0-9]{2}$/\"",
+    "test:types:ts51": "node ../../node_modules/typescript51/lib/tsc.js",
+    "test:types:ts52": "node ../../node_modules/typescript52/lib/tsc.js",
+    "test:types:ts53": "node ../../node_modules/typescript53/lib/tsc.js",
+    "test:types:ts54": "node ../../node_modules/typescript54/lib/tsc.js",
+    "test:types:ts55": "node ../../node_modules/typescript55/lib/tsc.js",
+    "test:types:ts56": "tsc",
+    "test:lib": "vitest",
+    "test:lib:dev": "pnpm run test:lib --watch",
+    "test:build": "publint --strict",
+    "build": "tsc -p tsconfig.build.json"
+  },
+  "type": "module",
+  "types": "dist/index.d.ts",
+  "main": "dist/index.js",
+  "module": "dist/index.js",
+  "exports": {
+    ".": {
+      "import": {
+        "types": "./dist/index.d.ts",
+        "default": "./dist/index.js"
+      }
+    },
+    "./package.json": "./package.json"
+  },
+  "sideEffects": false,
+  "files": [
+    "dist",
+    "src"
+  ],
+  "dependencies": {
+    "@tanstack/form-core": "workspace:*"
+  },
+  "devDependencies": {
+    "svelte": "^5.16.1",
+    "premove": "^4.0.0"
+  },
+  "peerDependencies": {
+    "svelte": "^5.16.1"
+  }
+}
diff --git a/packages/svelte-form/src/Field.svelte b/packages/svelte-form/src/Field.svelte
new file mode 100644
index 000000000..fa4194300
--- /dev/null
+++ b/packages/svelte-form/src/Field.svelte
@@ -0,0 +1,50 @@
+<!-- TODO (43081j): figure out how to reference types in generics -->
+<script type="ts" generics="TParentData,
+  TName extends DeepKeys<TParentData>,
+  TFieldValidator extends
+    | Validator<DeepValue<TParentData, TName>, unknown>
+    | undefined = undefined,
+  TFormValidator extends
+    | Validator<TParentData, unknown>
+    | undefined = undefined,
+  TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>
+">
+  import type { Snippet } from 'svelte';
+  // TODO (43081j): somehow remove this circular reference
+  import { createField } from './createField';
+
+  type Props = {
+    children: Snippet<[
+      FieldApi<
+        TParentData,
+        TName,
+        TFieldValidator,
+        TFormValidator,
+        TData
+      >
+    ]>
+    [key: string]: unknown;
+  } & FieldApiOptions<
+    TParentData,
+    TName,
+    TFieldValidator,
+    TFormValidator,
+    TData
+  >;
+
+  let {
+    children
+  }: Props = $props();
+
+  const fieldApi = createField<
+    TParentData,
+    TName,
+    TFieldValidator,
+    TFormValidator,
+    TData
+  >(() => {
+    return fieldOptions
+  })
+</script>
+
+{@render children(fieldApi)}
diff --git a/packages/svelte-form/src/createField.ts b/packages/svelte-form/src/createField.ts
new file mode 100644
index 000000000..21880ba66
--- /dev/null
+++ b/packages/svelte-form/src/createField.ts
@@ -0,0 +1,68 @@
+import { FieldApi } from '@tanstack/form-core'
+import { onDestroy, onMount } from 'svelte'
+import Field from './Field.js'
+
+import type {
+  DeepKeys,
+  DeepValue,
+  FieldApiOptions,
+  Validator,
+} from '@tanstack/form-core'
+
+interface SvelteFieldApi<
+  TParentData,
+  TFormValidator extends
+    | Validator<TParentData, unknown>
+    | undefined = undefined,
+> {
+  Field: Field<TParentData, TFormValidator>
+}
+
+export function createField<
+  TParentData,
+  TName extends DeepKeys<TParentData>,
+  TFieldValidator extends
+    | Validator<DeepValue<TParentData, TName>, unknown>
+    | undefined = undefined,
+  TFormValidator extends
+    | Validator<TParentData, unknown>
+    | undefined = undefined,
+  TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
+>(
+  opts: () => FieldApiOptions<
+    TParentData,
+    TName,
+    TFieldValidator,
+    TFormValidator,
+    TData
+  >,
+) {
+  const options = opts()
+
+  const api = new FieldApi(options)
+
+  const extendedApi: typeof api & SvelteFieldApi<TParentData, TFormValidator> =
+    api as never
+
+  extendedApi.Field = Field as never
+
+  let mounted = false
+  // Instantiates field meta and removes it when unrendered
+  onMount(() => {
+    const cleanupFn = api.mount()
+    mounted = true
+    onDestroy(() => {
+      cleanupFn()
+      mounted = false
+    })
+  })
+
+  // TODO (43081j): does this do what i think? we don't access anything
+  // svelte is aware of, so maybe it'll never call this?
+  $effect(() => {
+    if (!mounted) return
+    api.update(opts())
+  })
+
+  return extendedApi
+}
diff --git a/packages/svelte-form/src/createForm.ts b/packages/svelte-form/src/createForm.ts
new file mode 100644
index 000000000..3259ac141
--- /dev/null
+++ b/packages/svelte-form/src/createForm.ts
@@ -0,0 +1,41 @@
+import { FormApi } from '@tanstack/form-core'
+import { onMount } from 'svelte'
+import { Field, createField } from './createField'
+import type { FormOptions, Validator } from '@tanstack/form-core'
+
+export interface SvelteFormApi<
+  TFormData,
+  TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,
+> {
+  Field: Field<TFormData, TFormValidator>
+  createField: typeof createField<TFormData, TFormValidator>
+}
+
+export function createForm<
+  TParentData,
+  TFormValidator extends
+    | Validator<TParentData, unknown>
+    | undefined = undefined,
+>(opts?: () => FormOptions<TParentData, TFormValidator>) {
+  const options = opts?.()
+  const api = new FormApi<TParentData, TFormValidator>(options)
+  const extendedApi: typeof api & SvelteFormApi<TParentData, TFormValidator> =
+    api as never
+
+  // TODO (43081j): somehow this needs to actually be
+  // `<Field ...props form={api}>`.
+  // No clue right now how we do that
+  extendedApi.Field = Field
+  extendedApi.createField = (props) =>
+    createField(() => {
+      return { ...props(), form: api }
+    })
+
+  onMount(api.mount)
+
+  // TODO (43081j): does this actually work? we don't use any observed
+  // data, so maybe svelte won't re-run this effect?
+  $effect(() => api.update(opts?.()))
+
+  return extendedApi
+}
diff --git a/packages/svelte-form/src/index.ts b/packages/svelte-form/src/index.ts
new file mode 100644
index 000000000..2f308b888
--- /dev/null
+++ b/packages/svelte-form/src/index.ts
@@ -0,0 +1,6 @@
+export * from '@tanstack/form-core'
+
+export { createForm, type SvelteFormApi } from './createForm.js'
+
+export type { Field } from './Field.js'
+export { createField } from './createField.js'
diff --git a/packages/svelte-form/tests/simple.svelte b/packages/svelte-form/tests/simple.svelte
new file mode 100644
index 000000000..38a510ac1
--- /dev/null
+++ b/packages/svelte-form/tests/simple.svelte
@@ -0,0 +1,175 @@
+<script type="ts">
+import { createForm } from '../src/index.js'
+import type { FieldApi, FormOptions } from '../src/index.js'
+
+interface Employee {
+  firstName: string
+  lastName: string
+  color?: '#FF0000' | '#00FF00' | '#0000FF'
+  employed: boolean
+  jobTitle: string
+}
+
+const sampleData: Employee = {
+  firstName: 'Bob',
+  lastName: '',
+  employed: false,
+  jobTitle: '',
+}
+
+const formConfig: FormOptions<Employee, undefined> = {
+  defaultValues: sampleData,
+}
+
+const form = createForm(() => ({
+  defaultValues: {
+    firstName: '',
+    lastName: '',
+  },
+  onSubmit: async ({ value }) => {
+    // Do something with form data
+    console.log(value)
+  },
+}))
+
+</script>
+<form
+  id="form"
+  on:submit|preventDefault={form.onSubmit}
+>
+  <h1>TanStack Form - Svelte Demo</h1>
+
+  <form.Field
+    name="firstName"
+    validators={{
+      onChange: ({ value }) =>
+        value.length < 3 ? 'Not long enough' : undefined,
+    }}
+  >
+    {#snippet children(field)}
+      <div>
+        <label>First Name</label>
+        <input
+          id="firstName"
+          type="text"
+          placeholder="First Name"
+          value={field.state.value}
+          on:blur={() => field.handleBlur()}
+          on:input={(e: Event) => {
+            const target = e.target as HTMLInputElement
+            field.handleChange(target.value)
+          }}
+        />
+      </div>
+    {/snippet}
+  </form.Field>
+  <form.Field
+    name="lastName"
+    validators={{
+      onChange: ({ value }) =>
+        value.length < 3 ? 'Not long enough' : undefined,
+    }}
+  >
+    {#snippet children(field)}
+      <div>
+        <label>Last Name</label>
+        <input
+          id="lastName"
+          type="text"
+          placeholder="Last Name"
+          value={field.state.value}
+          on:blur={() => field.handleBlur()}
+          on:input={(e: Event) => {
+            const target = e.target as HTMLInputElement
+            field.handleChange(target.value)
+          }}
+        />
+      </div>
+    {/snippet}
+  </form.Field>
+  <form.Field
+    name="color"
+  >
+    {#snippet children(field)}
+      <div>
+        <label>Favorite Color</label>
+        <select
+          value={field.state.value}
+          on:blur={() => field.handleBlur()}
+          on:input={(e: Event) => {
+            const target = e.target as HTMLInputElement
+            field.handleChange(
+              target.value as '#FF0000' | '#00FF00' | '#0000FF',
+            )
+          }}
+        >
+          <option value="#FF0000">Red</option>
+          <option value="#00FF00">Green</option>
+          <option value="#0000FF">Blue</option>
+        </select>
+      </div>
+    {/snippet}
+  </form.Field>
+  <form.Field
+    name="employed"
+  >
+    {#snippet children(field)}
+      <div>
+        <label>Employed?</label>
+        <input
+          on:input={() => field.handleChange(!field.state.value)}
+          checked={field.state.value}
+          on:blur={() => field.handleBlur()}
+          id="employed"
+          type="checkbox"
+        />
+      </div>
+      {#if field.state.value}
+        <form.Field
+          name="jobTitle"
+          validators={{
+            onChange: ({ value }) =>
+              value.length === 0
+                ? 'Needs to have a job here'
+                : null,
+          }}
+        >
+          {#snippet children(field)}
+            <div>
+              <label>Job Title</label>
+              <input
+                type="text"
+                id="jobTitle"
+                placeholder="Job Title"
+                value={subField.state.value}
+                on:blur={() => subField.handleBlur()}
+                on:input={(e: Event) => {
+                  const target = e.target as HTMLInputElement
+                  subField.handleChange(target.value)
+                }}
+              />
+            </div>
+          {/snippet}
+        </form.Field>
+      {/if}
+    {/snippet}
+  </form.Field>
+  <div>
+    <button
+      type="submit"
+      disabled={form.api.state.isSubmitting}
+    >
+      {form.api.state.isSubmitting ? html` Submitting` : 'Submit'}
+    </button>
+    <button
+      type="button"
+      id="reset"
+      on:click={() => {
+        form.api.reset()
+      }}
+    >
+      Reset
+    </button>
+  </div>
+</form>
+<pre>{JSON.stringify(form.api.state, null, 2)}</pre>
diff --git a/packages/svelte-form/tests/simple.test.ts b/packages/svelte-form/tests/simple.test.ts
new file mode 100644
index 000000000..586643e51
--- /dev/null
+++ b/packages/svelte-form/tests/simple.test.ts
@@ -0,0 +1,76 @@
+/// <reference lib="dom" />
+import { afterEach, beforeEach, describe, expect, it } from 'vitest'
+import '@testing-library/jest-dom'
+import { userEvent } from '@testing-library/user-event'
+import { mount } from 'svelte'
+import TestForm from './simple'
+
+describe('Svelte Tests', () => {
+  let element: TestForm
+  beforeEach(async () => {
+    element = document.createElement('div')
+    document.body.appendChild(element)
+    mount(TestForm, {
+      target: element,
+      props: {},
+    })
+  })
+
+  afterEach(() => {
+    element.remove()
+  })
+
+  it('should have initial values', async () => {
+    expect(
+      await element.shadowRoot!.querySelector<HTMLInputElement>('#firstName'),
+    ).toHaveValue(sampleData.firstName)
+    expect(
+      await element.shadowRoot!.querySelector<HTMLInputElement>('#lastName'),
+    ).toHaveValue('')
+    const form = element.form!
+    expect(form.api.getFieldValue('firstName')).toBe('Bob')
+    expect(form.api.getFieldMeta('firstName')?.isTouched).toBeFalsy()
+    expect(form.api.getFieldValue('lastName')).toBe('')
+    expect(form.api.getFieldMeta('lastName')?.isTouched).toBeFalsy()
+  })
+  it('should mirror user input', async () => {
+    const lastName =
+      await element.shadowRoot!.querySelector<HTMLInputElement>('#lastName')!
+    const lastNameValue = 'Jobs'
+    await userEvent.type(lastName, lastNameValue)
+
+    const form = element.form!
+    expect(form.api.getFieldValue('lastName')).toBe(lastNameValue)
+    expect(form.api.getFieldMeta('lastName')?.isTouched).toBeTruthy()
+  })
+  it('Reset form to initial value', async () => {
+    const firstName =
+      await element.shadowRoot!.querySelector<HTMLInputElement>('#firstName')!
+    await userEvent.type(firstName, '-Joseph')
+
+    expect(firstName).toHaveValue('Christian-Joseph')
+
+    const form = element.form
+    await element
+      .shadowRoot!.querySelector<HTMLButtonElement>('#reset')
+      ?.click()
+    expect(form.api.getFieldValue('firstName')).toBe('Bob')
+  })
+
+  it('should display validation', async () => {
+    const lastName =
+      await element.shadowRoot!.querySelector<HTMLInputElement>('#lastName')!
+    const lastNameValue = 'Jo'
+    await userEvent.type(lastName, lastNameValue)
+    expect(lastName).toHaveValue('Jo')
+    const form = element.form
+    expect(form.api.getFieldMeta('lastName')?.errors[0]).toBe('Not long enough')
+
+    await userEvent.type(lastName, lastNameValue)
+
+    expect(await lastName.getAttribute('error-text')).toBeFalsy()
+    expect(form.api.getFieldValue('lastName')).toBe('JoJo')
+    expect(form.api.getFieldMeta('lastName')?.isTouched).toBeTruthy()
+    expect(form.api.getFieldMeta('lastName')?.errors.length).toBeFalsy()
+  })
+})
diff --git a/packages/svelte-form/tsconfig.build.json b/packages/svelte-form/tsconfig.build.json
new file mode 100644
index 000000000..25a44d38f
--- /dev/null
+++ b/packages/svelte-form/tsconfig.build.json
@@ -0,0 +1,12 @@
+{
+  "extends": "../../tsconfig.json",
+  "compilerOptions": {
+    "moduleResolution": "node16",
+    "rootDir": "./src",
+    "outDir": "./dist",
+    "noEmit": false,
+    "declaration": true,
+    "sourceMap": true
+  },
+  "include": ["src"]
+}
diff --git a/packages/svelte-form/tsconfig.docs.json b/packages/svelte-form/tsconfig.docs.json
new file mode 100644
index 000000000..2c9444e16
--- /dev/null
+++ b/packages/svelte-form/tsconfig.docs.json
@@ -0,0 +1,9 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "paths": {
+      "@tanstack/form-core": ["../form-core/src"]
+    }
+  },
+  "exclude": ["tests", "eslint.config.js", "vite.config.ts"]
+}
diff --git a/packages/svelte-form/tsconfig.json b/packages/svelte-form/tsconfig.json
new file mode 100644
index 000000000..c7651b3dc
--- /dev/null
+++ b/packages/svelte-form/tsconfig.json
@@ -0,0 +1,7 @@
+{
+  "extends": "../../tsconfig.json",
+  "compilerOptions": {
+    "moduleResolution": "node16"
+  },
+  "include": ["src", "tests", "eslint.config.js", "vite.config.ts"]
+}
diff --git a/packages/svelte-form/vite.config.ts b/packages/svelte-form/vite.config.ts
new file mode 100644
index 000000000..8de48919a
--- /dev/null
+++ b/packages/svelte-form/vite.config.ts
@@ -0,0 +1,14 @@
+import { defineConfig } from 'vitest/config'
+import packageJson from './package.json'
+
+export default defineConfig({
+  test: {
+    name: packageJson.name,
+    dir: './tests',
+    watch: false,
+    environment: 'jsdom',
+    globals: true,
+    coverage: { enabled: true, provider: 'istanbul', include: ['src/**/*'] },
+    typecheck: { enabled: true },
+  },
+})
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 3b5b642c9..15ea96f6b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1229,6 +1229,19 @@ importers:
         specifier: ^2.11.0
         version: 2.11.0(@testing-library/jest-dom@6.6.3)(solid-js@1.9.3)(vite@5.4.11(@types/node@22.10.1)(less@4.2.0)(sass@1.80.7)(sugarss@4.0.1(postcss@8.4.49))(terser@5.36.0))
 
+  packages/svelte-form:
+    dependencies:
+      '@tanstack/form-core':
+        specifier: workspace:*
+        version: link:../form-core
+    devDependencies:
+      premove:
+        specifier: ^4.0.0
+        version: 4.0.0
+      svelte:
+        specifier: ^5.16.1
+        version: 5.16.1
+
   packages/valibot-form-adapter:
     dependencies:
       '@tanstack/form-core':
@@ -5204,6 +5217,10 @@ packages:
   axios@1.7.7:
     resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==}
 
+  axobject-query@4.1.0:
+    resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
+    engines: {node: '>= 0.4'}
+
   b4a@1.6.7:
     resolution: {integrity: sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==}
 
@@ -6249,6 +6266,9 @@ packages:
       jiti:
         optional: true
 
+  esm-env@1.2.1:
+    resolution: {integrity: sha512-U9JedYYjCnadUlXk7e1Kr+aENQhtUaoaV9+gZm1T8LC/YBAPJx3NSPIAurFOC0U5vrdSevnUJS2/wUVxGwPhng==}
+
   espree@10.3.0:
     resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -6262,6 +6282,9 @@ packages:
     resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
     engines: {node: '>=0.10'}
 
+  esrap@1.3.2:
+    resolution: {integrity: sha512-C4PXusxYhFT98GjLSmb20k9PREuUdporer50dhzGuJu9IJXktbMddVCMLAERl5dAHyAi73GWWCE4FVHGP1794g==}
+
   esrecurse@4.3.0:
     resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
     engines: {node: '>=4.0'}
@@ -7467,6 +7490,9 @@ packages:
     resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==}
     engines: {node: '>=14'}
 
+  locate-character@3.0.0:
+    resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==}
+
   locate-path@5.0.0:
     resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
     engines: {node: '>=8'}
@@ -8597,6 +8623,11 @@ packages:
     resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
     engines: {node: '>= 0.8.0'}
 
+  premove@4.0.0:
+    resolution: {integrity: sha512-zim/Hr4+FVdCIM7zL9b9Z0Wfd5Ya3mnKtiuDv7L5lzYzanSq6cOcVJ7EFcgK4I0pt28l8H0jX/x3nyog380XgQ==}
+    engines: {node: '>=6'}
+    hasBin: true
+
   prettier@2.8.8:
     resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==}
     engines: {node: '>=10.13.0'}
@@ -9510,6 +9541,10 @@ packages:
     resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
     engines: {node: '>= 0.4'}
 
+  svelte@5.16.1:
+    resolution: {integrity: sha512-FsA1OjAKMAFSDob6j/Tv2ZV9rY4SeqPd1WXQlQkFkePAozSHLp6tbkU9qa1xJ+uTRzMSM2Vx3USdsYZBXd3H3g==}
+    engines: {node: '>=18'}
+
   symbol-observable@4.0.0:
     resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==}
     engines: {node: '>=0.10'}
@@ -10546,6 +10581,9 @@ packages:
   yup@1.5.0:
     resolution: {integrity: sha512-NJfBIHnp1QbqZwxcgl6irnDMIsb/7d1prNhFx02f1kp8h+orpi4xs3w90szNpOh68a/iHPdMsYvhZWoDmUvXBQ==}
 
+  zimmerframe@1.1.2:
+    resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==}
+
   zip-stream@6.0.1:
     resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==}
     engines: {node: '>= 14'}
@@ -14850,6 +14888,8 @@ snapshots:
     transitivePeerDependencies:
       - debug
 
+  axobject-query@4.1.0: {}
+
   b4a@1.6.7: {}
 
   babel-dead-code-elimination@1.0.6:
@@ -16155,6 +16195,8 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
+  esm-env@1.2.1: {}
+
   espree@10.3.0:
     dependencies:
       acorn: 8.14.0
@@ -16167,6 +16209,10 @@ snapshots:
     dependencies:
       estraverse: 5.3.0
 
+  esrap@1.3.2:
+    dependencies:
+      '@jridgewell/sourcemap-codec': 1.5.0
+
   esrecurse@4.3.0:
     dependencies:
       estraverse: 5.3.0
@@ -17508,6 +17554,8 @@ snapshots:
       mlly: 1.7.3
       pkg-types: 1.2.1
 
+  locate-character@3.0.0: {}
+
   locate-path@5.0.0:
     dependencies:
       p-locate: 4.1.0
@@ -19014,6 +19062,8 @@ snapshots:
 
   prelude-ls@1.2.1: {}
 
+  premove@4.0.0: {}
+
   prettier@2.8.8: {}
 
   prettier@3.4.2: {}
@@ -19981,6 +20031,23 @@ snapshots:
 
   supports-preserve-symlinks-flag@1.0.0: {}
 
+  svelte@5.16.1:
+    dependencies:
+      '@ampproject/remapping': 2.3.0
+      '@jridgewell/sourcemap-codec': 1.5.0
+      '@types/estree': 1.0.6
+      acorn: 8.14.0
+      acorn-typescript: 1.4.13(acorn@8.14.0)
+      aria-query: 5.3.2
+      axobject-query: 4.1.0
+      clsx: 2.1.1
+      esm-env: 1.2.1
+      esrap: 1.3.2
+      is-reference: 3.0.3
+      locate-character: 3.0.0
+      magic-string: 0.30.12
+      zimmerframe: 1.1.2
+
   symbol-observable@4.0.0: {}
 
   symbol-tree@3.2.4: {}
@@ -21112,6 +21179,8 @@ snapshots:
       toposort: 2.0.2
       type-fest: 2.19.0
 
+  zimmerframe@1.1.2: {}
+
   zip-stream@6.0.1:
     dependencies:
       archiver-utils: 5.0.2

From 4e53bdb0e6527889cb745e962d2749682690aa21 Mon Sep 17 00:00:00 2001
From: James Garbutt <43081j@users.noreply.github.com>
Date: Mon, 6 Jan 2025 11:08:22 +0000
Subject: [PATCH 02/17] wip: add vite and fix some types

---
 packages/svelte-form/package.json        |   3 +-
 packages/svelte-form/src/createField.ts  |  36 +++++++-
 packages/svelte-form/src/createForm.ts   |   6 +-
 packages/svelte-form/src/index.ts        |   2 +-
 packages/svelte-form/tsconfig.build.json |   2 +-
 packages/svelte-form/vite.config.ts      |   5 ++
 pnpm-lock.yaml                           | 106 ++++++++++++++++++-----
 7 files changed, 130 insertions(+), 30 deletions(-)

diff --git a/packages/svelte-form/package.json b/packages/svelte-form/package.json
index 892f427f0..7c0fddc92 100644
--- a/packages/svelte-form/package.json
+++ b/packages/svelte-form/package.json
@@ -48,7 +48,8 @@
   },
   "devDependencies": {
     "svelte": "^5.16.1",
-    "premove": "^4.0.0"
+    "premove": "^4.0.0",
+    "@sveltejs/vite-plugin-svelte": "^4.0.4"
   },
   "peerDependencies": {
     "svelte": "^5.16.1"
diff --git a/packages/svelte-form/src/createField.ts b/packages/svelte-form/src/createField.ts
index 21880ba66..adfff9f6a 100644
--- a/packages/svelte-form/src/createField.ts
+++ b/packages/svelte-form/src/createField.ts
@@ -1,11 +1,12 @@
 import { FieldApi } from '@tanstack/form-core'
 import { onDestroy, onMount } from 'svelte'
-import Field from './Field.js'
+import Field from './Field.svelte'
 
 import type {
   DeepKeys,
   DeepValue,
   FieldApiOptions,
+  Narrow,
   Validator,
 } from '@tanstack/form-core'
 
@@ -18,6 +19,37 @@ interface SvelteFieldApi<
   Field: Field<TParentData, TFormValidator>
 }
 
+export type CreateField<
+  TParentData,
+  TFormValidator extends
+    | Validator<TParentData, unknown>
+    | undefined = undefined,
+> = <
+  TName extends DeepKeys<TParentData>,
+  TFieldValidator extends
+    | Validator<DeepValue<TParentData, TName>, unknown>
+    | undefined = undefined,
+  TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
+>(
+  opts: () => { name: Narrow<TName> } & Omit<
+    FieldApiOptions<
+      TParentData,
+      TName,
+      TFieldValidator,
+      TFormValidator,
+      TData
+    >,
+    'form'
+  >,
+) => () => FieldApi<
+  TParentData,
+  TName,
+  TFieldValidator,
+  TFormValidator,
+  TData
+> &
+  SvelteFieldApi<TParentData, TFormValidator>
+
 export function createField<
   TParentData,
   TName extends DeepKeys<TParentData>,
@@ -64,5 +96,5 @@ export function createField<
     api.update(opts())
   })
 
-  return extendedApi
+  return () => extendedApi
 }
diff --git a/packages/svelte-form/src/createForm.ts b/packages/svelte-form/src/createForm.ts
index 3259ac141..6abce87ad 100644
--- a/packages/svelte-form/src/createForm.ts
+++ b/packages/svelte-form/src/createForm.ts
@@ -1,14 +1,16 @@
 import { FormApi } from '@tanstack/form-core'
 import { onMount } from 'svelte'
-import { Field, createField } from './createField'
+import { createField } from './createField.js'
+import Field from './Field.svelte';
 import type { FormOptions, Validator } from '@tanstack/form-core'
+import type { CreateField } from './createField.js';
 
 export interface SvelteFormApi<
   TFormData,
   TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,
 > {
   Field: Field<TFormData, TFormValidator>
-  createField: typeof createField<TFormData, TFormValidator>
+  createField: CreateField<TFormData, TFormValidator>
 }
 
 export function createForm<
diff --git a/packages/svelte-form/src/index.ts b/packages/svelte-form/src/index.ts
index 2f308b888..51faab167 100644
--- a/packages/svelte-form/src/index.ts
+++ b/packages/svelte-form/src/index.ts
@@ -2,5 +2,5 @@ export * from '@tanstack/form-core'
 
 export { createForm, type SvelteFormApi } from './createForm.js'
 
-export type { Field } from './Field.js'
+export type { Field } from './Field.svelte'
 export { createField } from './createField.js'
diff --git a/packages/svelte-form/tsconfig.build.json b/packages/svelte-form/tsconfig.build.json
index 25a44d38f..3832dfb1a 100644
--- a/packages/svelte-form/tsconfig.build.json
+++ b/packages/svelte-form/tsconfig.build.json
@@ -1,7 +1,7 @@
 {
   "extends": "../../tsconfig.json",
   "compilerOptions": {
-    "moduleResolution": "node16",
+    "moduleResolution": "bundler",
     "rootDir": "./src",
     "outDir": "./dist",
     "noEmit": false,
diff --git a/packages/svelte-form/vite.config.ts b/packages/svelte-form/vite.config.ts
index 8de48919a..3ec4909d6 100644
--- a/packages/svelte-form/vite.config.ts
+++ b/packages/svelte-form/vite.config.ts
@@ -1,4 +1,5 @@
 import { defineConfig } from 'vitest/config'
+import { svelte } from '@sveltejs/vite-plugin-svelte';
 import packageJson from './package.json'
 
 export default defineConfig({
@@ -11,4 +12,8 @@ export default defineConfig({
     coverage: { enabled: true, provider: 'istanbul', include: ['src/**/*'] },
     typecheck: { enabled: true },
   },
+  plugins: [
+    svelte({
+    })
+  ]
 })
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 15ea96f6b..12e353efd 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1235,6 +1235,9 @@ importers:
         specifier: workspace:*
         version: link:../form-core
     devDependencies:
+      '@sveltejs/vite-plugin-svelte':
+        specifier: ^4.0.4
+        version: 4.0.4(svelte@5.16.1)(vite@5.4.11(@types/node@22.10.1)(less@4.2.0)(sass@1.80.7)(sugarss@4.0.1(postcss@8.4.49))(terser@5.36.0))
       premove:
         specifier: ^4.0.0
         version: 4.0.0
@@ -4271,6 +4274,21 @@ packages:
     peerDependencies:
       eslint: '>=8.40.0'
 
+  '@sveltejs/vite-plugin-svelte-inspector@3.0.1':
+    resolution: {integrity: sha512-2CKypmj1sM4GE7HjllT7UKmo4Q6L5xFRd7VMGEWhYnZ+wc6AUVU01IBd7yUi6WnFndEwWoMNOd6e8UjoN0nbvQ==}
+    engines: {node: ^18.0.0 || ^20.0.0 || >=22}
+    peerDependencies:
+      '@sveltejs/vite-plugin-svelte': ^4.0.0-next.0||^4.0.0
+      svelte: ^5.0.0-next.96 || ^5.0.0
+      vite: ^5.0.0
+
+  '@sveltejs/vite-plugin-svelte@4.0.4':
+    resolution: {integrity: sha512-0ba1RQ/PHen5FGpdSrW7Y3fAMQjrXantECALeOiOdBdzR5+5vPP6HVZRLmZaQL+W8m++o+haIAKq5qT+MiZ7VA==}
+    engines: {node: ^18.0.0 || ^20.0.0 || >=22}
+    peerDependencies:
+      svelte: ^5.0.0-next.96 || ^5.0.0
+      vite: ^5.0.0
+
   '@swc/core-darwin-arm64@1.7.42':
     resolution: {integrity: sha512-fWhaCs2+8GDRIcjExVDEIfbptVrxDqG8oHkESnXgymmvqTWzWei5SOnPNMS8Q+MYsn/b++Y2bDxkcwmq35Bvxg==}
     engines: {node: '>=10'}
@@ -5810,6 +5828,15 @@ packages:
       supports-color:
         optional: true
 
+  debug@4.4.0:
+    resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
+    engines: {node: '>=6.0'}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+
   decimal.js@10.4.3:
     resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
 
@@ -7572,6 +7599,9 @@ packages:
   magic-string@0.30.12:
     resolution: {integrity: sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==}
 
+  magic-string@0.30.17:
+    resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
+
   magicast@0.2.11:
     resolution: {integrity: sha512-6saXbRDA1HMkqbsvHOU6HBjCVgZT460qheRkLhJQHWAbhXoWESI3Kn/dGGXyKs15FFKR85jsUqFx2sMK0wy/5g==}
 
@@ -10964,7 +10994,7 @@ snapshots:
       '@babel/core': 7.26.0
       '@babel/helper-compilation-targets': 7.25.9
       '@babel/helper-plugin-utils': 7.25.9
-      debug: 4.3.7
+      debug: 4.4.0
       lodash.debounce: 4.0.8
       resolve: 1.22.8
     transitivePeerDependencies:
@@ -12590,7 +12620,7 @@ snapshots:
 
   '@kwsites/file-exists@1.1.1':
     dependencies:
-      debug: 4.3.7
+      debug: 4.4.0
     transitivePeerDependencies:
       - supports-color
 
@@ -13355,7 +13385,7 @@ snapshots:
       estree-walker: 2.0.2
       glob: 8.1.0
       is-reference: 1.2.1
-      magic-string: 0.30.12
+      magic-string: 0.30.17
     optionalDependencies:
       rollup: 4.26.0
 
@@ -13363,7 +13393,7 @@ snapshots:
     dependencies:
       '@rollup/pluginutils': 5.1.3(rollup@4.26.0)
       estree-walker: 2.0.2
-      magic-string: 0.30.12
+      magic-string: 0.30.17
     optionalDependencies:
       rollup: 4.26.0
 
@@ -13386,7 +13416,7 @@ snapshots:
   '@rollup/plugin-replace@5.0.7(rollup@4.26.0)':
     dependencies:
       '@rollup/pluginutils': 5.1.3(rollup@4.26.0)
-      magic-string: 0.30.12
+      magic-string: 0.30.17
     optionalDependencies:
       rollup: 4.26.0
 
@@ -13578,6 +13608,28 @@ snapshots:
       eslint-visitor-keys: 4.2.0
       espree: 10.3.0
 
+  '@sveltejs/vite-plugin-svelte-inspector@3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.16.1)(vite@5.4.11(@types/node@22.10.1)(less@4.2.0)(sass@1.80.7)(sugarss@4.0.1(postcss@8.4.49))(terser@5.36.0)))(svelte@5.16.1)(vite@5.4.11(@types/node@22.10.1)(less@4.2.0)(sass@1.80.7)(sugarss@4.0.1(postcss@8.4.49))(terser@5.36.0))':
+    dependencies:
+      '@sveltejs/vite-plugin-svelte': 4.0.4(svelte@5.16.1)(vite@5.4.11(@types/node@22.10.1)(less@4.2.0)(sass@1.80.7)(sugarss@4.0.1(postcss@8.4.49))(terser@5.36.0))
+      debug: 4.4.0
+      svelte: 5.16.1
+      vite: 5.4.11(@types/node@22.10.1)(less@4.2.0)(sass@1.80.7)(sugarss@4.0.1(postcss@8.4.49))(terser@5.36.0)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.16.1)(vite@5.4.11(@types/node@22.10.1)(less@4.2.0)(sass@1.80.7)(sugarss@4.0.1(postcss@8.4.49))(terser@5.36.0))':
+    dependencies:
+      '@sveltejs/vite-plugin-svelte-inspector': 3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.16.1)(vite@5.4.11(@types/node@22.10.1)(less@4.2.0)(sass@1.80.7)(sugarss@4.0.1(postcss@8.4.49))(terser@5.36.0)))(svelte@5.16.1)(vite@5.4.11(@types/node@22.10.1)(less@4.2.0)(sass@1.80.7)(sugarss@4.0.1(postcss@8.4.49))(terser@5.36.0))
+      debug: 4.4.0
+      deepmerge: 4.3.1
+      kleur: 4.1.5
+      magic-string: 0.30.17
+      svelte: 5.16.1
+      vite: 5.4.11(@types/node@22.10.1)(less@4.2.0)(sass@1.80.7)(sugarss@4.0.1(postcss@8.4.49))(terser@5.36.0)
+      vitefu: 1.0.4(vite@5.4.11(@types/node@22.10.1)(less@4.2.0)(sass@1.80.7)(sugarss@4.0.1(postcss@8.4.49))(terser@5.36.0))
+    transitivePeerDependencies:
+      - supports-color
+
   '@swc/core-darwin-arm64@1.7.42':
     optional: true
 
@@ -14141,7 +14193,7 @@ snapshots:
       '@typescript-eslint/types': 8.17.0
       '@typescript-eslint/typescript-estree': 8.17.0(typescript@5.6.3)
       '@typescript-eslint/visitor-keys': 8.17.0
-      debug: 4.3.7
+      debug: 4.4.0
       eslint: 9.16.0(jiti@2.4.0)
     optionalDependencies:
       typescript: 5.6.3
@@ -14171,7 +14223,7 @@ snapshots:
     dependencies:
       '@typescript-eslint/types': 8.17.0
       '@typescript-eslint/visitor-keys': 8.17.0
-      debug: 4.3.7
+      debug: 4.4.0
       fast-glob: 3.3.2
       is-glob: 4.0.3
       minimatch: 9.0.5
@@ -14405,7 +14457,7 @@ snapshots:
     dependencies:
       '@vitest/spy': 2.1.4
       estree-walker: 3.0.3
-      magic-string: 0.30.12
+      magic-string: 0.30.17
     optionalDependencies:
       vite: 5.4.11(@types/node@22.10.1)(less@4.2.0)(sass@1.80.7)(sugarss@4.0.1(postcss@8.4.49))(terser@5.36.0)
 
@@ -14421,7 +14473,7 @@ snapshots:
   '@vitest/snapshot@2.1.4':
     dependencies:
       '@vitest/pretty-format': 2.1.4
-      magic-string: 0.30.12
+      magic-string: 0.30.17
       pathe: 1.1.2
 
   '@vitest/spy@2.1.4':
@@ -14686,13 +14738,13 @@ snapshots:
 
   agent-base@6.0.2:
     dependencies:
-      debug: 4.3.7
+      debug: 4.4.0
     transitivePeerDependencies:
       - supports-color
 
   agent-base@7.1.1:
     dependencies:
-      debug: 4.3.7
+      debug: 4.4.0
     transitivePeerDependencies:
       - supports-color
 
@@ -15523,6 +15575,10 @@ snapshots:
     dependencies:
       ms: 2.1.3
 
+  debug@4.4.0:
+    dependencies:
+      ms: 2.1.3
+
   decimal.js@10.4.3: {}
 
   decode-formdata@0.8.0: {}
@@ -16849,7 +16905,7 @@ snapshots:
   http-proxy-agent@7.0.2:
     dependencies:
       agent-base: 7.1.1
-      debug: 4.3.7
+      debug: 4.4.0
     transitivePeerDependencies:
       - supports-color
 
@@ -16889,14 +16945,14 @@ snapshots:
   https-proxy-agent@5.0.1:
     dependencies:
       agent-base: 6.0.2
-      debug: 4.3.7
+      debug: 4.4.0
     transitivePeerDependencies:
       - supports-color
 
   https-proxy-agent@7.0.5:
     dependencies:
       agent-base: 7.1.1
-      debug: 4.3.7
+      debug: 4.4.0
     transitivePeerDependencies:
       - supports-color
 
@@ -16985,7 +17041,7 @@ snapshots:
     dependencies:
       '@ioredis/commands': 1.2.0
       cluster-key-slot: 1.1.2
-      debug: 4.3.7
+      debug: 4.4.0
       denque: 2.1.0
       lodash.defaults: 4.2.0
       lodash.isarguments: 3.1.0
@@ -17628,6 +17684,10 @@ snapshots:
     dependencies:
       '@jridgewell/sourcemap-codec': 1.5.0
 
+  magic-string@0.30.17:
+    dependencies:
+      '@jridgewell/sourcemap-codec': 1.5.0
+
   magicast@0.2.11:
     dependencies:
       '@babel/parser': 7.26.2
@@ -18030,7 +18090,7 @@ snapshots:
   micromark@3.2.0:
     dependencies:
       '@types/debug': 4.1.12
-      debug: 4.3.7
+      debug: 4.4.0
       decode-named-character-reference: 1.0.2
       micromark-core-commonmark: 1.1.0
       micromark-factory-space: 1.1.0
@@ -19807,7 +19867,7 @@ snapshots:
   socks-proxy-agent@8.0.4:
     dependencies:
       agent-base: 7.1.1
-      debug: 4.3.7
+      debug: 4.4.0
       socks: 2.8.3
     transitivePeerDependencies:
       - supports-color
@@ -19869,7 +19929,7 @@ snapshots:
 
   spdy-transport@3.0.0:
     dependencies:
-      debug: 4.3.7
+      debug: 4.4.0
       detect-node: 2.1.0
       hpack.js: 2.1.6
       obuf: 1.1.2
@@ -19880,7 +19940,7 @@ snapshots:
 
   spdy@4.0.2:
     dependencies:
-      debug: 4.3.7
+      debug: 4.4.0
       handle-thing: 2.0.1
       http-deceiver: 1.2.7
       select-hose: 2.0.0
@@ -20235,7 +20295,7 @@ snapshots:
   tuf-js@3.0.1:
     dependencies:
       '@tufjs/models': 3.0.1
-      debug: 4.3.7
+      debug: 4.4.0
       make-fetch-happen: 14.0.3
     transitivePeerDependencies:
       - supports-color
@@ -20381,7 +20441,7 @@ snapshots:
       estree-walker: 3.0.3
       fast-glob: 3.3.2
       local-pkg: 0.5.1
-      magic-string: 0.30.12
+      magic-string: 0.30.17
       mlly: 1.7.3
       pathe: 1.1.2
       pkg-types: 1.2.1
@@ -20485,7 +20545,7 @@ snapshots:
   unwasm@0.3.9(webpack-sources@3.2.3):
     dependencies:
       knitwork: 1.1.0
-      magic-string: 0.30.12
+      magic-string: 0.30.17
       mlly: 1.7.3
       pathe: 1.1.2
       pkg-types: 1.2.1
@@ -20711,7 +20771,7 @@ snapshots:
   vite-node@2.1.4(@types/node@22.10.1)(less@4.2.0)(sass@1.80.7)(sugarss@4.0.1(postcss@8.4.49))(terser@5.36.0):
     dependencies:
       cac: 6.7.14
-      debug: 4.3.7
+      debug: 4.4.0
       pathe: 1.1.2
       vite: 5.4.11(@types/node@22.10.1)(less@4.2.0)(sass@1.80.7)(sugarss@4.0.1(postcss@8.4.49))(terser@5.36.0)
     transitivePeerDependencies:

From a105341e533b7986dffe3698e79705791f8db812 Mon Sep 17 00:00:00 2001
From: James Garbutt <43081j@users.noreply.github.com>
Date: Fri, 10 Jan 2025 10:45:16 +0000
Subject: [PATCH 03/17] fix: correct language attribute

---
 packages/svelte-form/src/Field.svelte    | 2 +-
 packages/svelte-form/tests/simple.svelte | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/svelte-form/src/Field.svelte b/packages/svelte-form/src/Field.svelte
index fa4194300..852f2ba67 100644
--- a/packages/svelte-form/src/Field.svelte
+++ b/packages/svelte-form/src/Field.svelte
@@ -1,5 +1,5 @@
 <!-- TODO (43081j): figure out how to reference types in generics -->
-<script type="ts" generics="TParentData,
+<script lang="ts" generics="TParentData,
   TName extends DeepKeys<TParentData>,
   TFieldValidator extends
     | Validator<DeepValue<TParentData, TName>, unknown>
diff --git a/packages/svelte-form/tests/simple.svelte b/packages/svelte-form/tests/simple.svelte
index 38a510ac1..dbc98cf90 100644
--- a/packages/svelte-form/tests/simple.svelte
+++ b/packages/svelte-form/tests/simple.svelte
@@ -1,4 +1,4 @@
-<script type="ts">
+<script lang="ts">
 import { createForm } from '../src/index.js'
 import type { FieldApi, FormOptions } from '../src/index.js'
 

From 3bd7f332482e4bbeaf46c72c2feba55adefe5a12 Mon Sep 17 00:00:00 2001
From: James Garbutt <43081j@users.noreply.github.com>
Date: Fri, 10 Jan 2025 10:58:39 +0000
Subject: [PATCH 04/17] chore: shuffle things around to use svelte files

---
 packages/svelte-form/index.html               | 20 +++++++++++++++++++
 packages/svelte-form/src/Field.svelte         |  5 +++--
 .../{createField.ts => createField.svelte.ts} |  0
 .../{createForm.ts => createForm.svelte.ts}   |  4 ++--
 packages/svelte-form/src/index.ts             |  4 ++--
 5 files changed, 27 insertions(+), 6 deletions(-)
 create mode 100644 packages/svelte-form/index.html
 rename packages/svelte-form/src/{createField.ts => createField.svelte.ts} (100%)
 rename packages/svelte-form/src/{createForm.ts => createForm.svelte.ts} (91%)

diff --git a/packages/svelte-form/index.html b/packages/svelte-form/index.html
new file mode 100644
index 000000000..5691a0f20
--- /dev/null
+++ b/packages/svelte-form/index.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>Svelte</title>
+  </head>
+  <body>
+  <div id="app"></div>
+  <script type="module">
+    import * as ts from './src/index.ts';
+    import { mount } from 'svelte';
+    import App from './tests/simple.svelte';
+
+    const app = mount(App, {
+      target: document.getElementById('app')
+    });
+  </script>
+  </body>
+</html>
diff --git a/packages/svelte-form/src/Field.svelte b/packages/svelte-form/src/Field.svelte
index 852f2ba67..dc3392eb8 100644
--- a/packages/svelte-form/src/Field.svelte
+++ b/packages/svelte-form/src/Field.svelte
@@ -11,7 +11,7 @@
 ">
   import type { Snippet } from 'svelte';
   // TODO (43081j): somehow remove this circular reference
-  import { createField } from './createField';
+  import { createField } from './createField.svelte.js';
 
   type Props = {
     children: Snippet<[
@@ -33,7 +33,8 @@
   >;
 
   let {
-    children
+    children,
+    ...fieldOptions
   }: Props = $props();
 
   const fieldApi = createField<
diff --git a/packages/svelte-form/src/createField.ts b/packages/svelte-form/src/createField.svelte.ts
similarity index 100%
rename from packages/svelte-form/src/createField.ts
rename to packages/svelte-form/src/createField.svelte.ts
diff --git a/packages/svelte-form/src/createForm.ts b/packages/svelte-form/src/createForm.svelte.ts
similarity index 91%
rename from packages/svelte-form/src/createForm.ts
rename to packages/svelte-form/src/createForm.svelte.ts
index 6abce87ad..633afad51 100644
--- a/packages/svelte-form/src/createForm.ts
+++ b/packages/svelte-form/src/createForm.svelte.ts
@@ -1,9 +1,9 @@
 import { FormApi } from '@tanstack/form-core'
 import { onMount } from 'svelte'
-import { createField } from './createField.js'
+import { createField } from './createField.svelte.js'
 import Field from './Field.svelte';
 import type { FormOptions, Validator } from '@tanstack/form-core'
-import type { CreateField } from './createField.js';
+import type { CreateField } from './createField.svelte.js';
 
 export interface SvelteFormApi<
   TFormData,
diff --git a/packages/svelte-form/src/index.ts b/packages/svelte-form/src/index.ts
index 51faab167..db0fbed4b 100644
--- a/packages/svelte-form/src/index.ts
+++ b/packages/svelte-form/src/index.ts
@@ -1,6 +1,6 @@
 export * from '@tanstack/form-core'
 
-export { createForm, type SvelteFormApi } from './createForm.js'
+export { createForm, type SvelteFormApi } from './createForm.svelte.js'
 
 export type { Field } from './Field.svelte'
-export { createField } from './createField.js'
+export { createField } from './createField.svelte.js'

From 5b87ca554045301c2c02761c7af74f1fd7e5f8f9 Mon Sep 17 00:00:00 2001
From: James Garbutt <43081j@users.noreply.github.com>
Date: Fri, 10 Jan 2025 11:27:57 +0000
Subject: [PATCH 05/17] fix: correct a whole bunch of bad refs/types

---
 packages/svelte-form/src/Field.svelte          |  4 ++--
 packages/svelte-form/src/createField.svelte.ts |  2 +-
 packages/svelte-form/tests/simple.svelte       | 12 ++++++++----
 3 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/packages/svelte-form/src/Field.svelte b/packages/svelte-form/src/Field.svelte
index dc3392eb8..45de85957 100644
--- a/packages/svelte-form/src/Field.svelte
+++ b/packages/svelte-form/src/Field.svelte
@@ -12,6 +12,7 @@
   import type { Snippet } from 'svelte';
   // TODO (43081j): somehow remove this circular reference
   import { createField } from './createField.svelte.js';
+  import type { FieldOptions } from '@tanstack/form-core';
 
   type Props = {
     children: Snippet<[
@@ -23,8 +24,7 @@
         TData
       >
     ]>
-    [key: string]: unknown;
-  } & FieldApiOptions<
+  } & FieldOptions<
     TParentData,
     TName,
     TFieldValidator,
diff --git a/packages/svelte-form/src/createField.svelte.ts b/packages/svelte-form/src/createField.svelte.ts
index adfff9f6a..7af842621 100644
--- a/packages/svelte-form/src/createField.svelte.ts
+++ b/packages/svelte-form/src/createField.svelte.ts
@@ -96,5 +96,5 @@ export function createField<
     api.update(opts())
   })
 
-  return () => extendedApi
+  return extendedApi
 }
diff --git a/packages/svelte-form/tests/simple.svelte b/packages/svelte-form/tests/simple.svelte
index dbc98cf90..29ba29405 100644
--- a/packages/svelte-form/tests/simple.svelte
+++ b/packages/svelte-form/tests/simple.svelte
@@ -41,6 +41,7 @@ const form = createForm(() => ({
 
   <form.Field
     name="firstName"
+    form={form}
     validators={{
       onChange: ({ value }) =>
         value.length < 3 ? 'Not long enough' : undefined,
@@ -65,6 +66,7 @@ const form = createForm(() => ({
   </form.Field>
   <form.Field
     name="lastName"
+    form={form}
     validators={{
       onChange: ({ value }) =>
         value.length < 3 ? 'Not long enough' : undefined,
@@ -89,6 +91,7 @@ const form = createForm(() => ({
   </form.Field>
   <form.Field
     name="color"
+    form={form}
   >
     {#snippet children(field)}
       <div>
@@ -112,6 +115,7 @@ const form = createForm(() => ({
   </form.Field>
   <form.Field
     name="employed"
+    form={form}
   >
     {#snippet children(field)}
       <div>
@@ -157,19 +161,19 @@ const form = createForm(() => ({
   <div>
     <button
       type="submit"
-      disabled={form.api.state.isSubmitting}
+      disabled={form.state.isSubmitting}
     >
-      {form.api.state.isSubmitting ? html` Submitting` : 'Submit'}
+      {form.state.isSubmitting ? html` Submitting` : 'Submit'}
     </button>
     <button
       type="button"
       id="reset"
       on:click={() => {
-        form.api.reset()
+        form.reset()
       }}
     >
       Reset
     </button>
   </div>
 </form>
-<pre>{JSON.stringify(form.api.state, null, 2)}</pre>
+<pre>{JSON.stringify(form.state, null, 2)}</pre>

From 43aa84cec07392ff5aa7d760799b429901a8dfe3 Mon Sep 17 00:00:00 2001
From: James Garbutt <43081j@users.noreply.github.com>
Date: Fri, 10 Jan 2025 14:35:04 +0000
Subject: [PATCH 06/17] fix: return FieldApi directly

---
 packages/svelte-form/src/createField.svelte.ts | 2 +-
 packages/svelte-form/src/createForm.svelte.ts  | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/packages/svelte-form/src/createField.svelte.ts b/packages/svelte-form/src/createField.svelte.ts
index 7af842621..0e25747d2 100644
--- a/packages/svelte-form/src/createField.svelte.ts
+++ b/packages/svelte-form/src/createField.svelte.ts
@@ -41,7 +41,7 @@ export type CreateField<
     >,
     'form'
   >,
-) => () => FieldApi<
+) => FieldApi<
   TParentData,
   TName,
   TFieldValidator,
diff --git a/packages/svelte-form/src/createForm.svelte.ts b/packages/svelte-form/src/createForm.svelte.ts
index 633afad51..e45a76c7d 100644
--- a/packages/svelte-form/src/createForm.svelte.ts
+++ b/packages/svelte-form/src/createForm.svelte.ts
@@ -29,6 +29,7 @@ export function createForm<
   // No clue right now how we do that
   extendedApi.Field = Field
   extendedApi.createField = (props) =>
+    // TODO (43081j): type is excessively deep.. no clue why yet
     createField(() => {
       return { ...props(), form: api }
     })

From 2b5208110f305ab2fa3f2b613bc9a914c8bca237 Mon Sep 17 00:00:00 2001
From: Lachlan Collins <1667261+lachlancollins@users.noreply.github.com>
Date: Mon, 13 Jan 2025 16:42:28 +1100
Subject: [PATCH 07/17] Build with @sveltejs/package

---
 .gitignore                        |  1 +
 packages/svelte-form/package.json | 20 +++++-----
 pnpm-lock.yaml                    | 65 ++++++++++++++++++++++++++++++-
 3 files changed, 74 insertions(+), 12 deletions(-)

diff --git a/.gitignore b/.gitignore
index d60197e0c..cc376bd6c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,6 +37,7 @@ stats.html
 .nx/workspace-data
 .pnpm-store
 .tsup
+.svelte-kit
 
 vite.config.js.timestamp-*
 vite.config.ts.timestamp-*
diff --git a/packages/svelte-form/package.json b/packages/svelte-form/package.json
index 7c0fddc92..08c7b7f8b 100644
--- a/packages/svelte-form/package.json
+++ b/packages/svelte-form/package.json
@@ -11,7 +11,7 @@
   },
   "homepage": "https://tanstack.com/form",
   "scripts": {
-    "clean": "premove ./build ./coverage",
+    "clean": "premove ./dist ./coverage",
     "test:eslint": "eslint ./src ./tests",
     "test:types": "pnpm run \"/^test:types:ts[0-9]{2}$/\"",
     "test:types:ts51": "node ../../node_modules/typescript51/lib/tsc.js",
@@ -23,18 +23,17 @@
     "test:lib": "vitest",
     "test:lib:dev": "pnpm run test:lib --watch",
     "test:build": "publint --strict",
-    "build": "tsc -p tsconfig.build.json"
+    "build": "svelte-package --input ./src --output ./dist"
   },
   "type": "module",
   "types": "dist/index.d.ts",
-  "main": "dist/index.js",
   "module": "dist/index.js",
+  "svelte": "./dist/index.js",
   "exports": {
     ".": {
-      "import": {
-        "types": "./dist/index.d.ts",
-        "default": "./dist/index.js"
-      }
+      "types": "./dist/index.d.ts",
+      "svelte": "./dist/index.js",
+      "import": "./dist/index.js"
     },
     "./package.json": "./package.json"
   },
@@ -47,11 +46,12 @@
     "@tanstack/form-core": "workspace:*"
   },
   "devDependencies": {
-    "svelte": "^5.16.1",
+    "@sveltejs/package": "^2.3.7",
+    "@sveltejs/vite-plugin-svelte": "^4.0.4",
     "premove": "^4.0.0",
-    "@sveltejs/vite-plugin-svelte": "^4.0.4"
+    "svelte": "^5.16.1"
   },
   "peerDependencies": {
-    "svelte": "^5.16.1"
+    "svelte": "^5.0.0"
   }
 }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 12e353efd..f34655404 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1235,6 +1235,9 @@ importers:
         specifier: workspace:*
         version: link:../form-core
     devDependencies:
+      '@sveltejs/package':
+        specifier: ^2.3.7
+        version: 2.3.7(svelte@5.16.1)(typescript@5.7.2)
       '@sveltejs/vite-plugin-svelte':
         specifier: ^4.0.4
         version: 4.0.4(svelte@5.16.1)(vite@5.4.11(@types/node@22.10.1)(less@4.2.0)(sass@1.80.7)(sugarss@4.0.1(postcss@8.4.49))(terser@5.36.0))
@@ -4274,6 +4277,13 @@ packages:
     peerDependencies:
       eslint: '>=8.40.0'
 
+  '@sveltejs/package@2.3.7':
+    resolution: {integrity: sha512-LYgUkde5GUYqOpXbcoCGUpEH4Ctl3Wj4u4CVZBl56dEeLW5fGHE037ZL1qlK0Ky+QD5uUfwONSeGwIOIighFMQ==}
+    engines: {node: ^16.14 || >=18}
+    hasBin: true
+    peerDependencies:
+      svelte: ^3.44.0 || ^4.0.0 || ^5.0.0-next.1
+
   '@sveltejs/vite-plugin-svelte-inspector@3.0.1':
     resolution: {integrity: sha512-2CKypmj1sM4GE7HjllT7UKmo4Q6L5xFRd7VMGEWhYnZ+wc6AUVU01IBd7yUi6WnFndEwWoMNOd6e8UjoN0nbvQ==}
     engines: {node: ^18.0.0 || ^20.0.0 || >=22}
@@ -5846,6 +5856,9 @@ packages:
   decode-named-character-reference@1.0.2:
     resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==}
 
+  dedent-js@1.0.1:
+    resolution: {integrity: sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ==}
+
   dedent@1.5.3:
     resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==}
     peerDependencies:
@@ -7568,6 +7581,9 @@ packages:
   loupe@3.1.2:
     resolution: {integrity: sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==}
 
+  lower-case@2.0.2:
+    resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
+
   lru-cache@10.4.3:
     resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
 
@@ -8064,6 +8080,9 @@ packages:
       xml2js:
         optional: true
 
+  no-case@3.0.4:
+    resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
+
   node-addon-api@6.1.0:
     resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==}
 
@@ -8424,6 +8443,9 @@ packages:
     resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
     engines: {node: '>= 0.8'}
 
+  pascal-case@3.1.2:
+    resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==}
+
   path-browserify@1.0.1:
     resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
 
@@ -9571,6 +9593,12 @@ packages:
     resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
     engines: {node: '>= 0.4'}
 
+  svelte2tsx@0.7.34:
+    resolution: {integrity: sha512-WTMhpNhFf8/h3SMtR5dkdSy2qfveomkhYei/QW9gSPccb0/b82tjHvLop6vT303ZkGswU/da1s6XvrLgthQPCw==}
+    peerDependencies:
+      svelte: ^3.55 || ^4.0.0-next.0 || ^4.0 || ^5.0.0-next.0
+      typescript: ^4.9.4 || ^5.0.0
+
   svelte@5.16.1:
     resolution: {integrity: sha512-FsA1OjAKMAFSDob6j/Tv2ZV9rY4SeqPd1WXQlQkFkePAozSHLp6tbkU9qa1xJ+uTRzMSM2Vx3USdsYZBXd3H3g==}
     engines: {node: '>=18'}
@@ -13608,6 +13636,17 @@ snapshots:
       eslint-visitor-keys: 4.2.0
       espree: 10.3.0
 
+  '@sveltejs/package@2.3.7(svelte@5.16.1)(typescript@5.7.2)':
+    dependencies:
+      chokidar: 4.0.1
+      kleur: 4.1.5
+      sade: 1.8.1
+      semver: 7.6.3
+      svelte: 5.16.1
+      svelte2tsx: 0.7.34(svelte@5.16.1)(typescript@5.7.2)
+    transitivePeerDependencies:
+      - typescript
+
   '@sveltejs/vite-plugin-svelte-inspector@3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.16.1)(vite@5.4.11(@types/node@22.10.1)(less@4.2.0)(sass@1.80.7)(sugarss@4.0.1(postcss@8.4.49))(terser@5.36.0)))(svelte@5.16.1)(vite@5.4.11(@types/node@22.10.1)(less@4.2.0)(sass@1.80.7)(sugarss@4.0.1(postcss@8.4.49))(terser@5.36.0))':
     dependencies:
       '@sveltejs/vite-plugin-svelte': 4.0.4(svelte@5.16.1)(vite@5.4.11(@types/node@22.10.1)(less@4.2.0)(sass@1.80.7)(sugarss@4.0.1(postcss@8.4.49))(terser@5.36.0))
@@ -15587,6 +15626,8 @@ snapshots:
     dependencies:
       character-entities: 2.0.2
 
+  dedent-js@1.0.1: {}
+
   dedent@1.5.3(babel-plugin-macros@3.1.0):
     optionalDependencies:
       babel-plugin-macros: 3.1.0
@@ -17657,6 +17698,10 @@ snapshots:
 
   loupe@3.1.2: {}
 
+  lower-case@2.0.2:
+    dependencies:
+      tslib: 2.8.1
+
   lru-cache@10.4.3: {}
 
   lru-cache@11.0.2: {}
@@ -18453,6 +18498,11 @@ snapshots:
       - uWebSockets.js
       - webpack-sources
 
+  no-case@3.0.4:
+    dependencies:
+      lower-case: 2.0.2
+      tslib: 2.8.1
+
   node-addon-api@6.1.0:
     optional: true
 
@@ -18921,6 +18971,11 @@ snapshots:
 
   parseurl@1.3.3: {}
 
+  pascal-case@3.1.2:
+    dependencies:
+      no-case: 3.0.4
+      tslib: 2.8.1
+
   path-browserify@1.0.1: {}
 
   path-exists@4.0.0: {}
@@ -20091,6 +20146,13 @@ snapshots:
 
   supports-preserve-symlinks-flag@1.0.0: {}
 
+  svelte2tsx@0.7.34(svelte@5.16.1)(typescript@5.7.2):
+    dependencies:
+      dedent-js: 1.0.1
+      pascal-case: 3.1.2
+      svelte: 5.16.1
+      typescript: 5.7.2
+
   svelte@5.16.1:
     dependencies:
       '@ampproject/remapping': 2.3.0
@@ -20372,8 +20434,7 @@ snapshots:
 
   typescript@5.6.3: {}
 
-  typescript@5.7.2:
-    optional: true
+  typescript@5.7.2: {}
 
   uc.micro@2.1.0: {}
 

From 2f0f6a1715351e11795c15a3487dde85603fbb0a Mon Sep 17 00:00:00 2001
From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com>
Date: Mon, 13 Jan 2025 05:44:24 +0000
Subject: [PATCH 08/17] ci: apply automated fixes and generate docs

---
 packages/svelte-form/index.html                | 18 +++++++++---------
 packages/svelte-form/src/createField.svelte.ts | 16 ++--------------
 packages/svelte-form/src/createForm.svelte.ts  |  4 ++--
 packages/svelte-form/vite.config.ts            |  7 ++-----
 4 files changed, 15 insertions(+), 30 deletions(-)

diff --git a/packages/svelte-form/index.html b/packages/svelte-form/index.html
index 5691a0f20..f8b7e0579 100644
--- a/packages/svelte-form/index.html
+++ b/packages/svelte-form/index.html
@@ -6,15 +6,15 @@
     <title>Svelte</title>
   </head>
   <body>
-  <div id="app"></div>
-  <script type="module">
-    import * as ts from './src/index.ts';
-    import { mount } from 'svelte';
-    import App from './tests/simple.svelte';
+    <div id="app"></div>
+    <script type="module">
+      import * as ts from './src/index.ts'
+      import { mount } from 'svelte'
+      import App from './tests/simple.svelte'
 
-    const app = mount(App, {
-      target: document.getElementById('app')
-    });
-  </script>
+      const app = mount(App, {
+        target: document.getElementById('app'),
+      })
+    </script>
   </body>
 </html>
diff --git a/packages/svelte-form/src/createField.svelte.ts b/packages/svelte-form/src/createField.svelte.ts
index 0e25747d2..75017799c 100644
--- a/packages/svelte-form/src/createField.svelte.ts
+++ b/packages/svelte-form/src/createField.svelte.ts
@@ -32,22 +32,10 @@ export type CreateField<
   TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
 >(
   opts: () => { name: Narrow<TName> } & Omit<
-    FieldApiOptions<
-      TParentData,
-      TName,
-      TFieldValidator,
-      TFormValidator,
-      TData
-    >,
+    FieldApiOptions<TParentData, TName, TFieldValidator, TFormValidator, TData>,
     'form'
   >,
-) => FieldApi<
-  TParentData,
-  TName,
-  TFieldValidator,
-  TFormValidator,
-  TData
-> &
+) => FieldApi<TParentData, TName, TFieldValidator, TFormValidator, TData> &
   SvelteFieldApi<TParentData, TFormValidator>
 
 export function createField<
diff --git a/packages/svelte-form/src/createForm.svelte.ts b/packages/svelte-form/src/createForm.svelte.ts
index e45a76c7d..fd97db98d 100644
--- a/packages/svelte-form/src/createForm.svelte.ts
+++ b/packages/svelte-form/src/createForm.svelte.ts
@@ -1,9 +1,9 @@
 import { FormApi } from '@tanstack/form-core'
 import { onMount } from 'svelte'
 import { createField } from './createField.svelte.js'
-import Field from './Field.svelte';
+import Field from './Field.svelte'
 import type { FormOptions, Validator } from '@tanstack/form-core'
-import type { CreateField } from './createField.svelte.js';
+import type { CreateField } from './createField.svelte.js'
 
 export interface SvelteFormApi<
   TFormData,
diff --git a/packages/svelte-form/vite.config.ts b/packages/svelte-form/vite.config.ts
index 3ec4909d6..59194a81b 100644
--- a/packages/svelte-form/vite.config.ts
+++ b/packages/svelte-form/vite.config.ts
@@ -1,5 +1,5 @@
 import { defineConfig } from 'vitest/config'
-import { svelte } from '@sveltejs/vite-plugin-svelte';
+import { svelte } from '@sveltejs/vite-plugin-svelte'
 import packageJson from './package.json'
 
 export default defineConfig({
@@ -12,8 +12,5 @@ export default defineConfig({
     coverage: { enabled: true, provider: 'istanbul', include: ['src/**/*'] },
     typecheck: { enabled: true },
   },
-  plugins: [
-    svelte({
-    })
-  ]
+  plugins: [svelte({})],
 })

From c2657de944b9324bc48fb406d8ffc3dab9cfdabb Mon Sep 17 00:00:00 2001
From: Simon Holthausen <simon.holthausen@vercel.com>
Date: Fri, 7 Mar 2025 20:51:51 +0100
Subject: [PATCH 09/17] implement svelte version

- one Field component with a module script with createField in it
- createForm file
- bunch of types that pass along generics, closely modeled after the other adapters
- tests
---
 packages/svelte-form/package.json             |  13 +-
 packages/svelte-form/src/Field.svelte         | 287 ++++++++++++--
 packages/svelte-form/src/Subscribe.svelte     |  16 +
 .../svelte-form/src/createField.svelte.ts     |  88 -----
 packages/svelte-form/src/createForm.svelte.ts | 240 +++++++++--
 packages/svelte-form/src/index.ts             |   8 +-
 packages/svelte-form/src/types.ts             | 374 ++++++++++++++++++
 packages/svelte-form/tests/simple.svelte      | 168 +++-----
 packages/svelte-form/tests/simple.test.ts     |  61 ++-
 packages/svelte-form/tsconfig.json            |   3 +-
 packages/svelte-form/vite.config.ts           |   6 +
 11 files changed, 956 insertions(+), 308 deletions(-)
 create mode 100644 packages/svelte-form/src/Subscribe.svelte
 delete mode 100644 packages/svelte-form/src/createField.svelte.ts
 create mode 100644 packages/svelte-form/src/types.ts

diff --git a/packages/svelte-form/package.json b/packages/svelte-form/package.json
index 08c7b7f8b..047b822b0 100644
--- a/packages/svelte-form/package.json
+++ b/packages/svelte-form/package.json
@@ -1,8 +1,8 @@
 {
   "name": "@tanstack/svelte-form",
-  "version": "0.0.1",
+  "version": "1.0.0",
   "description": "Powerful, type-safe forms for Svelte.",
-  "author": "James Garbutt (https://github.com/43081j)",
+  "author": "tannerlinsley",
   "license": "MIT",
   "repository": {
     "type": "git",
@@ -43,13 +43,14 @@
     "src"
   ],
   "dependencies": {
-    "@tanstack/form-core": "workspace:*"
+    "@tanstack/form-core": "workspace:*",
+    "@tanstack/svelte-store": "^0.7.0"
   },
   "devDependencies": {
-    "@sveltejs/package": "^2.3.7",
-    "@sveltejs/vite-plugin-svelte": "^4.0.4",
+    "@sveltejs/package": "^2.3.10",
+    "@sveltejs/vite-plugin-svelte": "^5.0.3",
     "premove": "^4.0.0",
-    "svelte": "^5.16.1"
+    "svelte": "^5.20.2"
   },
   "peerDependencies": {
     "svelte": "^5.0.0"
diff --git a/packages/svelte-form/src/Field.svelte b/packages/svelte-form/src/Field.svelte
index 45de85957..62032794c 100644
--- a/packages/svelte-form/src/Field.svelte
+++ b/packages/svelte-form/src/Field.svelte
@@ -1,48 +1,267 @@
-<!-- TODO (43081j): figure out how to reference types in generics -->
-<script lang="ts" generics="TParentData,
-  TName extends DeepKeys<TParentData>,
-  TFieldValidator extends
-    | Validator<DeepValue<TParentData, TName>, unknown>
-    | undefined = undefined,
-  TFormValidator extends
-    | Validator<TParentData, unknown>
-    | undefined = undefined,
-  TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>
-">
-  import type { Snippet } from 'svelte';
-  // TODO (43081j): somehow remove this circular reference
-  import { createField } from './createField.svelte.js';
-  import type { FieldOptions } from '@tanstack/form-core';
-
-  type Props = {
-    children: Snippet<[
-      FieldApi<
+<script module lang="ts">
+  import {
+    type DeepKeys,
+    type DeepValue,
+    FieldApi,
+    type FieldAsyncValidateOrFn,
+    type FieldValidateOrFn,
+    type FormAsyncValidateOrFn,
+    type FormValidateOrFn,
+  } from '@tanstack/form-core'
+  import { useStore } from '@tanstack/svelte-store'
+  import { onMount, type Snippet } from 'svelte'
+  import Field from './Field.svelte'
+  import type { CreateFieldOptions, SvelteFieldApi } from './types.js'
+
+  export function createField<
+    TParentData,
+    TName extends DeepKeys<TParentData>,
+    TData extends DeepValue<TParentData, TName>,
+    TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+    TOnChange extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+    TOnChangeAsync extends
+      | undefined
+      | FieldAsyncValidateOrFn<TParentData, TName, TData>,
+    TOnBlur extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+    TOnBlurAsync extends
+      | undefined
+      | FieldAsyncValidateOrFn<TParentData, TName, TData>,
+    TOnSubmit extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+    TOnSubmitAsync extends
+      | undefined
+      | FieldAsyncValidateOrFn<TParentData, TName, TData>,
+    TFormOnMount extends undefined | FormValidateOrFn<TParentData>,
+    TFormOnChange extends undefined | FormValidateOrFn<TParentData>,
+    TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+    TFormOnBlur extends undefined | FormValidateOrFn<TParentData>,
+    TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+    TFormOnSubmit extends undefined | FormValidateOrFn<TParentData>,
+    TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+    TFormOnServer extends undefined | FormAsyncValidateOrFn<TParentData>,
+    TParentSubmitMeta,
+  >(
+    opts: () => CreateFieldOptions<
+      TParentData,
+      TName,
+      TData,
+      TOnMount,
+      TOnChange,
+      TOnChangeAsync,
+      TOnBlur,
+      TOnBlurAsync,
+      TOnSubmit,
+      TOnSubmitAsync,
+      TFormOnMount,
+      TFormOnChange,
+      TFormOnChangeAsync,
+      TFormOnBlur,
+      TFormOnBlurAsync,
+      TFormOnSubmit,
+      TFormOnSubmitAsync,
+      TFormOnServer,
+      TParentSubmitMeta
+    >,
+  ) {
+    const options = opts()
+
+    const api = new FieldApi(options)
+
+    const extendedApi: typeof api &
+      SvelteFieldApi<
         TParentData,
-        TName,
-        TFieldValidator,
-        TFormValidator,
-        TData
-      >
-    ]>
-  } & FieldOptions<
+        TFormOnMount,
+        TFormOnChange,
+        TFormOnChangeAsync,
+        TFormOnBlur,
+        TFormOnBlurAsync,
+        TFormOnSubmit,
+        TFormOnSubmitAsync,
+        TFormOnServer,
+        TParentSubmitMeta
+      > = api as never
+
+    extendedApi.Field = Field as never
+
+    let mounted = false
+    // Instantiates field meta and removes it when unrendered
+    onMount(() => {
+      const cleanupFn = api.mount()
+      mounted = true
+      return () => {
+        cleanupFn()
+        mounted = false
+      }
+    })
+
+    // TODO (43081j): does this do what i think? we don't access anything
+    // svelte is aware of, so maybe it'll never call this?
+    $effect.pre(() => {
+      const current = opts()
+      Object.values(current) // make sure we're watching all the things
+      if (!mounted) return
+      api.update(current)
+    })
+
+    const storeSub = useStore(api.store)
+    Object.defineProperty(extendedApi, 'state', {
+      get() {
+        return storeSub.current
+      },
+    })
+
+    return extendedApi
+  }
+</script>
+
+<script
+  lang="ts"
+  generics="
+    TParentData,
+    TName extends DeepKeys<TParentData>,
+    TData extends DeepValue<TParentData, TName>,
+    TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+    TOnChange extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+    TOnChangeAsync extends
+      | undefined
+      | FieldAsyncValidateOrFn<TParentData, TName, TData>,
+    TOnBlur extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+    TOnBlurAsync extends
+      | undefined
+      | FieldAsyncValidateOrFn<TParentData, TName, TData>,
+    TOnSubmit extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+    TOnSubmitAsync extends
+      | undefined
+      | FieldAsyncValidateOrFn<TParentData, TName, TData>,
+    TFormOnMount extends undefined | FormValidateOrFn<TParentData>,
+    TFormOnChange extends undefined | FormValidateOrFn<TParentData>,
+    TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+    TFormOnBlur extends undefined | FormValidateOrFn<TParentData>,
+    TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+    TFormOnSubmit extends undefined | FormValidateOrFn<TParentData>,
+    TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+    TFormOnServer extends undefined | FormAsyncValidateOrFn<TParentData>,
+    TParentSubmitMeta,
+"
+>
+  type Props<
+    TParentData,
+    TName extends DeepKeys<TParentData>,
+    TData extends DeepValue<TParentData, TName>,
+    TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+    TOnChange extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+    TOnChangeAsync extends
+      | undefined
+      | FieldAsyncValidateOrFn<TParentData, TName, TData>,
+    TOnBlur extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+    TOnBlurAsync extends
+      | undefined
+      | FieldAsyncValidateOrFn<TParentData, TName, TData>,
+    TOnSubmit extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+    TOnSubmitAsync extends
+      | undefined
+      | FieldAsyncValidateOrFn<TParentData, TName, TData>,
+    TFormOnMount extends undefined | FormValidateOrFn<TParentData>,
+    TFormOnChange extends undefined | FormValidateOrFn<TParentData>,
+    TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+    TFormOnBlur extends undefined | FormValidateOrFn<TParentData>,
+    TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+    TFormOnSubmit extends undefined | FormValidateOrFn<TParentData>,
+    TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+    TFormOnServer extends undefined | FormAsyncValidateOrFn<TParentData>,
+    TParentSubmitMeta,
+  > = {
+    children: Snippet<
+      [
+        FieldApi<
+          TParentData,
+          TName,
+          TData,
+          TOnMount,
+          TOnChange,
+          TOnChangeAsync,
+          TOnBlur,
+          TOnBlurAsync,
+          TOnSubmit,
+          TOnSubmitAsync,
+          TFormOnMount,
+          TFormOnChange,
+          TFormOnChangeAsync,
+          TFormOnBlur,
+          TFormOnBlurAsync,
+          TFormOnSubmit,
+          TFormOnSubmitAsync,
+          TFormOnServer,
+          TParentSubmitMeta
+        >,
+      ]
+    >
+  } & CreateFieldOptions<
     TParentData,
     TName,
-    TFieldValidator,
-    TFormValidator,
-    TData
-  >;
+    TData,
+    TOnMount,
+    TOnChange,
+    TOnChangeAsync,
+    TOnBlur,
+    TOnBlurAsync,
+    TOnSubmit,
+    TOnSubmitAsync,
+    TFormOnMount,
+    TFormOnChange,
+    TFormOnChangeAsync,
+    TFormOnBlur,
+    TFormOnBlurAsync,
+    TFormOnSubmit,
+    TFormOnSubmitAsync,
+    TFormOnServer,
+    TParentSubmitMeta
+  >
 
   let {
     children,
     ...fieldOptions
-  }: Props = $props();
+  }: Props<
+    TParentData,
+    TName,
+    TData,
+    TOnMount,
+    TOnChange,
+    TOnChangeAsync,
+    TOnBlur,
+    TOnBlurAsync,
+    TOnSubmit,
+    TOnSubmitAsync,
+    TFormOnMount,
+    TFormOnChange,
+    TFormOnChangeAsync,
+    TFormOnBlur,
+    TFormOnBlurAsync,
+    TFormOnSubmit,
+    TFormOnSubmitAsync,
+    TFormOnServer,
+    TParentSubmitMeta
+  > = $props()
 
   const fieldApi = createField<
     TParentData,
     TName,
-    TFieldValidator,
-    TFormValidator,
-    TData
+    TData,
+    TOnMount,
+    TOnChange,
+    TOnChangeAsync,
+    TOnBlur,
+    TOnBlurAsync,
+    TOnSubmit,
+    TOnSubmitAsync,
+    TFormOnMount,
+    TFormOnChange,
+    TFormOnChangeAsync,
+    TFormOnBlur,
+    TFormOnBlurAsync,
+    TFormOnSubmit,
+    TFormOnSubmitAsync,
+    TFormOnServer,
+    TParentSubmitMeta
   >(() => {
     return fieldOptions
   })
diff --git a/packages/svelte-form/src/Subscribe.svelte b/packages/svelte-form/src/Subscribe.svelte
new file mode 100644
index 000000000..4f5f255a2
--- /dev/null
+++ b/packages/svelte-form/src/Subscribe.svelte
@@ -0,0 +1,16 @@
+<script lang="ts">
+  import { useStore } from '@tanstack/svelte-store'
+
+  // Don't bother typing this out, this component is only usable through a wrapper
+  interface Props {
+    children: any
+    store: any
+    selector?: (state: any) => any
+  }
+
+  let { children, store, selector = (state) => state }: Props = $props()
+
+  const value = useStore(store, selector)
+</script>
+
+{@render children(value.current)}
diff --git a/packages/svelte-form/src/createField.svelte.ts b/packages/svelte-form/src/createField.svelte.ts
deleted file mode 100644
index 75017799c..000000000
--- a/packages/svelte-form/src/createField.svelte.ts
+++ /dev/null
@@ -1,88 +0,0 @@
-import { FieldApi } from '@tanstack/form-core'
-import { onDestroy, onMount } from 'svelte'
-import Field from './Field.svelte'
-
-import type {
-  DeepKeys,
-  DeepValue,
-  FieldApiOptions,
-  Narrow,
-  Validator,
-} from '@tanstack/form-core'
-
-interface SvelteFieldApi<
-  TParentData,
-  TFormValidator extends
-    | Validator<TParentData, unknown>
-    | undefined = undefined,
-> {
-  Field: Field<TParentData, TFormValidator>
-}
-
-export type CreateField<
-  TParentData,
-  TFormValidator extends
-    | Validator<TParentData, unknown>
-    | undefined = undefined,
-> = <
-  TName extends DeepKeys<TParentData>,
-  TFieldValidator extends
-    | Validator<DeepValue<TParentData, TName>, unknown>
-    | undefined = undefined,
-  TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
->(
-  opts: () => { name: Narrow<TName> } & Omit<
-    FieldApiOptions<TParentData, TName, TFieldValidator, TFormValidator, TData>,
-    'form'
-  >,
-) => FieldApi<TParentData, TName, TFieldValidator, TFormValidator, TData> &
-  SvelteFieldApi<TParentData, TFormValidator>
-
-export function createField<
-  TParentData,
-  TName extends DeepKeys<TParentData>,
-  TFieldValidator extends
-    | Validator<DeepValue<TParentData, TName>, unknown>
-    | undefined = undefined,
-  TFormValidator extends
-    | Validator<TParentData, unknown>
-    | undefined = undefined,
-  TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
->(
-  opts: () => FieldApiOptions<
-    TParentData,
-    TName,
-    TFieldValidator,
-    TFormValidator,
-    TData
-  >,
-) {
-  const options = opts()
-
-  const api = new FieldApi(options)
-
-  const extendedApi: typeof api & SvelteFieldApi<TParentData, TFormValidator> =
-    api as never
-
-  extendedApi.Field = Field as never
-
-  let mounted = false
-  // Instantiates field meta and removes it when unrendered
-  onMount(() => {
-    const cleanupFn = api.mount()
-    mounted = true
-    onDestroy(() => {
-      cleanupFn()
-      mounted = false
-    })
-  })
-
-  // TODO (43081j): does this do what i think? we don't access anything
-  // svelte is aware of, so maybe it'll never call this?
-  $effect(() => {
-    if (!mounted) return
-    api.update(opts())
-  })
-
-  return extendedApi
-}
diff --git a/packages/svelte-form/src/createForm.svelte.ts b/packages/svelte-form/src/createForm.svelte.ts
index fd97db98d..e44c37a5b 100644
--- a/packages/svelte-form/src/createForm.svelte.ts
+++ b/packages/svelte-form/src/createForm.svelte.ts
@@ -1,44 +1,236 @@
 import { FormApi } from '@tanstack/form-core'
+import { useStore } from '@tanstack/svelte-store'
 import { onMount } from 'svelte'
-import { createField } from './createField.svelte.js'
-import Field from './Field.svelte'
-import type { FormOptions, Validator } from '@tanstack/form-core'
-import type { CreateField } from './createField.svelte.js'
+// @ts-ignore tsc doesn't know about named exports from svelte files
+import Field, { createField } from './Field.svelte'
+import Subscribe from './Subscribe.svelte'
+import type {
+  Component,
+  ComponentConstructorOptions,
+  Snippet,
+  SvelteComponent,
+} from 'svelte'
+import type {
+  FormAsyncValidateOrFn,
+  FormOptions,
+  FormState,
+  FormValidateOrFn,
+} from '@tanstack/form-core'
+import type { CreateField, FieldComponent, WithoutFunction } from './types.js'
 
 export interface SvelteFormApi<
-  TFormData,
-  TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,
+  TParentData,
+  TFormOnMount extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnChange extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TFormOnBlur extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TFormOnSubmit extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TFormOnServer extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TSubmitMeta,
 > {
-  Field: Field<TFormData, TFormValidator>
-  createField: CreateField<TFormData, TFormValidator>
+  Field: FieldComponent<
+    TParentData,
+    TFormOnMount,
+    TFormOnChange,
+    TFormOnChangeAsync,
+    TFormOnBlur,
+    TFormOnBlurAsync,
+    TFormOnSubmit,
+    TFormOnSubmitAsync,
+    TFormOnServer,
+    TSubmitMeta
+  >
+  createField: CreateField<
+    TParentData,
+    TFormOnMount,
+    TFormOnChange,
+    TFormOnChangeAsync,
+    TFormOnBlur,
+    TFormOnBlurAsync,
+    TFormOnSubmit,
+    TFormOnSubmitAsync,
+    TFormOnServer,
+    TSubmitMeta
+  >
+  useStore: <
+    TSelected = NoInfer<
+      FormState<
+        TParentData,
+        TFormOnMount,
+        TFormOnChange,
+        TFormOnChangeAsync,
+        TFormOnBlur,
+        TFormOnBlurAsync,
+        TFormOnSubmit,
+        TFormOnSubmitAsync,
+        TFormOnServer
+      >
+    >,
+  >(
+    selector?: (
+      state: NoInfer<
+        FormState<
+          TParentData,
+          TFormOnMount,
+          TFormOnChange,
+          TFormOnChangeAsync,
+          TFormOnBlur,
+          TFormOnBlurAsync,
+          TFormOnSubmit,
+          TFormOnSubmitAsync,
+          TFormOnServer
+        >
+      >,
+    ) => TSelected,
+  ) => { current: TSelected }
+  // This giant type allows the type
+  // - to be used as a function (which they are now in Svelte 5)
+  // - to be used as a class (which they were in Svelte 4, and which Svelte intellisense still uses for backwards compat)
+  // - to preserve the generics correctly
+  // Once Svelte intellisense no longer has/needs backwards compat, we can remove the class constructor part
+  Subscribe: (<
+    TSelected = NoInfer<
+      FormState<
+        TParentData,
+        TFormOnMount,
+        TFormOnChange,
+        TFormOnChangeAsync,
+        TFormOnBlur,
+        TFormOnBlurAsync,
+        TFormOnSubmit,
+        TFormOnSubmitAsync,
+        TFormOnServer
+      >
+    >,
+  >(
+    internal: any,
+    props: {
+      selector?: (
+        state: NoInfer<
+          FormState<
+            TParentData,
+            TFormOnMount,
+            TFormOnChange,
+            TFormOnChangeAsync,
+            TFormOnBlur,
+            TFormOnBlurAsync,
+            TFormOnSubmit,
+            TFormOnSubmitAsync,
+            TFormOnServer
+          >
+        >,
+      ) => TSelected
+      children: Snippet<[NoInfer<TSelected>]>
+    },
+  ) => {}) &
+    (new <
+      TSelected = NoInfer<
+        FormState<
+          TParentData,
+          TFormOnMount,
+          TFormOnChange,
+          TFormOnChangeAsync,
+          TFormOnBlur,
+          TFormOnBlurAsync,
+          TFormOnSubmit,
+          TFormOnSubmitAsync,
+          TFormOnServer
+        >
+      >,
+    >(
+      opts: ComponentConstructorOptions<{
+        selector?: (
+          state: NoInfer<
+            FormState<
+              TParentData,
+              TFormOnMount,
+              TFormOnChange,
+              TFormOnChangeAsync,
+              TFormOnBlur,
+              TFormOnBlurAsync,
+              TFormOnSubmit,
+              TFormOnSubmitAsync,
+              TFormOnServer
+            >
+          >,
+        ) => TSelected
+        children: Snippet<[NoInfer<TSelected>]>
+      }>,
+    ) => SvelteComponent) &
+    WithoutFunction<Component>
 }
 
 export function createForm<
   TParentData,
-  TFormValidator extends
-    | Validator<TParentData, unknown>
-    | undefined = undefined,
->(opts?: () => FormOptions<TParentData, TFormValidator>) {
+  TFormOnMount extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnChange extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TFormOnBlur extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TFormOnSubmit extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TFormOnServer extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TSubmitMeta,
+>(
+  opts?: () => FormOptions<
+    TParentData,
+    TFormOnMount,
+    TFormOnChange,
+    TFormOnChangeAsync,
+    TFormOnBlur,
+    TFormOnBlurAsync,
+    TFormOnSubmit,
+    TFormOnSubmitAsync,
+    TFormOnServer,
+    TSubmitMeta
+  >,
+) {
   const options = opts?.()
-  const api = new FormApi<TParentData, TFormValidator>(options)
-  const extendedApi: typeof api & SvelteFormApi<TParentData, TFormValidator> =
-    api as never
+  const api = new FormApi<
+    TParentData,
+    TFormOnMount,
+    TFormOnChange,
+    TFormOnChangeAsync,
+    TFormOnBlur,
+    TFormOnBlurAsync,
+    TFormOnSubmit,
+    TFormOnSubmitAsync,
+    TFormOnServer,
+    TSubmitMeta
+  >(options)
+  const extendedApi: typeof api &
+    SvelteFormApi<
+      TParentData,
+      TFormOnMount,
+      TFormOnChange,
+      TFormOnChangeAsync,
+      TFormOnBlur,
+      TFormOnBlurAsync,
+      TFormOnSubmit,
+      TFormOnSubmitAsync,
+      TFormOnServer,
+      TSubmitMeta
+    > = api as never
 
-  // TODO (43081j): somehow this needs to actually be
-  // `<Field ...props form={api}>`.
-  // No clue right now how we do that
-  extendedApi.Field = Field
+  // @ts-expect-error constructor definition exists only on a type level
+  extendedApi.Field = (internal, props) =>
+    Field(internal, { ...props, form: api })
   extendedApi.createField = (props) =>
-    // TODO (43081j): type is excessively deep.. no clue why yet
     createField(() => {
       return { ...props(), form: api }
-    })
+    }) as never
+  extendedApi.useStore = (selector) => useStore(api.store, selector)
+  // @ts-expect-error constructor definition exists only on a type level
+  extendedApi.Subscribe = (internal, props) =>
+    Subscribe(internal, { ...props, store: api.store })
 
   onMount(api.mount)
 
-  // TODO (43081j): does this actually work? we don't use any observed
-  // data, so maybe svelte won't re-run this effect?
-  $effect(() => api.update(opts?.()))
+  // formApi.update should not have any side effects. Think of it like a `useRef`
+  // that we need to keep updated every render with the most up-to-date information.
+  $effect.pre(() => api.update(opts?.()))
 
   return extendedApi
 }
diff --git a/packages/svelte-form/src/index.ts b/packages/svelte-form/src/index.ts
index db0fbed4b..0b24263e3 100644
--- a/packages/svelte-form/src/index.ts
+++ b/packages/svelte-form/src/index.ts
@@ -1,6 +1,10 @@
 export * from '@tanstack/form-core'
 
+export { useStore } from '@tanstack/svelte-store'
+
 export { createForm, type SvelteFormApi } from './createForm.svelte.js'
 
-export type { Field } from './Field.svelte'
-export { createField } from './createField.svelte.js'
+// @ts-ignore tsc doesn't know about named exports from svelte files
+export { default as Field, createField } from './Field.svelte'
+
+export type { CreateField, FieldComponent } from './types.js'
diff --git a/packages/svelte-form/src/types.ts b/packages/svelte-form/src/types.ts
new file mode 100644
index 000000000..6b4ad3133
--- /dev/null
+++ b/packages/svelte-form/src/types.ts
@@ -0,0 +1,374 @@
+import type {
+  DeepKeys,
+  DeepValue,
+  FieldApi,
+  FieldApiOptions,
+  FieldAsyncValidateOrFn,
+  FieldValidateOrFn,
+  FormAsyncValidateOrFn,
+  FormValidateOrFn,
+  Narrow,
+} from '@tanstack/form-core'
+import type {
+  Component,
+  ComponentConstructorOptions,
+  Snippet,
+  SvelteComponent,
+} from 'svelte'
+
+export type CreateFieldOptions<
+  TParentData,
+  TName extends DeepKeys<TParentData>,
+  TData extends DeepValue<TParentData, TName>,
+  TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+  TOnChange extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+  TOnChangeAsync extends
+    | undefined
+    | FieldAsyncValidateOrFn<TParentData, TName, TData>,
+  TOnBlur extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+  TOnBlurAsync extends
+    | undefined
+    | FieldAsyncValidateOrFn<TParentData, TName, TData>,
+  TOnSubmit extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+  TOnSubmitAsync extends
+    | undefined
+    | FieldAsyncValidateOrFn<TParentData, TName, TData>,
+  TFormOnMount extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnChange extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TFormOnBlur extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TFormOnSubmit extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TFormOnServer extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TSubmitMeta,
+> = FieldApiOptions<
+  TParentData,
+  TName,
+  TData,
+  TOnMount,
+  TOnChange,
+  TOnChangeAsync,
+  TOnBlur,
+  TOnBlurAsync,
+  TOnSubmit,
+  TOnSubmitAsync,
+  TFormOnMount,
+  TFormOnChange,
+  TFormOnChangeAsync,
+  TFormOnBlur,
+  TFormOnBlurAsync,
+  TFormOnSubmit,
+  TFormOnSubmitAsync,
+  TFormOnServer,
+  TSubmitMeta
+> & {
+  mode?: 'value' | 'array'
+}
+
+export type WithoutFunction<T> = {
+  [K in keyof T as T[K] extends Function ? never : K]: T[K]
+}
+
+export interface SvelteFieldApi<
+  TParentData,
+  TFormOnMount extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnChange extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TFormOnBlur extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TFormOnSubmit extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TFormOnServer extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TParentSubmitMeta,
+> {
+  Field: FieldComponent<
+    TParentData,
+    TFormOnMount,
+    TFormOnChange,
+    TFormOnChangeAsync,
+    TFormOnBlur,
+    TFormOnBlurAsync,
+    TFormOnSubmit,
+    TFormOnSubmitAsync,
+    TFormOnServer,
+    TParentSubmitMeta
+  >
+}
+
+export type FieldComponent<
+  TParentData,
+  TFormOnMount extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnChange extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TFormOnBlur extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TFormOnSubmit extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TFormOnServer extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TParentSubmitMeta,
+> =
+  // This giant type allows the type
+  // - to be used as a function (which they are now in Svelte 5)
+  // - to be used as a class (which they were in Svelte 4, and which Svelte intellisense still uses for backwards compat)
+  // - to preserve the generics correctly
+  // Once Svelte intellisense no longer has/needs backwards compat, we can remove the class constructor part
+  (<
+    TName extends DeepKeys<TParentData>,
+    TData extends DeepValue<TParentData, TName>,
+    TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+    TOnChange extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+    TOnChangeAsync extends
+      | undefined
+      | FieldAsyncValidateOrFn<TParentData, TName, TData>,
+    TOnBlur extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+    TOnBlurAsync extends
+      | undefined
+      | FieldAsyncValidateOrFn<TParentData, TName, TData>,
+    TOnSubmit extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+    TOnSubmitAsync extends
+      | undefined
+      | FieldAsyncValidateOrFn<TParentData, TName, TData>,
+  >(
+    internal: any,
+    {
+      children,
+      ...fieldOptions
+    }: Omit<
+      FieldComponentProps<
+        TParentData,
+        TName,
+        TData,
+        TOnMount,
+        TOnChange,
+        TOnChangeAsync,
+        TOnBlur,
+        TOnBlurAsync,
+        TOnSubmit,
+        TOnSubmitAsync,
+        TFormOnMount,
+        TFormOnChange,
+        TFormOnChangeAsync,
+        TFormOnBlur,
+        TFormOnBlurAsync,
+        TFormOnSubmit,
+        TFormOnSubmitAsync,
+        TFormOnServer,
+        TParentSubmitMeta
+      >,
+      'form'
+    >,
+  ) => {}) &
+    (new <
+      TName extends DeepKeys<TParentData>,
+      TData extends DeepValue<TParentData, TName>,
+      TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+      TOnChange extends
+        | undefined
+        | FieldValidateOrFn<TParentData, TName, TData>,
+      TOnChangeAsync extends
+        | undefined
+        | FieldAsyncValidateOrFn<TParentData, TName, TData>,
+      TOnBlur extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+      TOnBlurAsync extends
+        | undefined
+        | FieldAsyncValidateOrFn<TParentData, TName, TData>,
+      TOnSubmit extends
+        | undefined
+        | FieldValidateOrFn<TParentData, TName, TData>,
+      TOnSubmitAsync extends
+        | undefined
+        | FieldAsyncValidateOrFn<TParentData, TName, TData>,
+    >(
+      opts: ComponentConstructorOptions<
+        Omit<
+          FieldComponentProps<
+            TParentData,
+            TName,
+            TData,
+            TOnMount,
+            TOnChange,
+            TOnChangeAsync,
+            TOnBlur,
+            TOnBlurAsync,
+            TOnSubmit,
+            TOnSubmitAsync,
+            TFormOnMount,
+            TFormOnChange,
+            TFormOnChangeAsync,
+            TFormOnBlur,
+            TFormOnBlurAsync,
+            TFormOnSubmit,
+            TFormOnSubmitAsync,
+            TFormOnServer,
+            TParentSubmitMeta
+          >,
+          'form'
+        >
+      >,
+    ) => SvelteComponent) &
+    WithoutFunction<Component>
+
+type FieldComponentProps<
+  TParentData,
+  TName extends DeepKeys<TParentData>,
+  TData extends DeepValue<TParentData, TName>,
+  TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+  TOnChange extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+  TOnChangeAsync extends
+    | undefined
+    | FieldAsyncValidateOrFn<TParentData, TName, TData>,
+  TOnBlur extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+  TOnBlurAsync extends
+    | undefined
+    | FieldAsyncValidateOrFn<TParentData, TName, TData>,
+  TOnSubmit extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+  TOnSubmitAsync extends
+    | undefined
+    | FieldAsyncValidateOrFn<TParentData, TName, TData>,
+  TFormOnMount extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnChange extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TFormOnBlur extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TFormOnSubmit extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TFormOnServer extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TParentSubmitMeta,
+> = {
+  children: Snippet<
+    [
+      FieldApi<
+        TParentData,
+        TName,
+        TData,
+        TOnMount,
+        TOnChange,
+        TOnChangeAsync,
+        TOnBlur,
+        TOnBlurAsync,
+        TOnSubmit,
+        TOnSubmitAsync,
+        TFormOnMount,
+        TFormOnChange,
+        TFormOnChangeAsync,
+        TFormOnBlur,
+        TFormOnBlurAsync,
+        TFormOnSubmit,
+        TFormOnSubmitAsync,
+        TFormOnServer,
+        TParentSubmitMeta
+      >,
+    ]
+  >
+} & Omit<
+  CreateFieldOptions<
+    TParentData,
+    TName,
+    TData,
+    TOnMount,
+    TOnChange,
+    TOnChangeAsync,
+    TOnBlur,
+    TOnBlurAsync,
+    TOnSubmit,
+    TOnSubmitAsync,
+    TFormOnMount,
+    TFormOnChange,
+    TFormOnChangeAsync,
+    TFormOnBlur,
+    TFormOnBlurAsync,
+    TFormOnSubmit,
+    TFormOnSubmitAsync,
+    TFormOnServer,
+    TParentSubmitMeta
+  >,
+  'form'
+>
+
+export type CreateField<
+  TParentData,
+  TFormOnMount extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnChange extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TFormOnBlur extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TFormOnSubmit extends undefined | FormValidateOrFn<TParentData>,
+  TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TFormOnServer extends undefined | FormAsyncValidateOrFn<TParentData>,
+  TParentSubmitMeta,
+> = <
+  TName extends DeepKeys<TParentData>,
+  TData extends DeepValue<TParentData, TName>,
+  TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+  TOnChange extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+  TOnChangeAsync extends
+    | undefined
+    | FieldAsyncValidateOrFn<TParentData, TName, TData>,
+  TOnBlur extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+  TOnBlurAsync extends
+    | undefined
+    | FieldAsyncValidateOrFn<TParentData, TName, TData>,
+  TOnSubmit extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
+  TOnSubmitAsync extends
+    | undefined
+    | FieldAsyncValidateOrFn<TParentData, TName, TData>,
+  TSubmitMeta,
+>(
+  opts: () => { name: Narrow<TName> } & Omit<
+    CreateFieldOptions<
+      TParentData,
+      TName,
+      TData,
+      TOnMount,
+      TOnChange,
+      TOnChangeAsync,
+      TOnBlur,
+      TOnBlurAsync,
+      TOnSubmit,
+      TOnSubmitAsync,
+      TFormOnMount,
+      TFormOnChange,
+      TFormOnChangeAsync,
+      TFormOnBlur,
+      TFormOnBlurAsync,
+      TFormOnSubmit,
+      TFormOnSubmitAsync,
+      TFormOnServer,
+      TSubmitMeta
+    >,
+    'form'
+  >,
+) => () => FieldApi<
+  TParentData,
+  TName,
+  TData,
+  TOnMount,
+  TOnChange,
+  TOnChangeAsync,
+  TOnBlur,
+  TOnBlurAsync,
+  TOnSubmit,
+  TOnSubmitAsync,
+  TFormOnMount,
+  TFormOnChange,
+  TFormOnChangeAsync,
+  TFormOnBlur,
+  TFormOnBlurAsync,
+  TFormOnSubmit,
+  TFormOnSubmitAsync,
+  TFormOnServer,
+  TParentSubmitMeta
+> &
+  SvelteFieldApi<
+    TParentData,
+    TFormOnMount,
+    TFormOnChange,
+    TFormOnChangeAsync,
+    TFormOnBlur,
+    TFormOnBlurAsync,
+    TFormOnSubmit,
+    TFormOnSubmitAsync,
+    TFormOnServer,
+    TParentSubmitMeta
+  >
diff --git a/packages/svelte-form/tests/simple.svelte b/packages/svelte-form/tests/simple.svelte
index 29ba29405..c48080ce5 100644
--- a/packages/svelte-form/tests/simple.svelte
+++ b/packages/svelte-form/tests/simple.svelte
@@ -1,47 +1,41 @@
-<script lang="ts">
-import { createForm } from '../src/index.js'
-import type { FieldApi, FormOptions } from '../src/index.js'
-
-interface Employee {
-  firstName: string
-  lastName: string
-  color?: '#FF0000' | '#00FF00' | '#0000FF'
-  employed: boolean
-  jobTitle: string
-}
+<script module lang="ts">
+  interface Employee {
+    firstName: string
+    lastName: string
+  }
 
-const sampleData: Employee = {
-  firstName: 'Bob',
-  lastName: '',
-  employed: false,
-  jobTitle: '',
-}
+  export const sampleData: Employee = {
+    firstName: 'Christian',
+    lastName: '',
+  }
+</script>
 
-const formConfig: FormOptions<Employee, undefined> = {
-  defaultValues: sampleData,
-}
+<script lang="ts">
+  import { createForm } from '@tanstack/svelte-form'
 
-const form = createForm(() => ({
-  defaultValues: {
-    firstName: '',
-    lastName: '',
-  },
-  onSubmit: async ({ value }) => {
-    // Do something with form data
-    console.log(value)
-  },
-}))
+  const form = createForm(() => ({
+    defaultValues: sampleData,
+    onSubmit: async ({ value }) => {
+      // Do something with form data
+      console.log(JSON.stringify(value))
+    },
+  }))
 
+  const state = form.useStore()
 </script>
+
 <form
   id="form"
-  on:submit|preventDefault={form.onSubmit}
+  onsubmit={(e) => {
+    e.preventDefault()
+    e.stopPropagation()
+    form.handleSubmit()
+  }}
 >
   <h1>TanStack Form - Svelte Demo</h1>
 
   <form.Field
     name="firstName"
-    form={form}
     validators={{
       onChange: ({ value }) =>
         value.length < 3 ? 'Not long enough' : undefined,
@@ -49,14 +43,14 @@ const form = createForm(() => ({
   >
     {#snippet children(field)}
       <div>
-        <label>First Name</label>
+        <label for={field.name}>First Name</label>
         <input
-          id="firstName"
+          id={field.name}
           type="text"
           placeholder="First Name"
           value={field.state.value}
-          on:blur={() => field.handleBlur()}
-          on:input={(e: Event) => {
+          onblur={() => field.handleBlur()}
+          oninput={(e: Event) => {
             const target = e.target as HTMLInputElement
             field.handleChange(target.value)
           }}
@@ -66,7 +60,6 @@ const form = createForm(() => ({
   </form.Field>
   <form.Field
     name="lastName"
-    form={form}
     validators={{
       onChange: ({ value }) =>
         value.length < 3 ? 'Not long enough' : undefined,
@@ -74,101 +67,41 @@ const form = createForm(() => ({
   >
     {#snippet children(field)}
       <div>
-        <label>Last Name</label>
+        <label for={field.name}>Last Name</label>
         <input
-          id="lastName"
+          id={field.name}
           type="text"
           placeholder="Last Name"
           value={field.state.value}
-          on:blur={() => field.handleBlur()}
-          on:input={(e: Event) => {
+          onblur={() => field.handleBlur()}
+          oninput={(e: Event) => {
             const target = e.target as HTMLInputElement
             field.handleChange(target.value)
           }}
         />
+        {#each field.state.meta.errors as error}
+          <em>{error}</em>
+        {/each}
       </div>
     {/snippet}
   </form.Field>
-  <form.Field
-    name="color"
-    form={form}
-  >
-    {#snippet children(field)}
-      <div>
-        <label>Favorite Color</label>
-        <select
-          value={field.state.value}
-          on:blur={() => field.handleBlur()}
-          on:input={(e: Event) => {
-            const target = e.target as HTMLInputElement
-            field.handleChange(
-              target.value as '#FF0000' | '#00FF00' | '#0000FF',
-            )
-          }}
-        >
-          <option value="#FF0000">Red</option>
-          <option value="#00FF00">Green</option>
-          <option value="#0000FF">Blue</option>
-        </select>
-      </div>
-    {/snippet}
-  </form.Field>
-  <form.Field
-    name="employed"
-    form={form}
-  >
-    {#snippet children(field)}
-      <div>
-        <label>Employed?</label>
-        <input
-          on:input={() => field.handleChange(!field.state.value)}
-          checked={field.state.value}
-          on:blur={() => field.handleBlur()}
-          id="employed"
-          type="checkbox"
-        />
-      </div>
-      {#if field.state.value}
-        <form.Field
-          name="jobTitle"
-          validators={{
-            onChange: ({ value }) =>
-              value.length === 0
-                ? 'Needs to have a job here'
-                : null,
-          }}
-        >
-          {#snippet children(field)}
-            <div>
-              <label>Job Title</label>
-              <input
-                type="text"
-                id="jobTitle"
-                placeholder="Job Title"
-                value={subField.state.value}
-                on:blur={() => subField.handleBlur()}
-                on:input={(e: Event) => {
-                  const target = e.target as HTMLInputElement
-                  subField.handleChange(target.value)
-                }}
-              />
-            </div>
-          {/snippet}
-        </form.Field>
-      {/if}
-    {/snippet}
-  </form.Field>
   <div>
-    <button
-      type="submit"
-      disabled={form.state.isSubmitting}
+    <form.Subscribe
+      selector={(state) => ({
+        canSubmit: state.canSubmit,
+        isSubmitting: state.isSubmitting,
+      })}
     >
-      {form.state.isSubmitting ? html` Submitting` : 'Submit'}
-    </button>
+      {#snippet children({ canSubmit, isSubmitting })}
+        <button type="submit" disabled={!canSubmit}>
+          {isSubmitting ? 'Submitting' : 'Submit'}
+        </button>
+      {/snippet}
+    </form.Subscribe>
     <button
       type="button"
       id="reset"
-      on:click={() => {
+      onclick={() => {
         form.reset()
       }}
     >
@@ -176,4 +109,5 @@ const form = createForm(() => ({
     </button>
   </div>
 </form>
-<pre>{JSON.stringify(form.state, null, 2)}</pre>
+
+<pre>{JSON.stringify(state.current, null, 2)}</pre>
diff --git a/packages/svelte-form/tests/simple.test.ts b/packages/svelte-form/tests/simple.test.ts
index 586643e51..3fa23d8c6 100644
--- a/packages/svelte-form/tests/simple.test.ts
+++ b/packages/svelte-form/tests/simple.test.ts
@@ -2,75 +2,64 @@
 import { afterEach, beforeEach, describe, expect, it } from 'vitest'
 import '@testing-library/jest-dom'
 import { userEvent } from '@testing-library/user-event'
-import { mount } from 'svelte'
-import TestForm from './simple'
+import { mount, unmount } from 'svelte'
+// @ts-ignore tsc doesn't know about named exports from svelte files
+import TestForm, { sampleData } from './simple.svelte'
 
 describe('Svelte Tests', () => {
-  let element: TestForm
+  let element: HTMLDivElement
+  let instance: any
   beforeEach(async () => {
     element = document.createElement('div')
     document.body.appendChild(element)
-    mount(TestForm, {
+    instance = mount(TestForm, {
       target: element,
-      props: {},
     })
   })
 
   afterEach(() => {
+    unmount(instance)
     element.remove()
   })
 
   it('should have initial values', async () => {
-    expect(
-      await element.shadowRoot!.querySelector<HTMLInputElement>('#firstName'),
-    ).toHaveValue(sampleData.firstName)
-    expect(
-      await element.shadowRoot!.querySelector<HTMLInputElement>('#lastName'),
-    ).toHaveValue('')
-    const form = element.form!
-    expect(form.api.getFieldValue('firstName')).toBe('Bob')
-    expect(form.api.getFieldMeta('firstName')?.isTouched).toBeFalsy()
-    expect(form.api.getFieldValue('lastName')).toBe('')
-    expect(form.api.getFieldMeta('lastName')?.isTouched).toBeFalsy()
+    expect(element.querySelector<HTMLInputElement>('#firstName')).toHaveValue(
+      sampleData.firstName,
+    )
+    expect(element.querySelector<HTMLInputElement>('#lastName')).toHaveValue(
+      sampleData.lastName,
+    )
   })
+
   it('should mirror user input', async () => {
-    const lastName =
-      await element.shadowRoot!.querySelector<HTMLInputElement>('#lastName')!
+    const lastName = element.querySelector<HTMLInputElement>('#lastName')!
     const lastNameValue = 'Jobs'
     await userEvent.type(lastName, lastNameValue)
 
-    const form = element.form!
-    expect(form.api.getFieldValue('lastName')).toBe(lastNameValue)
-    expect(form.api.getFieldMeta('lastName')?.isTouched).toBeTruthy()
+    const form = JSON.parse(element.querySelector('pre')!.textContent!)
+    expect(form.values.lastName).toBe(lastNameValue)
   })
+
   it('Reset form to initial value', async () => {
-    const firstName =
-      await element.shadowRoot!.querySelector<HTMLInputElement>('#firstName')!
+    const firstName = element.querySelector<HTMLInputElement>('#firstName')!
     await userEvent.type(firstName, '-Joseph')
 
-    expect(firstName).toHaveValue('Christian-Joseph')
+    expect(firstName).toHaveValue(sampleData.firstName + '-Joseph')
 
-    const form = element.form
-    await element
-      .shadowRoot!.querySelector<HTMLButtonElement>('#reset')
-      ?.click()
-    expect(form.api.getFieldValue('firstName')).toBe('Bob')
+    await userEvent.click(element.querySelector<HTMLButtonElement>('#reset')!)
+    expect(firstName).toHaveValue(sampleData.firstName)
   })
 
   it('should display validation', async () => {
-    const lastName =
-      await element.shadowRoot!.querySelector<HTMLInputElement>('#lastName')!
+    const lastName = element.querySelector<HTMLInputElement>('#lastName')!
     const lastNameValue = 'Jo'
     await userEvent.type(lastName, lastNameValue)
     expect(lastName).toHaveValue('Jo')
-    const form = element.form
-    expect(form.api.getFieldMeta('lastName')?.errors[0]).toBe('Not long enough')
+    expect(element.querySelector('em')?.textContent).toBe('Not long enough')
 
     await userEvent.type(lastName, lastNameValue)
 
     expect(await lastName.getAttribute('error-text')).toBeFalsy()
-    expect(form.api.getFieldValue('lastName')).toBe('JoJo')
-    expect(form.api.getFieldMeta('lastName')?.isTouched).toBeTruthy()
-    expect(form.api.getFieldMeta('lastName')?.errors.length).toBeFalsy()
+    expect(element.querySelector('em')).not.toBeInTheDocument()
   })
 })
diff --git a/packages/svelte-form/tsconfig.json b/packages/svelte-form/tsconfig.json
index c7651b3dc..0d8e5e4c6 100644
--- a/packages/svelte-form/tsconfig.json
+++ b/packages/svelte-form/tsconfig.json
@@ -1,7 +1,8 @@
 {
   "extends": "../../tsconfig.json",
   "compilerOptions": {
-    "moduleResolution": "node16"
+    "module": "NodeNext",
+    "moduleResolution": "NodeNext"
   },
   "include": ["src", "tests", "eslint.config.js", "vite.config.ts"]
 }
diff --git a/packages/svelte-form/vite.config.ts b/packages/svelte-form/vite.config.ts
index 59194a81b..1317cf4eb 100644
--- a/packages/svelte-form/vite.config.ts
+++ b/packages/svelte-form/vite.config.ts
@@ -1,5 +1,6 @@
 import { defineConfig } from 'vitest/config'
 import { svelte } from '@sveltejs/vite-plugin-svelte'
+// @ts-expect-error tsconfig with NodeNext throws an annoying error here which is not relevant
 import packageJson from './package.json'
 
 export default defineConfig({
@@ -12,5 +13,10 @@ export default defineConfig({
     coverage: { enabled: true, provider: 'istanbul', include: ['src/**/*'] },
     typecheck: { enabled: true },
   },
+  resolve: process.env.VITEST
+    ? {
+        conditions: ['browser'],
+      }
+    : undefined,
   plugins: [svelte({})],
 })

From c4e8350952658607ef096d8791bc31f8804246a0 Mon Sep 17 00:00:00 2001
From: Simon Holthausen <simon.holthausen@vercel.com>
Date: Fri, 7 Mar 2025 20:51:58 +0100
Subject: [PATCH 10/17] add svelte examples

---
 examples/svelte/array/.gitignore            |  24 ++++
 examples/svelte/array/README.md             |   6 +
 examples/svelte/array/index.html            |  13 ++
 examples/svelte/array/package.json          |  21 +++
 examples/svelte/array/src/App.svelte        |  55 +++++++
 examples/svelte/array/src/main.ts           |   8 ++
 examples/svelte/array/src/vite-env.d.ts     |   2 +
 examples/svelte/array/svelte.config.js      |   7 +
 examples/svelte/array/tsconfig.json         |  20 +++
 examples/svelte/array/vite.config.ts        |   7 +
 examples/svelte/simple/.gitignore           |  24 ++++
 examples/svelte/simple/README.md            |   6 +
 examples/svelte/simple/index.html           |  13 ++
 examples/svelte/simple/package.json         |  21 +++
 examples/svelte/simple/src/App.svelte       | 150 ++++++++++++++++++++
 examples/svelte/simple/src/FieldInfo.svelte |  12 ++
 examples/svelte/simple/src/main.ts          |   8 ++
 examples/svelte/simple/src/vite-env.d.ts    |   2 +
 examples/svelte/simple/svelte.config.js     |   7 +
 examples/svelte/simple/tsconfig.json        |  20 +++
 examples/svelte/simple/vite.config.ts       |   7 +
 pnpm-lock.yaml                              |  96 ++++++++++---
 pnpm-workspace.yaml                         |   1 +
 23 files changed, 513 insertions(+), 17 deletions(-)
 create mode 100644 examples/svelte/array/.gitignore
 create mode 100644 examples/svelte/array/README.md
 create mode 100644 examples/svelte/array/index.html
 create mode 100644 examples/svelte/array/package.json
 create mode 100644 examples/svelte/array/src/App.svelte
 create mode 100644 examples/svelte/array/src/main.ts
 create mode 100644 examples/svelte/array/src/vite-env.d.ts
 create mode 100644 examples/svelte/array/svelte.config.js
 create mode 100644 examples/svelte/array/tsconfig.json
 create mode 100644 examples/svelte/array/vite.config.ts
 create mode 100644 examples/svelte/simple/.gitignore
 create mode 100644 examples/svelte/simple/README.md
 create mode 100644 examples/svelte/simple/index.html
 create mode 100644 examples/svelte/simple/package.json
 create mode 100644 examples/svelte/simple/src/App.svelte
 create mode 100644 examples/svelte/simple/src/FieldInfo.svelte
 create mode 100644 examples/svelte/simple/src/main.ts
 create mode 100644 examples/svelte/simple/src/vite-env.d.ts
 create mode 100644 examples/svelte/simple/svelte.config.js
 create mode 100644 examples/svelte/simple/tsconfig.json
 create mode 100644 examples/svelte/simple/vite.config.ts

diff --git a/examples/svelte/array/.gitignore b/examples/svelte/array/.gitignore
new file mode 100644
index 000000000..a547bf36d
--- /dev/null
+++ b/examples/svelte/array/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/examples/svelte/array/README.md b/examples/svelte/array/README.md
new file mode 100644
index 000000000..1cf889265
--- /dev/null
+++ b/examples/svelte/array/README.md
@@ -0,0 +1,6 @@
+# Example
+
+To run this example:
+
+- `npm install`
+- `npm run dev`
diff --git a/examples/svelte/array/index.html b/examples/svelte/array/index.html
new file mode 100644
index 000000000..b6c5f0afa
--- /dev/null
+++ b/examples/svelte/array/index.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>Vite + Svelte + TS</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.ts"></script>
+  </body>
+</html>
diff --git a/examples/svelte/array/package.json b/examples/svelte/array/package.json
new file mode 100644
index 000000000..dc04b9fe9
--- /dev/null
+++ b/examples/svelte/array/package.json
@@ -0,0 +1,21 @@
+{
+  "name": "@tanstack/form-example-svelte-array",
+  "private": true,
+  "version": "0.0.0",
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "@tanstack/svelte-form": "^1.0.0"
+  },
+  "devDependencies": {
+    "@sveltejs/vite-plugin-svelte": "^5.0.3",
+    "@tsconfig/svelte": "^5.0.4",
+    "svelte": "^5.20.2",
+    "typescript": "5.8.2",
+    "vite": "^6.1.1"
+  }
+}
diff --git a/examples/svelte/array/src/App.svelte b/examples/svelte/array/src/App.svelte
new file mode 100644
index 000000000..4d8c02dcf
--- /dev/null
+++ b/examples/svelte/array/src/App.svelte
@@ -0,0 +1,55 @@
+<script lang="ts">
+  import { createForm } from '@tanstack/svelte-form'
+
+  const form = createForm(() => ({
+    defaultValues: {
+      people: [] as Array<{ age: number; name: string }>,
+    },
+    onSubmit: ({ value }) => alert(JSON.stringify(value)),
+  }))
+</script>
+
+<form
+  id="form"
+  onsubmit={(e) => {
+    e.preventDefault()
+    e.stopPropagation()
+    form.handleSubmit()
+  }}
+>
+  <h1>TanStack Form - Svelte Demo</h1>
+
+  <form.Field name="people">
+    {#snippet children(field)}
+      <div>
+        {#each field.state.value as person, i}
+          <form.Field name={`people[${i}].name`}>
+            {#snippet children(subField)}
+              <div>
+                <label>
+                  <div>Name for person {i}</div>
+                  <input
+                    value={person.name}
+                    oninput={(e: Event) => {
+                      const target = e.target as HTMLInputElement
+                      subField.handleChange(target.value)
+                    }}
+                  />
+                </label>
+              </div>
+            {/snippet}
+          </form.Field>
+        {/each}
+
+        <button
+          onclick={() => field.pushValue({ name: '', age: 0 })}
+          type="button"
+        >
+          Add person
+        </button>
+      </div>
+    {/snippet}
+  </form.Field>
+
+  <button type="submit"> Submit </button>
+</form>
diff --git a/examples/svelte/array/src/main.ts b/examples/svelte/array/src/main.ts
new file mode 100644
index 000000000..928b6c527
--- /dev/null
+++ b/examples/svelte/array/src/main.ts
@@ -0,0 +1,8 @@
+import { mount } from 'svelte'
+import App from './App.svelte'
+
+const app = mount(App, {
+  target: document.getElementById('app')!,
+})
+
+export default app
diff --git a/examples/svelte/array/src/vite-env.d.ts b/examples/svelte/array/src/vite-env.d.ts
new file mode 100644
index 000000000..4078e7476
--- /dev/null
+++ b/examples/svelte/array/src/vite-env.d.ts
@@ -0,0 +1,2 @@
+/// <reference types="svelte" />
+/// <reference types="vite/client" />
diff --git a/examples/svelte/array/svelte.config.js b/examples/svelte/array/svelte.config.js
new file mode 100644
index 000000000..b0683fd24
--- /dev/null
+++ b/examples/svelte/array/svelte.config.js
@@ -0,0 +1,7 @@
+import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
+
+export default {
+  // Consult https://svelte.dev/docs#compile-time-svelte-preprocess
+  // for more information about preprocessors
+  preprocess: vitePreprocess(),
+}
diff --git a/examples/svelte/array/tsconfig.json b/examples/svelte/array/tsconfig.json
new file mode 100644
index 000000000..55a2f9b65
--- /dev/null
+++ b/examples/svelte/array/tsconfig.json
@@ -0,0 +1,20 @@
+{
+  "extends": "@tsconfig/svelte/tsconfig.json",
+  "compilerOptions": {
+    "target": "ESNext",
+    "useDefineForClassFields": true,
+    "module": "ESNext",
+    "resolveJsonModule": true,
+    /**
+     * Typecheck JS in `.svelte` and `.js` files by default.
+     * Disable checkJs if you'd like to use dynamic types in JS.
+     * Note that setting allowJs false does not prevent the use
+     * of JS in `.svelte` files.
+     */
+    "allowJs": true,
+    "checkJs": true,
+    "isolatedModules": true,
+    "moduleDetection": "force"
+  },
+  "include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte"]
+}
diff --git a/examples/svelte/array/vite.config.ts b/examples/svelte/array/vite.config.ts
new file mode 100644
index 000000000..d32eba1d6
--- /dev/null
+++ b/examples/svelte/array/vite.config.ts
@@ -0,0 +1,7 @@
+import { defineConfig } from 'vite'
+import { svelte } from '@sveltejs/vite-plugin-svelte'
+
+// https://vite.dev/config/
+export default defineConfig({
+  plugins: [svelte()],
+})
diff --git a/examples/svelte/simple/.gitignore b/examples/svelte/simple/.gitignore
new file mode 100644
index 000000000..a547bf36d
--- /dev/null
+++ b/examples/svelte/simple/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/examples/svelte/simple/README.md b/examples/svelte/simple/README.md
new file mode 100644
index 000000000..1cf889265
--- /dev/null
+++ b/examples/svelte/simple/README.md
@@ -0,0 +1,6 @@
+# Example
+
+To run this example:
+
+- `npm install`
+- `npm run dev`
diff --git a/examples/svelte/simple/index.html b/examples/svelte/simple/index.html
new file mode 100644
index 000000000..b6c5f0afa
--- /dev/null
+++ b/examples/svelte/simple/index.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>Vite + Svelte + TS</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.ts"></script>
+  </body>
+</html>
diff --git a/examples/svelte/simple/package.json b/examples/svelte/simple/package.json
new file mode 100644
index 000000000..13b7f3925
--- /dev/null
+++ b/examples/svelte/simple/package.json
@@ -0,0 +1,21 @@
+{
+  "name": "@tanstack/form-example-svelte-simple",
+  "private": true,
+  "version": "0.0.0",
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "@tanstack/svelte-form": "^1.0.0"
+  },
+  "devDependencies": {
+    "@sveltejs/vite-plugin-svelte": "^5.0.3",
+    "@tsconfig/svelte": "^5.0.4",
+    "svelte": "^5.20.2",
+    "typescript": "5.8.2",
+    "vite": "^6.1.1"
+  }
+}
diff --git a/examples/svelte/simple/src/App.svelte b/examples/svelte/simple/src/App.svelte
new file mode 100644
index 000000000..1ed0374c9
--- /dev/null
+++ b/examples/svelte/simple/src/App.svelte
@@ -0,0 +1,150 @@
+<script lang="ts">
+  import { createForm } from '@tanstack/svelte-form'
+  import FieldInfo from './FieldInfo.svelte'
+
+  const form = createForm(() => ({
+    defaultValues: {
+      firstName: '',
+      lastName: '',
+      employed: false,
+      jobTitle: '',
+    },
+    onSubmit: async ({ value }) => {
+      // Do something with form data
+      alert(JSON.stringify(value))
+    },
+  }))
+</script>
+
+<form
+  id="form"
+  onsubmit={(e) => {
+    e.preventDefault()
+    e.stopPropagation()
+    form.handleSubmit()
+  }}
+>
+  <h1>TanStack Form - Svelte Demo</h1>
+
+  <form.Field
+    name="firstName"
+    validators={{
+      onChange: ({ value }) =>
+        value.length < 3 ? 'Not long enough' : undefined,
+      onChangeAsyncDebounceMs: 500,
+      onChangeAsync: async ({ value }) => {
+        await new Promise((resolve) => setTimeout(resolve, 1000))
+        return value.includes('error') && 'No "error" allowed in first name'
+      },
+    }}
+  >
+    {#snippet children(field)}
+      <div>
+        <label for={field.name}>First Name</label>
+        <input
+          id={field.name}
+          type="text"
+          placeholder="First Name"
+          value={field.state.value}
+          onblur={() => field.handleBlur()}
+          oninput={(e: Event) => {
+            const target = e.target as HTMLInputElement
+            field.handleChange(target.value)
+          }}
+        />
+        <FieldInfo {field} />
+      </div>
+    {/snippet}
+  </form.Field>
+  <form.Field
+    name="lastName"
+    validators={{
+      onChange: ({ value }) =>
+        value.length < 3 ? 'Not long enough' : undefined,
+    }}
+  >
+    {#snippet children(field)}
+      <div>
+        <label for={field.name}>Last Name</label>
+        <input
+          id={field.name}
+          type="text"
+          placeholder="Last Name"
+          value={field.state.value}
+          onblur={() => field.handleBlur()}
+          oninput={(e: Event) => {
+            const target = e.target as HTMLInputElement
+            field.handleChange(target.value)
+          }}
+        />
+        <FieldInfo {field} />
+      </div>
+    {/snippet}
+  </form.Field>
+  <form.Field name="employed">
+    {#snippet children(field)}
+      <div>
+        <label for={field.name}>Employed?</label>
+        <input
+          oninput={() => field.handleChange(!field.state.value)}
+          checked={field.state.value}
+          onblur={() => field.handleBlur()}
+          id={field.name}
+          type="checkbox"
+        />
+      </div>
+      {#if field.state.value}
+        <form.Field
+          name="jobTitle"
+          validators={{
+            onChange: ({ value }) =>
+              value.length === 0 ? 'If you have a job, you need a title' : null,
+          }}
+        >
+          {#snippet children(field)}
+            <div>
+              <label for={field.name}>Job Title</label>
+              <input
+                type="text"
+                id={field.name}
+                placeholder="Job Title"
+                value={field.state.value}
+                onblur={field.handleBlur}
+                oninput={(e: Event) => {
+                  const target = e.target as HTMLInputElement
+                  field.handleChange(target.value)
+                }}
+              />
+              <FieldInfo {field} />
+            </div>
+          {/snippet}
+        </form.Field>
+      {/if}
+    {/snippet}
+  </form.Field>
+  <div>
+    <form.Subscribe
+      selector={(state) => ({
+        canSubmit: state.canSubmit,
+        isSubmitting: state.isSubmitting,
+      })}
+    >
+      {#snippet children({ canSubmit, isSubmitting })}
+        <button type="submit" disabled={!canSubmit}>
+          {isSubmitting ? 'Submitting' : 'Submit'}
+        </button>
+      {/snippet}
+    </form.Subscribe>
+    <button
+      type="button"
+      id="reset"
+      onclick={() => {
+        form.reset()
+      }}
+    >
+      Reset
+    </button>
+  </div>
+</form>
+
+<pre>{JSON.stringify(form.state, null, 2)}</pre>
diff --git a/examples/svelte/simple/src/FieldInfo.svelte b/examples/svelte/simple/src/FieldInfo.svelte
new file mode 100644
index 000000000..45e950412
--- /dev/null
+++ b/examples/svelte/simple/src/FieldInfo.svelte
@@ -0,0 +1,12 @@
+<script lang="ts">
+  import type { AnyFieldApi } from '@tanstack/svelte-form'
+
+  let { field }: { field: AnyFieldApi } = $props()
+</script>
+
+{#if field.state.meta.isTouched}
+  {#each field.state.meta.errors as error}
+    <em>{error}</em>
+  {/each}
+  {field.state.meta.isValidating ? 'Validating...' : ''}
+{/if}
diff --git a/examples/svelte/simple/src/main.ts b/examples/svelte/simple/src/main.ts
new file mode 100644
index 000000000..928b6c527
--- /dev/null
+++ b/examples/svelte/simple/src/main.ts
@@ -0,0 +1,8 @@
+import { mount } from 'svelte'
+import App from './App.svelte'
+
+const app = mount(App, {
+  target: document.getElementById('app')!,
+})
+
+export default app
diff --git a/examples/svelte/simple/src/vite-env.d.ts b/examples/svelte/simple/src/vite-env.d.ts
new file mode 100644
index 000000000..4078e7476
--- /dev/null
+++ b/examples/svelte/simple/src/vite-env.d.ts
@@ -0,0 +1,2 @@
+/// <reference types="svelte" />
+/// <reference types="vite/client" />
diff --git a/examples/svelte/simple/svelte.config.js b/examples/svelte/simple/svelte.config.js
new file mode 100644
index 000000000..b0683fd24
--- /dev/null
+++ b/examples/svelte/simple/svelte.config.js
@@ -0,0 +1,7 @@
+import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
+
+export default {
+  // Consult https://svelte.dev/docs#compile-time-svelte-preprocess
+  // for more information about preprocessors
+  preprocess: vitePreprocess(),
+}
diff --git a/examples/svelte/simple/tsconfig.json b/examples/svelte/simple/tsconfig.json
new file mode 100644
index 000000000..55a2f9b65
--- /dev/null
+++ b/examples/svelte/simple/tsconfig.json
@@ -0,0 +1,20 @@
+{
+  "extends": "@tsconfig/svelte/tsconfig.json",
+  "compilerOptions": {
+    "target": "ESNext",
+    "useDefineForClassFields": true,
+    "module": "ESNext",
+    "resolveJsonModule": true,
+    /**
+     * Typecheck JS in `.svelte` and `.js` files by default.
+     * Disable checkJs if you'd like to use dynamic types in JS.
+     * Note that setting allowJs false does not prevent the use
+     * of JS in `.svelte` files.
+     */
+    "allowJs": true,
+    "checkJs": true,
+    "isolatedModules": true,
+    "moduleDetection": "force"
+  },
+  "include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte"]
+}
diff --git a/examples/svelte/simple/vite.config.ts b/examples/svelte/simple/vite.config.ts
new file mode 100644
index 000000000..d32eba1d6
--- /dev/null
+++ b/examples/svelte/simple/vite.config.ts
@@ -0,0 +1,7 @@
+import { defineConfig } from 'vite'
+import { svelte } from '@sveltejs/vite-plugin-svelte'
+
+// https://vite.dev/config/
+export default defineConfig({
+  plugins: [svelte()],
+})
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ef34e30e6..f2c632cbb 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -635,6 +635,50 @@ importers:
         specifier: ^2.11.2
         version: 2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.5)(vite@6.2.0(@types/node@22.13.9)(jiti@2.4.2)(less@4.2.2)(sass@1.85.1)(sugarss@4.0.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))
 
+  examples/svelte/array:
+    dependencies:
+      '@tanstack/svelte-form':
+        specifier: ^1.0.0
+        version: link:../../../packages/svelte-form
+    devDependencies:
+      '@sveltejs/vite-plugin-svelte':
+        specifier: ^5.0.3
+        version: 5.0.3(svelte@5.22.5)(vite@6.2.0(@types/node@22.13.9)(jiti@2.4.2)(less@4.2.2)(sass@1.85.1)(sugarss@4.0.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))
+      '@tsconfig/svelte':
+        specifier: ^5.0.4
+        version: 5.0.4
+      svelte:
+        specifier: ^5.20.2
+        version: 5.22.5
+      typescript:
+        specifier: 5.8.2
+        version: 5.8.2
+      vite:
+        specifier: ^6.1.1
+        version: 6.2.0(@types/node@22.13.9)(jiti@2.4.2)(less@4.2.2)(sass@1.85.1)(sugarss@4.0.1(postcss@8.5.1))(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0)
+
+  examples/svelte/simple:
+    dependencies:
+      '@tanstack/svelte-form':
+        specifier: ^1.0.0
+        version: link:../../../packages/svelte-form
+    devDependencies:
+      '@sveltejs/vite-plugin-svelte':
+        specifier: ^5.0.3
+        version: 5.0.3(svelte@5.22.5)(vite@6.2.0(@types/node@22.13.9)(jiti@2.4.2)(less@4.2.2)(sass@1.85.1)(sugarss@4.0.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))
+      '@tsconfig/svelte':
+        specifier: ^5.0.4
+        version: 5.0.4
+      svelte:
+        specifier: ^5.20.2
+        version: 5.22.5
+      typescript:
+        specifier: 5.8.2
+        version: 5.8.2
+      vite:
+        specifier: ^6.1.1
+        version: 6.2.0(@types/node@22.13.9)(jiti@2.4.2)(less@4.2.2)(sass@1.85.1)(sugarss@4.0.1(postcss@8.5.1))(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0)
+
   examples/vue/array:
     dependencies:
       '@tanstack/vue-form':
@@ -818,18 +862,21 @@ importers:
       '@tanstack/form-core':
         specifier: workspace:*
         version: link:../form-core
+      '@tanstack/svelte-store':
+        specifier: ^0.7.0
+        version: 0.7.0(svelte@5.22.5)
     devDependencies:
       '@sveltejs/package':
-        specifier: ^2.3.7
+        specifier: ^2.3.10
         version: 2.3.10(svelte@5.22.5)(typescript@5.8.2)
       '@sveltejs/vite-plugin-svelte':
-        specifier: ^4.0.4
-        version: 4.0.4(svelte@5.22.5)(vite@6.2.0(@types/node@22.13.9)(jiti@2.4.2)(less@4.2.2)(sass@1.85.1)(sugarss@4.0.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))
+        specifier: ^5.0.3
+        version: 5.0.3(svelte@5.22.5)(vite@6.2.0(@types/node@22.13.9)(jiti@2.4.2)(less@4.2.2)(sass@1.85.1)(sugarss@4.0.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))
       premove:
         specifier: ^4.0.0
         version: 4.0.0
       svelte:
-        specifier: ^5.16.1
+        specifier: ^5.20.2
         version: 5.22.5
 
   packages/vue-form:
@@ -3907,20 +3954,20 @@ packages:
     peerDependencies:
       svelte: ^3.44.0 || ^4.0.0 || ^5.0.0-next.1
 
-  '@sveltejs/vite-plugin-svelte-inspector@3.0.1':
-    resolution: {integrity: sha512-2CKypmj1sM4GE7HjllT7UKmo4Q6L5xFRd7VMGEWhYnZ+wc6AUVU01IBd7yUi6WnFndEwWoMNOd6e8UjoN0nbvQ==}
+  '@sveltejs/vite-plugin-svelte-inspector@4.0.1':
+    resolution: {integrity: sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==}
     engines: {node: ^18.0.0 || ^20.0.0 || >=22}
     peerDependencies:
-      '@sveltejs/vite-plugin-svelte': ^4.0.0-next.0||^4.0.0
-      svelte: ^5.0.0-next.96 || ^5.0.0
-      vite: ^5.0.0
+      '@sveltejs/vite-plugin-svelte': ^5.0.0
+      svelte: ^5.0.0
+      vite: ^6.0.0
 
-  '@sveltejs/vite-plugin-svelte@4.0.4':
-    resolution: {integrity: sha512-0ba1RQ/PHen5FGpdSrW7Y3fAMQjrXantECALeOiOdBdzR5+5vPP6HVZRLmZaQL+W8m++o+haIAKq5qT+MiZ7VA==}
+  '@sveltejs/vite-plugin-svelte@5.0.3':
+    resolution: {integrity: sha512-MCFS6CrQDu1yGwspm4qtli0e63vaPCehf6V7pIMP15AsWgMKrqDGCPFF/0kn4SP0ii4aySu4Pa62+fIRGFMjgw==}
     engines: {node: ^18.0.0 || ^20.0.0 || >=22}
     peerDependencies:
-      svelte: ^5.0.0-next.96 || ^5.0.0
-      vite: ^5.0.0
+      svelte: ^5.0.0
+      vite: ^6.0.0
 
   '@swc/core-darwin-arm64@1.11.7':
     resolution: {integrity: sha512-3+LhCP2H50CLI6yv/lhOtoZ5B/hi7Q/23dye1KhbSDeDprLTm/KfLJh/iQqwaHUponf5m8C2U0y6DD+HGLz8Yw==}
@@ -4158,6 +4205,11 @@ packages:
   '@tanstack/store@0.7.0':
     resolution: {integrity: sha512-CNIhdoUsmD2NolYuaIs8VfWM467RK6oIBAW4nPEKZhg1smZ+/CwtCdpURgp7nxSqOaV9oKkzdWD80+bC66F/Jg==}
 
+  '@tanstack/svelte-store@0.7.0':
+    resolution: {integrity: sha512-FVuPuCLkGV/YcsJImIiJAZRh3s8DqI8C9jQMNXymELwsrpe538Vmb81fxHHNZx9MCtzsFcHfYOkImFB4r4QcfA==}
+    peerDependencies:
+      svelte: ^5.0.0
+
   '@tanstack/virtual-file-routes@1.99.0':
     resolution: {integrity: sha512-XvX8bfdo4CYiCW+ItVdBfCorh3PwQFqYqd7ll+XKWiWOJpqUGIG7VlziVavARZpUySiY2VBlHadiUYS7jhgjRg==}
     engines: {node: '>=12'}
@@ -4226,6 +4278,9 @@ packages:
   '@ts-morph/common@0.22.0':
     resolution: {integrity: sha512-HqNBuV/oIlMKdkLshXd1zKBqNQCsuPEsgQOkfFQ/eUKjRlwndXW1AjN9LVkBEIukm00gGXSRmfkl0Wv5VXLnlw==}
 
+  '@tsconfig/svelte@5.0.4':
+    resolution: {integrity: sha512-BV9NplVgLmSi4mwKzD8BD/NQ8erOY/nUE/GpgWe2ckx+wIQF5RyRirn/QsSSCPeulVpc3RA/iJt6DpfTIZps0Q==}
+
   '@tufjs/canonical-json@2.0.0':
     resolution: {integrity: sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==}
     engines: {node: ^16.14.0 || >=18.0.0}
@@ -13602,18 +13657,18 @@ snapshots:
     transitivePeerDependencies:
       - typescript
 
-  '@sveltejs/vite-plugin-svelte-inspector@3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.22.5)(vite@6.2.0(@types/node@22.13.9)(jiti@2.4.2)(less@4.2.2)(sass@1.85.1)(sugarss@4.0.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0)))(svelte@5.22.5)(vite@6.2.0(@types/node@22.13.9)(jiti@2.4.2)(less@4.2.2)(sass@1.85.1)(sugarss@4.0.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))':
+  '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.22.5)(vite@6.2.0(@types/node@22.13.9)(jiti@2.4.2)(less@4.2.2)(sass@1.85.1)(sugarss@4.0.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0)))(svelte@5.22.5)(vite@6.2.0(@types/node@22.13.9)(jiti@2.4.2)(less@4.2.2)(sass@1.85.1)(sugarss@4.0.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))':
     dependencies:
-      '@sveltejs/vite-plugin-svelte': 4.0.4(svelte@5.22.5)(vite@6.2.0(@types/node@22.13.9)(jiti@2.4.2)(less@4.2.2)(sass@1.85.1)(sugarss@4.0.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))
+      '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.22.5)(vite@6.2.0(@types/node@22.13.9)(jiti@2.4.2)(less@4.2.2)(sass@1.85.1)(sugarss@4.0.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))
       debug: 4.4.0(supports-color@9.4.0)
       svelte: 5.22.5
       vite: 6.2.0(@types/node@22.13.9)(jiti@2.4.2)(less@4.2.2)(sass@1.85.1)(sugarss@4.0.1(postcss@8.5.1))(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0)
     transitivePeerDependencies:
       - supports-color
 
-  '@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.22.5)(vite@6.2.0(@types/node@22.13.9)(jiti@2.4.2)(less@4.2.2)(sass@1.85.1)(sugarss@4.0.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))':
+  '@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.22.5)(vite@6.2.0(@types/node@22.13.9)(jiti@2.4.2)(less@4.2.2)(sass@1.85.1)(sugarss@4.0.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))':
     dependencies:
-      '@sveltejs/vite-plugin-svelte-inspector': 3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.22.5)(vite@6.2.0(@types/node@22.13.9)(jiti@2.4.2)(less@4.2.2)(sass@1.85.1)(sugarss@4.0.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0)))(svelte@5.22.5)(vite@6.2.0(@types/node@22.13.9)(jiti@2.4.2)(less@4.2.2)(sass@1.85.1)(sugarss@4.0.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))
+      '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.22.5)(vite@6.2.0(@types/node@22.13.9)(jiti@2.4.2)(less@4.2.2)(sass@1.85.1)(sugarss@4.0.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0)))(svelte@5.22.5)(vite@6.2.0(@types/node@22.13.9)(jiti@2.4.2)(less@4.2.2)(sass@1.85.1)(sugarss@4.0.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))
       debug: 4.4.0(supports-color@9.4.0)
       deepmerge: 4.3.1
       kleur: 4.1.5
@@ -14445,6 +14500,11 @@ snapshots:
 
   '@tanstack/store@0.7.0': {}
 
+  '@tanstack/svelte-store@0.7.0(svelte@5.22.5)':
+    dependencies:
+      '@tanstack/store': 0.7.0
+      svelte: 5.22.5
+
   '@tanstack/virtual-file-routes@1.99.0': {}
 
   '@tanstack/vue-store@0.7.0(vue@3.5.13(typescript@5.8.2))':
@@ -14524,6 +14584,8 @@ snapshots:
       mkdirp: 3.0.1
       path-browserify: 1.0.1
 
+  '@tsconfig/svelte@5.0.4': {}
+
   '@tufjs/canonical-json@2.0.0': {}
 
   '@tufjs/models@3.0.1':
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 029aff20d..47d0b79bc 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -5,3 +5,4 @@ packages:
   - 'examples/solid/**'
   - 'examples/vue/**'
   - 'examples/lit/**'
+  - 'examples/svelte/**'

From 69a6eb6d7324ed4fa7b48e0acb3d7da58af1e011 Mon Sep 17 00:00:00 2001
From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com>
Date: Fri, 7 Mar 2025 23:06:41 +0000
Subject: [PATCH 11/17] ci: apply automated fixes and generate docs

---
 docs/framework/solid/reference/functions/usestore.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/docs/framework/solid/reference/functions/usestore.md b/docs/framework/solid/reference/functions/usestore.md
index a2456113e..aee840203 100644
--- a/docs/framework/solid/reference/functions/usestore.md
+++ b/docs/framework/solid/reference/functions/usestore.md
@@ -13,7 +13,7 @@ title: useStore
 function useStore<TState, TSelected>(store, selector?): Accessor<TSelected>
 ```
 
-Defined in: node\_modules/.pnpm/@tanstack+solid-store@0.7.0\_solid-js@1.9.4/node\_modules/@tanstack/solid-store/dist/esm/index.d.ts:8
+Defined in: node\_modules/.pnpm/@tanstack+solid-store@0.7.0\_solid-js@1.9.5/node\_modules/@tanstack/solid-store/dist/esm/index.d.ts:8
 
 ### Type Parameters
 
@@ -41,7 +41,7 @@ Defined in: node\_modules/.pnpm/@tanstack+solid-store@0.7.0\_solid-js@1.9.4/node
 function useStore<TState, TSelected>(store, selector?): Accessor<TSelected>
 ```
 
-Defined in: node\_modules/.pnpm/@tanstack+solid-store@0.7.0\_solid-js@1.9.4/node\_modules/@tanstack/solid-store/dist/esm/index.d.ts:9
+Defined in: node\_modules/.pnpm/@tanstack+solid-store@0.7.0\_solid-js@1.9.5/node\_modules/@tanstack/solid-store/dist/esm/index.d.ts:9
 
 ### Type Parameters
 

From 032e1f76f365f844bb8f156cca9b7377ab87d279 Mon Sep 17 00:00:00 2001
From: Simon Holthausen <simon.holthausen@vercel.com>
Date: Mon, 10 Mar 2025 16:42:20 +0100
Subject: [PATCH 12/17] use svelte-check for type checking

---
 packages/svelte-form/package.json             | 11 +++------
 packages/svelte-form/src/createForm.svelte.ts |  1 -
 packages/svelte-form/src/index.ts             |  1 -
 pnpm-lock.yaml                                | 23 +++++++++++++++++++
 4 files changed, 26 insertions(+), 10 deletions(-)

diff --git a/packages/svelte-form/package.json b/packages/svelte-form/package.json
index 047b822b0..6cb4cfe27 100644
--- a/packages/svelte-form/package.json
+++ b/packages/svelte-form/package.json
@@ -13,13 +13,7 @@
   "scripts": {
     "clean": "premove ./dist ./coverage",
     "test:eslint": "eslint ./src ./tests",
-    "test:types": "pnpm run \"/^test:types:ts[0-9]{2}$/\"",
-    "test:types:ts51": "node ../../node_modules/typescript51/lib/tsc.js",
-    "test:types:ts52": "node ../../node_modules/typescript52/lib/tsc.js",
-    "test:types:ts53": "node ../../node_modules/typescript53/lib/tsc.js",
-    "test:types:ts54": "node ../../node_modules/typescript54/lib/tsc.js",
-    "test:types:ts55": "node ../../node_modules/typescript55/lib/tsc.js",
-    "test:types:ts56": "tsc",
+    "test:types": "svelte-check",
     "test:lib": "vitest",
     "test:lib:dev": "pnpm run test:lib --watch",
     "test:build": "publint --strict",
@@ -50,7 +44,8 @@
     "@sveltejs/package": "^2.3.10",
     "@sveltejs/vite-plugin-svelte": "^5.0.3",
     "premove": "^4.0.0",
-    "svelte": "^5.20.2"
+    "svelte": "^5.20.2",
+    "svelte-check": "^4.1.5"
   },
   "peerDependencies": {
     "svelte": "^5.0.0"
diff --git a/packages/svelte-form/src/createForm.svelte.ts b/packages/svelte-form/src/createForm.svelte.ts
index e44c37a5b..a687e6a10 100644
--- a/packages/svelte-form/src/createForm.svelte.ts
+++ b/packages/svelte-form/src/createForm.svelte.ts
@@ -1,7 +1,6 @@
 import { FormApi } from '@tanstack/form-core'
 import { useStore } from '@tanstack/svelte-store'
 import { onMount } from 'svelte'
-// @ts-ignore tsc doesn't know about named exports from svelte files
 import Field, { createField } from './Field.svelte'
 import Subscribe from './Subscribe.svelte'
 import type {
diff --git a/packages/svelte-form/src/index.ts b/packages/svelte-form/src/index.ts
index 0b24263e3..a76cbb7f2 100644
--- a/packages/svelte-form/src/index.ts
+++ b/packages/svelte-form/src/index.ts
@@ -4,7 +4,6 @@ export { useStore } from '@tanstack/svelte-store'
 
 export { createForm, type SvelteFormApi } from './createForm.svelte.js'
 
-// @ts-ignore tsc doesn't know about named exports from svelte files
 export { default as Field, createField } from './Field.svelte'
 
 export type { CreateField, FieldComponent } from './types.js'
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f2c632cbb..e6fe5eec2 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -878,6 +878,9 @@ importers:
       svelte:
         specifier: ^5.20.2
         version: 5.22.5
+      svelte-check:
+        specifier: ^4.1.5
+        version: 4.1.5(picomatch@4.0.2)(svelte@5.22.5)(typescript@5.8.2)
 
   packages/vue-form:
     dependencies:
@@ -9232,6 +9235,14 @@ packages:
     resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
     engines: {node: '>= 0.4'}
 
+  svelte-check@4.1.5:
+    resolution: {integrity: sha512-Gb0T2IqBNe1tLB9EB1Qh+LOe+JB8wt2/rNBDGvkxQVvk8vNeAoG+vZgFB/3P5+zC7RWlyBlzm9dVjZFph/maIg==}
+    engines: {node: '>= 18.0.0'}
+    hasBin: true
+    peerDependencies:
+      svelte: ^4.0.0 || ^5.0.0-next.0
+      typescript: '>=5.0.0'
+
   svelte2tsx@0.7.35:
     resolution: {integrity: sha512-z2lnOnrfb5nrlRfFQI8Qdz03xQqMHUfPj0j8l/fQuydrH89cCeN+v9jgDwK9GyMtdTRUkE7Neu9Gh+vfXJAfuQ==}
     peerDependencies:
@@ -20546,6 +20557,18 @@ snapshots:
 
   supports-preserve-symlinks-flag@1.0.0: {}
 
+  svelte-check@4.1.5(picomatch@4.0.2)(svelte@5.22.5)(typescript@5.8.2):
+    dependencies:
+      '@jridgewell/trace-mapping': 0.3.25
+      chokidar: 4.0.3
+      fdir: 6.4.3(picomatch@4.0.2)
+      picocolors: 1.1.1
+      sade: 1.8.1
+      svelte: 5.22.5
+      typescript: 5.8.2
+    transitivePeerDependencies:
+      - picomatch
+
   svelte2tsx@0.7.35(svelte@5.22.5)(typescript@5.8.2):
     dependencies:
       dedent-js: 1.0.1

From 08daf3bcd163d34819a71777c246688bacea0b5d Mon Sep 17 00:00:00 2001
From: Simon Holthausen <simon.holthausen@vercel.com>
Date: Mon, 10 Mar 2025 16:46:35 +0100
Subject: [PATCH 13/17] remove accidental leftover

---
 examples/svelte/simple/src/App.svelte | 2 --
 1 file changed, 2 deletions(-)

diff --git a/examples/svelte/simple/src/App.svelte b/examples/svelte/simple/src/App.svelte
index 1ed0374c9..6bf6e805d 100644
--- a/examples/svelte/simple/src/App.svelte
+++ b/examples/svelte/simple/src/App.svelte
@@ -146,5 +146,3 @@
     </button>
   </div>
 </form>
-
-<pre>{JSON.stringify(form.state, null, 2)}</pre>

From 34dadc437871146d7e5ba24a4b87a61bf975ab9a Mon Sep 17 00:00:00 2001
From: Simon Holthausen <simon.holthausen@vercel.com>
Date: Mon, 10 Mar 2025 16:53:30 +0100
Subject: [PATCH 14/17] add svelte to keywords

---
 packages/svelte-form/package.json | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/packages/svelte-form/package.json b/packages/svelte-form/package.json
index 6cb4cfe27..b2e158b9b 100644
--- a/packages/svelte-form/package.json
+++ b/packages/svelte-form/package.json
@@ -4,6 +4,9 @@
   "description": "Powerful, type-safe forms for Svelte.",
   "author": "tannerlinsley",
   "license": "MIT",
+  "keywords": [
+    "svelte"
+  ],
   "repository": {
     "type": "git",
     "url": "https://github.com/TanStack/form.git",

From 151c2b21c63ab22ee02988ca1558f7568eedc463 Mon Sep 17 00:00:00 2001
From: Simon Holthausen <simon.holthausen@vercel.com>
Date: Mon, 10 Mar 2025 17:35:56 +0100
Subject: [PATCH 15/17] clarify, fix

---
 packages/svelte-form/src/createForm.svelte.ts | 2 +-
 packages/svelte-form/src/types.ts             | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/svelte-form/src/createForm.svelte.ts b/packages/svelte-form/src/createForm.svelte.ts
index a687e6a10..f902f1a96 100644
--- a/packages/svelte-form/src/createForm.svelte.ts
+++ b/packages/svelte-form/src/createForm.svelte.ts
@@ -219,7 +219,7 @@ export function createForm<
   extendedApi.createField = (props) =>
     createField(() => {
       return { ...props(), form: api }
-    }) as never
+    }) as never // Type cast because else "Error: Type instantiation is excessively deep and possibly infinite."
   extendedApi.useStore = (selector) => useStore(api.store, selector)
   // @ts-expect-error constructor definition exists only on a type level
   extendedApi.Subscribe = (internal, props) =>
diff --git a/packages/svelte-form/src/types.ts b/packages/svelte-form/src/types.ts
index 6b4ad3133..45aa74b3c 100644
--- a/packages/svelte-form/src/types.ts
+++ b/packages/svelte-form/src/types.ts
@@ -339,7 +339,7 @@ export type CreateField<
     >,
     'form'
   >,
-) => () => FieldApi<
+) => FieldApi<
   TParentData,
   TName,
   TData,

From 55e5c82d7c8df979c2ff121ff37e06b3818495dd Mon Sep 17 00:00:00 2001
From: Simon Holthausen <simon.holthausen@vercel.com>
Date: Mon, 17 Mar 2025 11:03:35 +0100
Subject: [PATCH 16/17] fix

---
 packages/svelte-form/tests/simple.svelte  | 2 +-
 packages/svelte-form/tests/simple.test.ts | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/svelte-form/tests/simple.svelte b/packages/svelte-form/tests/simple.svelte
index c48080ce5..bcca2193c 100644
--- a/packages/svelte-form/tests/simple.svelte
+++ b/packages/svelte-form/tests/simple.svelte
@@ -11,7 +11,7 @@
 </script>
 
 <script lang="ts">
-  import { createForm } from '@tanstack/svelte-form'
+  import { createForm } from '../src/index.js'
 
   const form = createForm(() => ({
     defaultValues: sampleData,
diff --git a/packages/svelte-form/tests/simple.test.ts b/packages/svelte-form/tests/simple.test.ts
index 3fa23d8c6..d39cd594b 100644
--- a/packages/svelte-form/tests/simple.test.ts
+++ b/packages/svelte-form/tests/simple.test.ts
@@ -59,7 +59,7 @@ describe('Svelte Tests', () => {
 
     await userEvent.type(lastName, lastNameValue)
 
-    expect(await lastName.getAttribute('error-text')).toBeFalsy()
+    expect(lastName.getAttribute('error-text')).toBeFalsy()
     expect(element.querySelector('em')).not.toBeInTheDocument()
   })
 })

From f01a213eec9590400cd17b8729716ccb31bd8b54 Mon Sep 17 00:00:00 2001
From: Simon Holthausen <simon.holthausen@vercel.com>
Date: Mon, 17 Mar 2025 13:39:07 +0100
Subject: [PATCH 17/17] resolve todo, tweak code, add test

---
 packages/svelte-form/src/Field.svelte     |  5 ++---
 packages/svelte-form/tests/simple.svelte  | 19 +++++++++++++++----
 packages/svelte-form/tests/simple.test.ts | 19 ++++++++++++++-----
 3 files changed, 31 insertions(+), 12 deletions(-)

diff --git a/packages/svelte-form/src/Field.svelte b/packages/svelte-form/src/Field.svelte
index 62032794c..ebaf1f854 100644
--- a/packages/svelte-form/src/Field.svelte
+++ b/packages/svelte-form/src/Field.svelte
@@ -93,11 +93,10 @@
       }
     })
 
-    // TODO (43081j): does this do what i think? we don't access anything
-    // svelte is aware of, so maybe it'll never call this?
     $effect.pre(() => {
+      // Invoke options function before mounted check, else it wouldn't rerun on changes to options.
+      // Changes to options are seen by the effect because signals inside them are picked up.
       const current = opts()
-      Object.values(current) // make sure we're watching all the things
       if (!mounted) return
       api.update(current)
     })
diff --git a/packages/svelte-form/tests/simple.svelte b/packages/svelte-form/tests/simple.svelte
index bcca2193c..147556ea0 100644
--- a/packages/svelte-form/tests/simple.svelte
+++ b/packages/svelte-form/tests/simple.svelte
@@ -1,13 +1,17 @@
+<svelte:options runes />
+
 <script module lang="ts">
   interface Employee {
     firstName: string
     lastName: string
   }
 
-  export const sampleData: Employee = {
+  let sampleData: Employee = $state({
     firstName: 'Christian',
     lastName: '',
-  }
+  })
+
+  export const getSampleData = () => sampleData
 </script>
 
 <script lang="ts">
@@ -21,7 +25,7 @@
     },
   }))
 
-  const state = form.useStore()
+  const formState = form.useStore()
 </script>
 
 <form
@@ -110,4 +114,11 @@
   </div>
 </form>
 
-<pre>{JSON.stringify(state.current, null, 2)}</pre>
+<button
+  id="change"
+  onclick={() => (sampleData = { firstName: 'Julian', lastName: '' })}
+>
+  Change Sample Data
+</button>
+
+<pre>{JSON.stringify(formState.current, null, 2)}</pre>
diff --git a/packages/svelte-form/tests/simple.test.ts b/packages/svelte-form/tests/simple.test.ts
index d39cd594b..ab0a433e7 100644
--- a/packages/svelte-form/tests/simple.test.ts
+++ b/packages/svelte-form/tests/simple.test.ts
@@ -4,7 +4,7 @@ import '@testing-library/jest-dom'
 import { userEvent } from '@testing-library/user-event'
 import { mount, unmount } from 'svelte'
 // @ts-ignore tsc doesn't know about named exports from svelte files
-import TestForm, { sampleData } from './simple.svelte'
+import TestForm, { getSampleData } from './simple.svelte'
 
 describe('Svelte Tests', () => {
   let element: HTMLDivElement
@@ -24,13 +24,22 @@ describe('Svelte Tests', () => {
 
   it('should have initial values', async () => {
     expect(element.querySelector<HTMLInputElement>('#firstName')).toHaveValue(
-      sampleData.firstName,
+      getSampleData().firstName,
     )
     expect(element.querySelector<HTMLInputElement>('#lastName')).toHaveValue(
-      sampleData.lastName,
+      getSampleData().lastName,
     )
   })
 
+  it('should change initial values when defaults update', async () => {
+    await userEvent.click(element.querySelector<HTMLButtonElement>('#change')!)
+
+    expect(element.querySelector<HTMLInputElement>('#firstName')).toHaveValue(
+      getSampleData().firstName,
+    )
+    expect(getSampleData().firstName).toBe('Julian')
+  })
+
   it('should mirror user input', async () => {
     const lastName = element.querySelector<HTMLInputElement>('#lastName')!
     const lastNameValue = 'Jobs'
@@ -44,10 +53,10 @@ describe('Svelte Tests', () => {
     const firstName = element.querySelector<HTMLInputElement>('#firstName')!
     await userEvent.type(firstName, '-Joseph')
 
-    expect(firstName).toHaveValue(sampleData.firstName + '-Joseph')
+    expect(firstName).toHaveValue(getSampleData().firstName + '-Joseph')
 
     await userEvent.click(element.querySelector<HTMLButtonElement>('#reset')!)
-    expect(firstName).toHaveValue(sampleData.firstName)
+    expect(firstName).toHaveValue(getSampleData().firstName)
   })
 
   it('should display validation', async () => {