Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
110 commits
Select commit Hold shift + click to select a range
8929a9f
feat(svelte5): add Svelte 5 adapter and test app
brodienguyen Sep 23, 2025
f4a305c
refactor(test-app): update imports to @inertiajs/svelte5
brodienguyen Sep 23, 2025
2cddcf5
refactor(test-app): update to svelte5 page store usage
brodienguyen Sep 23, 2025
0a4ab02
refactor(components): migrate to $props and $state
brodienguyen Sep 23, 2025
2c77cc9
refactor(components): switch to children/fallback props
brodienguyen Sep 23, 2025
9fd8b75
refactor(deferred-props): update fallback syntax
brodienguyen Sep 23, 2025
05f05d8
refactor(events): use $page.url instead of page.url
brodienguyen Sep 23, 2025
6815b1b
refactor(FormComponent): use snippet for children
brodienguyen Sep 23, 2025
6b346fc
refactor(Deferred): improve reactivity in effect
brodienguyen Sep 23, 2025
d12b857
refactor(App): improve page state reactivity
brodienguyen Sep 23, 2025
fd1f177
refactor(FormComponent): wrap form content in snippet
brodienguyen Sep 23, 2025
ec49202
refactor(components): update event handler syntax
brodienguyen Sep 23, 2025
c891424
feat(form): expose isDirty and setError in Form
brodienguyen Sep 23, 2025
f353ab4
refactor(form): update Form usage to snippet block
brodienguyen Sep 23, 2025
b322570
refactor(form): update Form usage to snippet block
brodienguyen Sep 23, 2025
232103a
refactor(FormComponent): update slot usage
brodienguyen Sep 23, 2025
acf6066
refactor(form): update Form usage and error display
brodienguyen Sep 23, 2025
9e911e1
refactor(FormComponent): update Form usage
brodienguyen Sep 23, 2025
935b917
feat(FormComponent): add email field to form
brodienguyen Sep 23, 2025
8337de1
test(svelte): skip tests for svelte5 adapter
brodienguyen Sep 23, 2025
073f47f
refactor(form): migrate useForm to Svelte runes
brodienguyen Sep 23, 2025
9906982
refactor(FormHelper): remove $ prefix from form usage
brodienguyen Sep 24, 2025
461cffc
refactor(FormHelper): remove $ prefix from form usage
brodienguyen Sep 24, 2025
e1bf42a
refactor(FormComponent): update Reset.svelte children
brodienguyen Sep 24, 2025
adb6a9b
refactor(FormComponent): use snippet for form children
brodienguyen Sep 24, 2025
0d719a1
refactor(form): use snippet for form children
brodienguyen Sep 24, 2025
fef4783
fix(useForm): correct error handling in onError callback
brodienguyen Sep 24, 2025
fea83cf
fix(useForm): improve reactivity and reset logic
brodienguyen Sep 24, 2025
2901ea6
fix(form): add id to error message div
brodienguyen Sep 24, 2025
f39a609
refactor(useForm): improve defaults update logic
brodienguyen Sep 24, 2025
bf61286
refactor(test-app): make window props reactive to page
brodienguyen Sep 24, 2025
874815a
refactor(test-app): use $props in Svelte pages
brodienguyen Sep 24, 2025
664c038
refactor(test-app): use $props in Svelte5 components
brodienguyen Sep 24, 2025
e5ed818
feat(useForm): add reactive getters for form data
brodienguyen Sep 24, 2025
bc59e37
refactor(remember): remove $ prefix from form references
brodienguyen Sep 24, 2025
b684a14
refactor(ManyGroups): use $props for prop destructuring
brodienguyen Sep 24, 2025
ae6817c
refactor(InstantReload): use $props for foo and bar
brodienguyen Sep 24, 2025
57c25d7
refactor(test-app): use $props destructuring in pages
brodienguyen Sep 24, 2025
cdec571
refactor(form-helper): remove $ prefix from form usage
brodienguyen Sep 24, 2025
0919055
refactor(WhenVisible): use #snippet blocks for slots
brodienguyen Sep 24, 2025
5969cd1
refactor(test-app): use $props in Svelte components
brodienguyen Sep 24, 2025
af3cf0f
refactor(FormHelper): update form type to InertiaFormRunes
brodienguyen Sep 24, 2025
351c05c
refactor(FormHelper): update to use InertiaFormRunes type
brodienguyen Sep 24, 2025
720744c
refactor(DeepMergeProps): use $props and $state helpers
brodienguyen Sep 24, 2025
a351d97
fix(useForm): update defaults and remember logic
brodienguyen Sep 24, 2025
0904ca9
refactor(useForm): improve Svelte 5 runes compatibility
brodienguyen Sep 24, 2025
a897d2e
test: skip tests for svelte5 package
brodienguyen Sep 24, 2025
fdc07f0
refactor(useForm): simplify and modernize form logic
brodienguyen Sep 24, 2025
cb60fff
refactor(form): migrate useForm to TypeScript store
brodienguyen Sep 24, 2025
da1be08
refactor(FormComponent): update error markup and layout
brodienguyen Sep 24, 2025
ef1bf65
refactor(svelte5): migrate usePrefetch to $state
brodienguyen Sep 24, 2025
1c8dfe1
refactor(svelte5): rewrite useRemember for Svelte 5
brodienguyen Sep 24, 2025
9bc6a48
fix(index): update imports for usePrefetch and useRemember
brodienguyen Sep 24, 2025
eed53a9
build(test-app): update Svelte and Vite dependencies
brodienguyen Sep 24, 2025
1a0cb87
lint: run pnpm run format
brodienguyen Sep 24, 2025
6ba2754
ci(workflows): add svelte5 to adapter matrix
brodienguyen Sep 25, 2025
a0107b9
Merge remote-tracking branch 'inertia/master' into new-package-svelte-5
puRe1337 Oct 31, 2025
ccc3d85
chore: update lock file
puRe1337 Oct 31, 2025
880260c
chore: update dependencies
puRe1337 Oct 31, 2025
353e03c
chore: update test-app packages and copied files from original svelte…
puRe1337 Oct 31, 2025
bd39ed2
refactor: migrate Render.svelte to Component and use RenderProps type…
puRe1337 Oct 31, 2025
86dc955
feat: purge page store completely and use state only
puRe1337 Oct 31, 2025
14bcd0a
refactor: use Component instead of ComponentType
puRe1337 Oct 31, 2025
d1c0f05
feat: migrated useForm to runes mode
puRe1337 Oct 31, 2025
9fc163a
chore: keep types in svelte5/index.ts of original package
puRe1337 Oct 31, 2025
afe3870
chore: types inside svelte/5links.ts and more syncing with current sv…
puRe1337 Oct 31, 2025
437cc90
fix: pass down a snapshot of current state to ensure core router can …
puRe1337 Oct 31, 2025
8778403
refactor: tidy up createInertiaApp
puRe1337 Oct 31, 2025
a374f8a
fix: dedupe axios version in lock file
puRe1337 Oct 31, 2025
b4ff8ea
fix: skip more test
puRe1337 Oct 31, 2025
43ae195
fix: App.svelte effect
puRe1337 Oct 31, 2025
22ead7e
refactor: tidy up Form.svelte
puRe1337 Oct 31, 2025
13a064d
feat: add InfiniteScroll but in runes style
puRe1337 Oct 31, 2025
f48fd8d
refactor: tidy up Link, WhenVisible
puRe1337 Oct 31, 2025
44f3cb3
chore: run sv migrate on test-app
puRe1337 Oct 31, 2025
c421a8a
refactor: tidy up WhenVisible component
puRe1337 Oct 31, 2025
7309b45
refactor: types in Deferred component & fix side-effects
puRe1337 Oct 31, 2025
9d1f527
refactor: dont take a snapshot of newPage inside page state
puRe1337 Oct 31, 2025
3ae5d95
refactor: tidyup PreseveEqualProps
puRe1337 Oct 31, 2025
8865267
chore: add missing Router component
puRe1337 Oct 31, 2025
2e3d86d
chore: add comment to resolveRenderProps
puRe1337 Oct 31, 2025
172ef40
chore: add type-check:test-app:svelte5 script to package.json
puRe1337 Oct 31, 2025
3d58470
fix: dont return mount
puRe1337 Oct 31, 2025
5ff46d3
chore: disable eslint on children in Grid test component
puRe1337 Oct 31, 2025
197ffac
fix: type errors
puRe1337 Oct 31, 2025
87e7a4b
format code-style
puRe1337 Oct 31, 2025
bca56f3
refactor: use attachment instead of manually handling
puRe1337 Oct 31, 2025
f031658
fix: set target version to ES2020 in build
puRe1337 Nov 1, 2025
f7255f2
fix(ci): prevent pnpm wildcard from matching both svelte and svelte5 …
puRe1337 Nov 3, 2025
900e72a
fix(ci): handle vue3 build in playwright workflow
puRe1337 Nov 3, 2025
a7a3fc5
fix: await setup in createInertiaApp
puRe1337 Nov 4, 2025
61f8b13
fix: improve nestedA comparison logic for preserveEqualProps
puRe1337 Nov 4, 2025
30f2814
feat: add missing 'dev:test-app:svelte5' script
puRe1337 Nov 4, 2025
26a0c70
feat: add lint script for svelte5 test app
puRe1337 Nov 4, 2025
1986f60
refactor: use types instead of any in PreserveEqualProps
puRe1337 Nov 4, 2025
e32e1a6
chore: replaced svelte-adapter with the new svelte5-adapter
puRe1337 Nov 26, 2025
8fb27b6
chore(ci): removed svelte5 adapter from ci
puRe1337 Nov 26, 2025
158df94
Merge remote-tracking branch 'inertia/master' into new-package-svelte-5
puRe1337 Nov 26, 2025
41f48ff
fix merge issues
puRe1337 Nov 26, 2025
efe105b
refactor: use svelte 5 syntax
puRe1337 Nov 26, 2025
d7cd8f5
adapt more to svelte 5 syntax
puRe1337 Nov 26, 2025
c4fc8a8
chore: refresh lockfile with master and added svelte5 packages
puRe1337 Nov 26, 2025
8c94df3
export data functions from Form Component again
puRe1337 Nov 26, 2025
d92c9de
wrap keys into derived
puRe1337 Nov 27, 2025
5b73976
lock svelte version in svelte-test-app
puRe1337 Nov 27, 2025
5191a19
Merge remote-tracking branch 'origin/master' into new-package-svelte-5
puRe1337 Nov 27, 2025
23fadc8
use form state instead of store
puRe1337 Nov 27, 2025
39f9486
converted ScrollAfterRender
puRe1337 Nov 27, 2025
5517717
format
puRe1337 Nov 27, 2025
75cd56e
added callbacks to Link component
puRe1337 Nov 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/playwright-chromium.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
run: pnpm install

- name: Build Inertia
run: pnpm -r --filter ./packages/core --filter ./packages/${{ matrix.adapter }}* build
run: pnpm -r --filter ./packages/core --filter "@inertiajs/${{ matrix.adapter == 'vue' && 'vue3' || matrix.adapter }}" build

- name: Install Playwright Browsers
run: pnpm playwright install chromium
Expand Down
12 changes: 6 additions & 6 deletions packages/svelte/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,21 @@
"package": "svelte-kit sync && svelte-package --input src"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^3.3.1",
"@sveltejs/adapter-auto": "^7.0.0",
"@sveltejs/kit": "^2.48.5",
"@sveltejs/package": "^2.5.4",
"@sveltejs/vite-plugin-svelte": "^3.1.2",
"@sveltejs/vite-plugin-svelte": "^6.2.1",
"axios": "^1.13.2",
"es-check": "^9.4.5",
"publint": "^0.2.12",
"svelte": "^4.2.20",
"publint": "^0.3.15",
"svelte": "^5.43.2",
"svelte-check": "^4.3.4",
"tslib": "^2.8.1",
"typescript": "^5.9.3",
"vite": "^5.4.21"
"vite": "^7.1.12"
},
"peerDependencies": {
"svelte": "^4.0.0 || ^5.0.0"
"svelte": "^5.0.0"
},
"dependencies": {
"@inertiajs/core": "workspace:*",
Expand Down
32 changes: 19 additions & 13 deletions packages/svelte/src/components/App.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<script context="module" lang="ts">
<script module lang="ts">
import type { ComponentResolver, ResolvedComponent } from '../types'
import { type Page, type PageProps } from '@inertiajs/core'

Expand All @@ -13,18 +13,25 @@
import type { LayoutType, LayoutResolver } from '../types'
import { router } from '@inertiajs/core'
import Render, { h, type RenderProps } from './Render.svelte'
import { setPage } from '../page'
import { setPage } from '../page.svelte'

export let initialComponent: InertiaAppProps['initialComponent']
export let initialPage: InertiaAppProps['initialPage']
export let resolveComponent: InertiaAppProps['resolveComponent']
interface Props {
initialComponent: InertiaAppProps['initialComponent']
initialPage: InertiaAppProps['initialPage']
resolveComponent: InertiaAppProps['resolveComponent']
}

const { initialComponent, initialPage, resolveComponent }: Props = $props()

let component = initialComponent
let key: number | null = null
let page = initialPage
let renderProps = resolveRenderProps(component, page, key)
let component = $state(initialComponent)
let key = $state<number | null>(null)
let page = $state(initialPage)
let renderProps = $derived.by<RenderProps>(() => resolveRenderProps(component, page, key))

setPage(page)
// Reactively update the global page state when local page state changes
$effect.pre(() => {
setPage(page)
})

const isServer = typeof window === 'undefined'

Expand All @@ -35,10 +42,8 @@
swapComponent: async (args) => {
component = args.component
page = args.page
key = args.preserveState ? key : Date.now()

renderProps = resolveRenderProps(component, page, key)
setPage(page)
key = args.preserveState ? key : Date.now()
},
})
}
Expand All @@ -47,6 +52,7 @@
* Resolves the render props for the current page component, including layouts.
*/
function resolveRenderProps(component: ResolvedComponent, page: Page, key: number | null = null): RenderProps {
// If the component does not exists, it will throw on component.default and component.layout here
const child = h(component.default, page.props, [], key)
const layout = component.layout

Expand Down
42 changes: 25 additions & 17 deletions packages/svelte/src/components/Deferred.svelte
Original file line number Diff line number Diff line change
@@ -1,34 +1,42 @@
<script lang="ts">
import { untrack } from 'svelte'
import { page } from '../index'
import { onDestroy } from 'svelte'

export let data: string | string[]
interface Props {
data: string | string[]
fallback?: import('svelte').Snippet
children?: import('svelte').Snippet
}

const keys = Array.isArray(data) ? data : [data]
let loaded = false
let { data, fallback, children }: Props = $props()

const isServer = typeof window === 'undefined'
const keys = $derived(Array.isArray(data) ? data : [data])
let loaded = $state(false)

const isServer = typeof window === 'undefined'
if (!isServer) {
const unsubscribe = page.subscribe(({ props }) => {
// Ensures the slot isn't loaded before the deferred props are available
window.queueMicrotask(() => {
loaded = keys.every((key) => typeof props[key] !== 'undefined')
})
})
// Use $effect to watch for changes in pageState.props
$effect(() => {
// Access pageState.props to make this effect reactive
const props = page.props

onDestroy(() => {
unsubscribe()
// Wrap this up into untrack, to make sure it doesn't gets picked as a depedency to retrigger the $effect
untrack(() => {
// Ensures the content isn't loaded before the deferred props are available
window.queueMicrotask(() => {
loaded = keys.every((key) => typeof props[key] !== 'undefined')
})
})
})
}

if (!$$slots.fallback) {
throw new Error('`<Deferred>` requires a `<svelte:fragment slot="fallback">` slot')
if (!fallback) {
throw new Error('`<Deferred>` requires a `fallback` snippet')
}
</script>

{#if loaded}
<slot />
{@render children?.()}
{:else}
<slot name="fallback" />
{@render fallback?.()}
{/if}
138 changes: 85 additions & 53 deletions packages/svelte/src/components/Form.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,42 +12,73 @@
} from '@inertiajs/core'
import { isEqual } from 'lodash-es'
import { onMount } from 'svelte'
import useForm from '../useForm'
import useForm from '../useForm.svelte'

const noop = () => undefined

export let action: FormComponentProps['action'] = ''
export let method: FormComponentProps['method'] = 'get'
export let headers: FormComponentProps['headers'] = {}
export let queryStringArrayFormat: FormComponentProps['queryStringArrayFormat'] = 'brackets'
export let errorBag: FormComponentProps['errorBag'] = null
export let showProgress: FormComponentProps['showProgress'] = true
export let transform: FormComponentProps['transform'] = (data) => data
export let options: FormComponentProps['options'] = {}
export let onCancelToken: FormComponentProps['onCancelToken'] = noop
export let onBefore: FormComponentProps['onBefore'] = noop
export let onStart: FormComponentProps['onStart'] = noop
export let onProgress: FormComponentProps['onProgress'] = noop
export let onFinish: FormComponentProps['onFinish'] = noop
export let onCancel: FormComponentProps['onCancel'] = noop
export let onSuccess: FormComponentProps['onSuccess'] = noop
export let onError: FormComponentProps['onError'] = noop
export let onSubmitComplete: FormComponentProps['onSubmitComplete'] = noop
export let disableWhileProcessing: boolean = false
export let invalidateCacheTags: FormComponentProps['invalidateCacheTags'] = []
export let resetOnError: FormComponentProps['resetOnError'] = false
export let resetOnSuccess: FormComponentProps['resetOnSuccess'] = false
export let setDefaultsOnSuccess: FormComponentProps['setDefaultsOnSuccess'] = false
interface Props {
action?: FormComponentProps['action']
method?: FormComponentProps['method']
headers?: FormComponentProps['headers']
queryStringArrayFormat?: FormComponentProps['queryStringArrayFormat']
errorBag?: FormComponentProps['errorBag']
showProgress?: FormComponentProps['showProgress']
transform?: FormComponentProps['transform']
options?: FormComponentProps['options']
onCancelToken?: FormComponentProps['onCancelToken']
onBefore?: FormComponentProps['onBefore']
onStart?: FormComponentProps['onStart']
onProgress?: FormComponentProps['onProgress']
onFinish?: FormComponentProps['onFinish']
onCancel?: FormComponentProps['onCancel']
onSuccess?: FormComponentProps['onSuccess']
onError?: FormComponentProps['onError']
onSubmitComplete?: FormComponentProps['onSubmitComplete']
disableWhileProcessing?: boolean
invalidateCacheTags?: FormComponentProps['invalidateCacheTags']
resetOnError?: FormComponentProps['resetOnError']
resetOnSuccess?: FormComponentProps['resetOnSuccess']
setDefaultsOnSuccess?: FormComponentProps['setDefaultsOnSuccess']
children?: import('svelte').Snippet<[any]>
[key: string]: any
}

let {
action = $bindable(''),
method = 'get',
headers = {},
queryStringArrayFormat = 'brackets',
errorBag = null,
showProgress = true,
transform = (data) => data,
options = {},
onCancelToken = noop,
onBefore = noop,
onStart = noop,
onProgress = noop,
onFinish = noop,
onCancel = noop,
onSuccess = noop,
onError = noop,
onSubmitComplete = noop,
disableWhileProcessing = false,
invalidateCacheTags = [],
resetOnError = false,
resetOnSuccess = false,
setDefaultsOnSuccess = false,
children,
...rest
}: Props = $props()

type FormSubmitOptions = Omit<VisitOptions, 'data' | 'onPrefetched' | 'onPrefetching'>

const form = useForm({})
let formElement: HTMLFormElement
let isDirty = false
let formElement: HTMLFormElement = $state(null!)
let isDirty = $state(false)
let defaultData: FormData = new FormData()

$: _method = isUrlMethodPair(action) ? action.method : ((method ?? 'get').toLowerCase() as Method)
$: _action = isUrlMethodPair(action) ? action.url : (action as string)
const _method = $derived(isUrlMethodPair(action) ? action.method : ((method ?? 'get').toLowerCase() as Method))
const _action = $derived(isUrlMethodPair(action) ? action.url : (action as string))

export function getFormData(): FormData {
return new FormData(formElement)
Expand Down Expand Up @@ -119,7 +150,7 @@
...options,
}

$form.transform(() => transform!(_data)).submit(_method, url, submitOptions)
form.transform(() => transform!(_data)).submit(_method, url, submitOptions)
}

function handleSubmit(event: Event) {
Expand All @@ -141,21 +172,21 @@

export function clearErrors(...fields: string[]) {
// @ts-expect-error
$form.clearErrors(...fields)
form.clearErrors(...fields)
}

export function resetAndClearErrors(...fields: string[]) {
// @ts-expect-error
$form.clearErrors(...fields)
form.clearErrors(...fields)
reset(...fields)
}

export function setError(field: string | object, value?: string) {
if (typeof field === 'string') {
// @ts-expect-error
$form.setError(field, value)
form.setError(field, value)
} else {
$form.setError(field)
form.setError(field)
}
}

Expand All @@ -174,32 +205,33 @@
formEvents.forEach((e) => formElement?.removeEventListener(e, updateDirtyState))
}
})
$: slotErrors = $form.errors as Errors
const slotErrors = $derived(form.errors as Errors)
</script>

{/* @ts-expect-error method type does not match here*/ null}
<form
bind:this={formElement}
action={_action}
method={_method}
on:submit={handleSubmit}
on:reset={handleReset}
{...$$restProps}
inert={disableWhileProcessing && $form.processing ? true : undefined}
onsubmit={handleSubmit}
onreset={handleReset}
{...rest}
inert={disableWhileProcessing && form.processing ? true : undefined}
>
<slot
errors={slotErrors}
hasErrors={$form.hasErrors}
processing={$form.processing}
progress={$form.progress}
wasSuccessful={$form.wasSuccessful}
recentlySuccessful={$form.recentlySuccessful}
{clearErrors}
{resetAndClearErrors}
{setError}
{isDirty}
{submit}
{defaults}
{getData}
{getFormData}
/>
{@render children?.({
errors: slotErrors,
hasErrors: form.hasErrors,
processing: form.processing,
progress: form.progress,
wasSuccessful: form.wasSuccessful,
recentlySuccessful: form.recentlySuccessful,
clearErrors,
resetAndClearErrors,
setError,
isDirty,
submit,
defaults,
getData,
getFormData,
})}
</form>
Loading