@inertiajs/svelte 3.0.0-beta.1 → 3.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/App.svelte +4 -0
- package/dist/createInertiaApp.d.ts +12 -4
- package/dist/createInertiaApp.js +16 -6
- package/dist/layoutProps.svelte.d.ts +1 -2
- package/dist/layoutProps.svelte.js +15 -4
- package/dist/types.d.ts +2 -2
- package/package.json +3 -3
- package/resources/boost/skills/inertia-svelte-development/SKILL.blade.php +241 -107
|
@@ -35,6 +35,10 @@
|
|
|
35
35
|
let page = $state({ ...initialPage, flash: initialPage.flash ?? {} })
|
|
36
36
|
let renderProps = $derived.by<RenderProps>(() => resolveRenderProps(component, page, key))
|
|
37
37
|
|
|
38
|
+
// Synchronous initialization so the global page store is populated during SSR
|
|
39
|
+
// ($effect.pre does not run during Svelte 5 SSR)
|
|
40
|
+
setPage(page)
|
|
41
|
+
|
|
38
42
|
// Reactively update the global page state when local page state changes
|
|
39
43
|
$effect.pre(() => {
|
|
40
44
|
setPage(page)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type CreateInertiaAppOptions, type CreateInertiaAppOptionsForCSR, type InertiaAppSSRResponse, type Page, type PageProps } from '@inertiajs/core';
|
|
1
|
+
import { type CreateInertiaAppOptions, type CreateInertiaAppOptionsForCSR, type InertiaAppSSRResponse, type Page, type PageProps, type SharedPageProps } from '@inertiajs/core';
|
|
2
2
|
import App, { type InertiaAppProps } from './components/App.svelte';
|
|
3
3
|
import type { ComponentResolver, SvelteInertiaAppConfig } from './types';
|
|
4
4
|
type SvelteRenderResult = {
|
|
@@ -10,14 +10,22 @@ type SetupOptions<SharedProps extends PageProps> = {
|
|
|
10
10
|
App: typeof App;
|
|
11
11
|
props: InertiaAppProps<SharedProps>;
|
|
12
12
|
};
|
|
13
|
-
type InertiaAppOptionsForCSR<SharedProps extends PageProps> = CreateInertiaAppOptionsForCSR<SharedProps, ComponentResolver, SetupOptions<SharedProps>, SvelteRenderResult | void, SvelteInertiaAppConfig
|
|
13
|
+
type InertiaAppOptionsForCSR<SharedProps extends PageProps> = CreateInertiaAppOptionsForCSR<SharedProps, ComponentResolver, SetupOptions<SharedProps>, SvelteRenderResult | void, SvelteInertiaAppConfig> & {
|
|
14
|
+
withApp?: (context: Map<any, any>, options: {
|
|
15
|
+
ssr: boolean;
|
|
16
|
+
}) => void;
|
|
17
|
+
};
|
|
14
18
|
type InertiaAppOptionsAuto<SharedProps extends PageProps> = CreateInertiaAppOptions<ComponentResolver, SetupOptions<SharedProps>, SvelteRenderResult | void, SvelteInertiaAppConfig> & {
|
|
15
19
|
page?: Page<SharedProps>;
|
|
20
|
+
withApp?: (context: Map<any, any>, options: {
|
|
21
|
+
ssr: boolean;
|
|
22
|
+
}) => void;
|
|
16
23
|
};
|
|
17
24
|
type SvelteServerRender = (component: typeof App, options: {
|
|
18
25
|
props: InertiaAppProps<PageProps>;
|
|
26
|
+
context?: Map<any, any>;
|
|
19
27
|
}) => SvelteRenderResult;
|
|
20
28
|
type RenderFunction<SharedProps extends PageProps> = (page: Page<SharedProps>, render: SvelteServerRender) => Promise<InertiaAppSSRResponse>;
|
|
21
|
-
export default function createInertiaApp<SharedProps extends PageProps = PageProps>(options: InertiaAppOptionsForCSR<SharedProps>): Promise<InertiaAppSSRResponse | void>;
|
|
22
|
-
export default function createInertiaApp<SharedProps extends PageProps = PageProps>(options?: InertiaAppOptionsAuto<SharedProps>): Promise<void | RenderFunction<SharedProps>>;
|
|
29
|
+
export default function createInertiaApp<SharedProps extends PageProps = PageProps & SharedPageProps>(options: InertiaAppOptionsForCSR<SharedProps>): Promise<InertiaAppSSRResponse | void>;
|
|
30
|
+
export default function createInertiaApp<SharedProps extends PageProps = PageProps & SharedPageProps>(options?: InertiaAppOptionsAuto<SharedProps>): Promise<void | RenderFunction<SharedProps>>;
|
|
23
31
|
export {};
|
package/dist/createInertiaApp.js
CHANGED
|
@@ -2,7 +2,7 @@ import { buildSSRBody, getInitialPageFromDOM, http as httpModule, router, setupP
|
|
|
2
2
|
import { hydrate, mount } from 'svelte';
|
|
3
3
|
import App, {} from './components/App.svelte';
|
|
4
4
|
import { config } from './index';
|
|
5
|
-
export default async function createInertiaApp({ id = 'app', resolve, setup, progress = {}, page, defaults = {}, http, layout, } = {}) {
|
|
5
|
+
export default async function createInertiaApp({ id = 'app', resolve, setup, progress = {}, page, defaults = {}, http, layout, withApp, } = {}) {
|
|
6
6
|
config.replace(defaults);
|
|
7
7
|
if (http) {
|
|
8
8
|
httpModule.setClient(http);
|
|
@@ -27,7 +27,11 @@ export default async function createInertiaApp({ id = 'app', resolve, setup, pro
|
|
|
27
27
|
svelteApp = result;
|
|
28
28
|
}
|
|
29
29
|
else {
|
|
30
|
-
|
|
30
|
+
const context = new Map();
|
|
31
|
+
if (withApp) {
|
|
32
|
+
withApp(context, { ssr: true });
|
|
33
|
+
}
|
|
34
|
+
svelteApp = render(App, { props, context });
|
|
31
35
|
}
|
|
32
36
|
const body = buildSSRBody(id, page, svelteApp.body);
|
|
33
37
|
return {
|
|
@@ -60,11 +64,17 @@ export default async function createInertiaApp({ id = 'app', resolve, setup, pro
|
|
|
60
64
|
if (setup) {
|
|
61
65
|
await setup({ el: target, App, props });
|
|
62
66
|
}
|
|
63
|
-
else if (target.hasAttribute('data-server-rendered')) {
|
|
64
|
-
hydrate(App, { target, props });
|
|
65
|
-
}
|
|
66
67
|
else {
|
|
67
|
-
|
|
68
|
+
const context = new Map();
|
|
69
|
+
if (withApp) {
|
|
70
|
+
withApp(context, { ssr: false });
|
|
71
|
+
}
|
|
72
|
+
if (target.hasAttribute('data-server-rendered')) {
|
|
73
|
+
hydrate(App, { target, props, context });
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
mount(App, { target, props, context });
|
|
77
|
+
}
|
|
68
78
|
}
|
|
69
79
|
if (progress) {
|
|
70
80
|
setupProgress(progress);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { type Readable } from 'svelte/store';
|
|
2
1
|
export declare function setLayoutProps(props: Record<string, unknown>): void;
|
|
3
2
|
export declare function setLayoutPropsFor(name: string, props: Record<string, unknown>): void;
|
|
4
3
|
export declare function resetLayoutProps(): void;
|
|
5
4
|
export declare const LAYOUT_CONTEXT_KEY: unique symbol;
|
|
6
|
-
export declare function useLayoutProps<T extends Record<string, unknown>>(defaults: T):
|
|
5
|
+
export declare function useLayoutProps<T extends Record<string, unknown>>(defaults: T): T;
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import { createLayoutPropsStore, mergeLayoutProps } from '@inertiajs/core';
|
|
2
2
|
import { getContext } from 'svelte';
|
|
3
|
-
import { readable } from 'svelte/store';
|
|
4
3
|
const store = createLayoutPropsStore();
|
|
4
|
+
const storeState = $state({
|
|
5
|
+
shared: {},
|
|
6
|
+
named: {},
|
|
7
|
+
});
|
|
8
|
+
store.subscribe(() => {
|
|
9
|
+
const snapshot = store.get();
|
|
10
|
+
storeState.shared = snapshot.shared;
|
|
11
|
+
storeState.named = snapshot.named;
|
|
12
|
+
});
|
|
5
13
|
export function setLayoutProps(props) {
|
|
6
14
|
store.set(props);
|
|
7
15
|
}
|
|
@@ -17,9 +25,12 @@ export function useLayoutProps(defaults) {
|
|
|
17
25
|
const resolve = () => {
|
|
18
26
|
const staticProps = context?.staticProps ?? {};
|
|
19
27
|
const name = context?.name;
|
|
20
|
-
const { shared, named }
|
|
21
|
-
const dynamicProps = name ? { ...shared, ...named[name] } : shared;
|
|
28
|
+
const dynamicProps = name ? { ...storeState.shared, ...(storeState.named[name] ?? {}) } : storeState.shared;
|
|
22
29
|
return mergeLayoutProps(defaults, staticProps, dynamicProps);
|
|
23
30
|
};
|
|
24
|
-
|
|
31
|
+
const state = $state(resolve());
|
|
32
|
+
$effect.pre(() => {
|
|
33
|
+
Object.assign(state, resolve());
|
|
34
|
+
});
|
|
35
|
+
return state;
|
|
25
36
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { type Page } from '@inertiajs/core';
|
|
1
|
+
import { type Page, type SharedPageProps } from '@inertiajs/core';
|
|
2
2
|
import type { Component } from 'svelte';
|
|
3
3
|
import type { RenderFunction, RenderProps } from './components/Render.svelte';
|
|
4
|
-
export type ComponentResolver = (name: string, page?: Page) => ResolvedComponent | Promise<ResolvedComponent>;
|
|
4
|
+
export type ComponentResolver = (name: string, page?: Page<SharedPageProps>) => ResolvedComponent | Promise<ResolvedComponent>;
|
|
5
5
|
export type LayoutResolver = (h: RenderFunction, page: RenderProps) => RenderProps;
|
|
6
6
|
export type LayoutTuple = [Component, Record<string, unknown>?];
|
|
7
7
|
export type LayoutObject = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inertiajs/svelte",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "The Svelte adapter for Inertia.js",
|
|
6
6
|
"contributors": [
|
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@types/lodash-es": "^4.17.12",
|
|
57
|
-
"laravel-precognition": "2.0.0-beta.
|
|
57
|
+
"laravel-precognition": "2.0.0-beta.3",
|
|
58
58
|
"lodash-es": "^4.17.23",
|
|
59
|
-
"@inertiajs/core": "3.0.0-beta.
|
|
59
|
+
"@inertiajs/core": "3.0.0-beta.3"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
62
|
"build": "pnpm package && svelte-check --tsconfig ./tsconfig.json && publint",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: inertia-svelte-development
|
|
3
|
-
description: "Develops Inertia.js
|
|
3
|
+
description: "Develops Inertia.js v3 Svelte 5 client-side applications. Activates when creating Svelte pages, forms, or navigation; using Link, Form, useForm, useHttp, useLayoutProps, or router; working with deferred props, prefetching, optimistic updates, instant visits, or polling; or when user mentions Svelte with Inertia, Svelte pages, Svelte forms, or Svelte navigation."
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
6
|
author: laravel
|
|
@@ -15,14 +15,24 @@ metadata:
|
|
|
15
15
|
Activate this skill when:
|
|
16
16
|
|
|
17
17
|
- Creating or modifying Svelte page components for Inertia
|
|
18
|
-
- Working with forms in Svelte (using `<Form
|
|
18
|
+
- Working with forms in Svelte (using `<Form>`, `useForm`, or `useHttp`)
|
|
19
19
|
- Implementing client-side navigation with `<Link>` or `router`
|
|
20
|
-
- Using
|
|
20
|
+
- Using v3 features: deferred props, prefetching, optimistic updates, instant visits, layout props, HTTP requests, WhenVisible, InfiniteScroll, once props, flash data, or polling
|
|
21
21
|
- Building Svelte-specific features with the Inertia protocol
|
|
22
22
|
|
|
23
|
+
## Important: Svelte 5 Required
|
|
24
|
+
|
|
25
|
+
Inertia v3 requires Svelte 5. All code must use Svelte 5 runes syntax:
|
|
26
|
+
|
|
27
|
+
- Use `let { prop } = $props()` (not `export let prop`)
|
|
28
|
+
- Use `onclick` (not `on:click`)
|
|
29
|
+
- Use `$derived()` for reactive values (not `$:`)
|
|
30
|
+
- Use `{#snippet}` for named slots (not `slot="name"`)
|
|
31
|
+
- Use `{@render children()}` for default slot content
|
|
32
|
+
|
|
23
33
|
## Documentation
|
|
24
34
|
|
|
25
|
-
Use `search-docs` for detailed Inertia
|
|
35
|
+
Use `search-docs` for detailed Inertia v3 Svelte patterns and documentation.
|
|
26
36
|
|
|
27
37
|
## Basic Usage
|
|
28
38
|
|
|
@@ -34,7 +44,7 @@ Svelte page components should be placed in the `{{ $assist->inertia()->pagesDire
|
|
|
34
44
|
|
|
35
45
|
@boostsnippet("Basic Svelte Page Component", "svelte")
|
|
36
46
|
<script>
|
|
37
|
-
|
|
47
|
+
let { users } = $props()
|
|
38
48
|
</script>
|
|
39
49
|
|
|
40
50
|
<div>
|
|
@@ -111,31 +121,33 @@ function createUser() {
|
|
|
111
121
|
@if($assist->inertia()->hasFormComponent())
|
|
112
122
|
### Form Component (Recommended)
|
|
113
123
|
|
|
114
|
-
The recommended way to build forms is with the `<Form>` component:
|
|
124
|
+
The recommended way to build forms is with the `<Form>` component. In Svelte 5, use `{#snippet}` for the default slot:
|
|
115
125
|
|
|
116
126
|
@boostsnippet("Form Component Example", "svelte")
|
|
117
127
|
<script>
|
|
118
128
|
import { Form } from '@inertiajs/svelte'
|
|
119
129
|
</script>
|
|
120
130
|
|
|
121
|
-
<Form action="/users" method="post"
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
{processing
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
131
|
+
<Form action="/users" method="post">
|
|
132
|
+
{#snippet children({ errors, processing, wasSuccessful })}
|
|
133
|
+
<input type="text" name="name" />
|
|
134
|
+
{#if errors.name}
|
|
135
|
+
<div>{errors.name}</div>
|
|
136
|
+
{/if}
|
|
137
|
+
|
|
138
|
+
<input type="email" name="email" />
|
|
139
|
+
{#if errors.email}
|
|
140
|
+
<div>{errors.email}</div>
|
|
141
|
+
{/if}
|
|
142
|
+
|
|
143
|
+
<button type="submit" disabled={processing}>
|
|
144
|
+
{processing ? 'Creating...' : 'Create User'}
|
|
145
|
+
</button>
|
|
146
|
+
|
|
147
|
+
{#if wasSuccessful}
|
|
148
|
+
<div>User created!</div>
|
|
149
|
+
{/if}
|
|
150
|
+
{/snippet}
|
|
139
151
|
</Form>
|
|
140
152
|
@endboostsnippet
|
|
141
153
|
|
|
@@ -146,40 +158,40 @@ import { Form } from '@inertiajs/svelte'
|
|
|
146
158
|
import { Form } from '@inertiajs/svelte'
|
|
147
159
|
</script>
|
|
148
160
|
|
|
149
|
-
<Form
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
{/
|
|
161
|
+
<Form action="/users" method="post">
|
|
162
|
+
{#snippet children({
|
|
163
|
+
errors,
|
|
164
|
+
hasErrors,
|
|
165
|
+
processing,
|
|
166
|
+
progress,
|
|
167
|
+
wasSuccessful,
|
|
168
|
+
recentlySuccessful,
|
|
169
|
+
clearErrors,
|
|
170
|
+
resetAndClearErrors,
|
|
171
|
+
defaults,
|
|
172
|
+
isDirty,
|
|
173
|
+
reset,
|
|
174
|
+
submit
|
|
175
|
+
})}
|
|
176
|
+
<input type="text" name="name" value={defaults.name} />
|
|
177
|
+
{#if errors.name}
|
|
178
|
+
<div>{errors.name}</div>
|
|
179
|
+
{/if}
|
|
180
|
+
|
|
181
|
+
<button type="submit" disabled={processing}>
|
|
182
|
+
{processing ? 'Saving...' : 'Save'}
|
|
183
|
+
</button>
|
|
184
|
+
|
|
185
|
+
{#if progress}
|
|
186
|
+
<progress value={progress.percentage} max="100">
|
|
187
|
+
{progress.percentage}%
|
|
188
|
+
</progress>
|
|
189
|
+
{/if}
|
|
190
|
+
|
|
191
|
+
{#if wasSuccessful}
|
|
192
|
+
<div>Saved!</div>
|
|
193
|
+
{/if}
|
|
194
|
+
{/snippet}
|
|
183
195
|
</Form>
|
|
184
196
|
@endboostsnippet
|
|
185
197
|
|
|
@@ -199,23 +211,17 @@ Use the `search-docs` tool with a query of `form component resetting` for detail
|
|
|
199
211
|
import { Form } from '@inertiajs/svelte'
|
|
200
212
|
</script>
|
|
201
213
|
|
|
202
|
-
<Form
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
{
|
|
213
|
-
<div>{errors.name}</div>
|
|
214
|
-
{/if}
|
|
215
|
-
|
|
216
|
-
<button type="submit" disabled={processing}>
|
|
217
|
-
Submit
|
|
218
|
-
</button>
|
|
214
|
+
<Form action="/users" method="post" resetOnSuccess setDefaultsOnSuccess>
|
|
215
|
+
{#snippet children({ errors, processing, wasSuccessful })}
|
|
216
|
+
<input type="text" name="name" />
|
|
217
|
+
{#if errors.name}
|
|
218
|
+
<div>{errors.name}</div>
|
|
219
|
+
{/if}
|
|
220
|
+
|
|
221
|
+
<button type="submit" disabled={processing}>
|
|
222
|
+
Submit
|
|
223
|
+
</button>
|
|
224
|
+
{/snippet}
|
|
219
225
|
</Form>
|
|
220
226
|
@endboostsnippet
|
|
221
227
|
@else
|
|
@@ -244,36 +250,166 @@ const form = useForm({
|
|
|
244
250
|
password: '',
|
|
245
251
|
})
|
|
246
252
|
|
|
247
|
-
function submit() {
|
|
248
|
-
|
|
249
|
-
|
|
253
|
+
function submit(e) {
|
|
254
|
+
e.preventDefault()
|
|
255
|
+
form.post('/users', {
|
|
256
|
+
onSuccess: () => form.reset('password'),
|
|
250
257
|
})
|
|
251
258
|
}
|
|
252
259
|
</script>
|
|
253
260
|
|
|
254
|
-
<form
|
|
255
|
-
<input type="text" bind:value={
|
|
256
|
-
{#if
|
|
257
|
-
<div>{
|
|
261
|
+
<form onsubmit={submit}>
|
|
262
|
+
<input type="text" bind:value={form.name} />
|
|
263
|
+
{#if form.errors.name}
|
|
264
|
+
<div>{form.errors.name}</div>
|
|
258
265
|
{/if}
|
|
259
266
|
|
|
260
|
-
<input type="email" bind:value={
|
|
261
|
-
{#if
|
|
262
|
-
<div>{
|
|
267
|
+
<input type="email" bind:value={form.email} />
|
|
268
|
+
{#if form.errors.email}
|
|
269
|
+
<div>{form.errors.email}</div>
|
|
263
270
|
{/if}
|
|
264
271
|
|
|
265
|
-
<input type="password" bind:value={
|
|
266
|
-
{#if
|
|
267
|
-
<div>{
|
|
272
|
+
<input type="password" bind:value={form.password} />
|
|
273
|
+
{#if form.errors.password}
|
|
274
|
+
<div>{form.errors.password}</div>
|
|
268
275
|
{/if}
|
|
269
276
|
|
|
270
|
-
<button type="submit" disabled={
|
|
277
|
+
<button type="submit" disabled={form.processing}>
|
|
271
278
|
Create User
|
|
272
279
|
</button>
|
|
273
280
|
</form>
|
|
274
281
|
@endboostsnippet
|
|
275
282
|
|
|
276
|
-
## Inertia
|
|
283
|
+
## Inertia v3 Features
|
|
284
|
+
|
|
285
|
+
### HTTP Requests
|
|
286
|
+
|
|
287
|
+
Use the `useHttp` hook for standalone HTTP requests that do not trigger Inertia page visits. It provides the same developer experience as `useForm`, but for plain JSON endpoints.
|
|
288
|
+
|
|
289
|
+
@boostsnippet("useHttp Example", "svelte")
|
|
290
|
+
<script>
|
|
291
|
+
import { useHttp } from '@inertiajs/svelte'
|
|
292
|
+
|
|
293
|
+
const http = useHttp({
|
|
294
|
+
query: '',
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
function search() {
|
|
298
|
+
http.get('/api/search', {
|
|
299
|
+
onSuccess: (response) => {
|
|
300
|
+
console.log(response)
|
|
301
|
+
},
|
|
302
|
+
})
|
|
303
|
+
}
|
|
304
|
+
</script>
|
|
305
|
+
|
|
306
|
+
<input bind:value={http.query} oninput={search} />
|
|
307
|
+
{#if http.processing}
|
|
308
|
+
<div>Searching...</div>
|
|
309
|
+
{/if}
|
|
310
|
+
@endboostsnippet
|
|
311
|
+
|
|
312
|
+
### Optimistic Updates
|
|
313
|
+
|
|
314
|
+
Apply data changes instantly before the server responds, with automatic rollback on failure:
|
|
315
|
+
|
|
316
|
+
@boostsnippet("Optimistic Update with Router", "svelte")
|
|
317
|
+
<script>
|
|
318
|
+
import { router } from '@inertiajs/svelte'
|
|
319
|
+
|
|
320
|
+
function like(post) {
|
|
321
|
+
router.optimistic((props) => ({
|
|
322
|
+
post: {
|
|
323
|
+
...props.post,
|
|
324
|
+
likes: props.post.likes + 1,
|
|
325
|
+
},
|
|
326
|
+
})).post(`/posts/${post.id}/like`)
|
|
327
|
+
}
|
|
328
|
+
</script>
|
|
329
|
+
@endboostsnippet
|
|
330
|
+
|
|
331
|
+
Optimistic updates also work with `useForm` and the `<Form>` component:
|
|
332
|
+
|
|
333
|
+
@boostsnippet("Optimistic Update with Form Component", "svelte")
|
|
334
|
+
<script>
|
|
335
|
+
import { Form } from '@inertiajs/svelte'
|
|
336
|
+
</script>
|
|
337
|
+
|
|
338
|
+
<Form
|
|
339
|
+
action="/todos"
|
|
340
|
+
method="post"
|
|
341
|
+
optimistic={(props, data) => ({
|
|
342
|
+
todos: [...props.todos, { id: Date.now(), name: data.name, done: false }],
|
|
343
|
+
})}
|
|
344
|
+
>
|
|
345
|
+
{#snippet children({ processing })}
|
|
346
|
+
<input type="text" name="name" />
|
|
347
|
+
<button type="submit" disabled={processing}>Add Todo</button>
|
|
348
|
+
{/snippet}
|
|
349
|
+
</Form>
|
|
350
|
+
@endboostsnippet
|
|
351
|
+
|
|
352
|
+
### Instant Visits
|
|
353
|
+
|
|
354
|
+
Navigate to a new page immediately without waiting for the server response. The target component renders right away with shared props, while page-specific props load in the background.
|
|
355
|
+
|
|
356
|
+
@verbatim
|
|
357
|
+
@boostsnippet("Instant Visit with Link", "svelte")
|
|
358
|
+
<script>
|
|
359
|
+
import { Link, inertia } from '@inertiajs/svelte'
|
|
360
|
+
</script>
|
|
361
|
+
|
|
362
|
+
<Link href="/dashboard" component="Dashboard">Dashboard</Link>
|
|
363
|
+
|
|
364
|
+
<a href="/dashboard" use:inertia={{ component: 'Dashboard' }}>Dashboard</a>
|
|
365
|
+
|
|
366
|
+
<Link
|
|
367
|
+
href="/posts/1"
|
|
368
|
+
component="Posts/Show"
|
|
369
|
+
pageProps={{ post: { id: 1, title: 'My Post' } }}
|
|
370
|
+
>
|
|
371
|
+
View Post
|
|
372
|
+
</Link>
|
|
373
|
+
@endboostsnippet
|
|
374
|
+
@endverbatim
|
|
375
|
+
|
|
376
|
+
### Layout Props
|
|
377
|
+
|
|
378
|
+
Share dynamic data between pages and persistent layouts:
|
|
379
|
+
|
|
380
|
+
@boostsnippet("Layout Props in Layout", "svelte")
|
|
381
|
+
<script>
|
|
382
|
+
import { useLayoutProps } from '@inertiajs/svelte'
|
|
383
|
+
|
|
384
|
+
const layout = useLayoutProps({
|
|
385
|
+
title: 'My App',
|
|
386
|
+
showSidebar: true,
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
let { children } = $props()
|
|
390
|
+
</script>
|
|
391
|
+
|
|
392
|
+
<header>{layout.title}</header>
|
|
393
|
+
{#if layout.showSidebar}
|
|
394
|
+
<aside>Sidebar</aside>
|
|
395
|
+
{/if}
|
|
396
|
+
<main>
|
|
397
|
+
{@render children()}
|
|
398
|
+
</main>
|
|
399
|
+
@endboostsnippet
|
|
400
|
+
|
|
401
|
+
@boostsnippet("Setting Layout Props from Page", "svelte")
|
|
402
|
+
<script>
|
|
403
|
+
import { setLayoutProps } from '@inertiajs/svelte'
|
|
404
|
+
|
|
405
|
+
setLayoutProps({
|
|
406
|
+
title: 'Dashboard',
|
|
407
|
+
showSidebar: false,
|
|
408
|
+
})
|
|
409
|
+
</script>
|
|
410
|
+
|
|
411
|
+
<h1>Dashboard</h1>
|
|
412
|
+
@endboostsnippet
|
|
277
413
|
|
|
278
414
|
### Deferred Props
|
|
279
415
|
|
|
@@ -281,7 +417,7 @@ Use deferred props to load data after initial page render:
|
|
|
281
417
|
|
|
282
418
|
@boostsnippet("Deferred Props with Empty State", "svelte")
|
|
283
419
|
<script>
|
|
284
|
-
|
|
420
|
+
let { users } = $props()
|
|
285
421
|
</script>
|
|
286
422
|
|
|
287
423
|
<div>
|
|
@@ -308,20 +444,16 @@ Automatically refresh data at intervals:
|
|
|
308
444
|
@boostsnippet("Polling Example", "svelte")
|
|
309
445
|
<script>
|
|
310
446
|
import { router } from '@inertiajs/svelte'
|
|
311
|
-
import { onMount
|
|
312
|
-
|
|
313
|
-
export let stats
|
|
447
|
+
import { onMount } from 'svelte'
|
|
314
448
|
|
|
315
|
-
let
|
|
449
|
+
let { stats } = $props()
|
|
316
450
|
|
|
317
451
|
onMount(() => {
|
|
318
|
-
interval = setInterval(() => {
|
|
452
|
+
const interval = setInterval(() => {
|
|
319
453
|
router.reload({ only: ['stats'] })
|
|
320
|
-
}, 5000)
|
|
321
|
-
})
|
|
454
|
+
}, 5000)
|
|
322
455
|
|
|
323
|
-
|
|
324
|
-
clearInterval(interval)
|
|
456
|
+
return () => clearInterval(interval)
|
|
325
457
|
})
|
|
326
458
|
</script>
|
|
327
459
|
|
|
@@ -339,22 +471,21 @@ Lazy-load a prop when an element scrolls into view. Useful for deferring expensi
|
|
|
339
471
|
<script>
|
|
340
472
|
import { WhenVisible } from '@inertiajs/svelte'
|
|
341
473
|
|
|
342
|
-
|
|
474
|
+
let { stats } = $props()
|
|
343
475
|
</script>
|
|
344
476
|
|
|
345
477
|
<div>
|
|
346
478
|
<h1>Dashboard</h1>
|
|
347
479
|
|
|
348
|
-
<!-- stats prop is loaded only when this section scrolls into view -->
|
|
349
480
|
<WhenVisible data="stats" buffer={200}>
|
|
481
|
+
{#snippet fallback()}
|
|
482
|
+
<div class="animate-pulse">Loading stats...</div>
|
|
483
|
+
{/snippet}
|
|
484
|
+
|
|
350
485
|
<div>
|
|
351
486
|
<p>Total Users: {stats.total_users}</p>
|
|
352
487
|
<p>Revenue: {stats.revenue}</p>
|
|
353
488
|
</div>
|
|
354
|
-
|
|
355
|
-
<svelte:fragment slot="fallback">
|
|
356
|
-
<div class="animate-pulse">Loading stats...</div>
|
|
357
|
-
</svelte:fragment>
|
|
358
489
|
</WhenVisible>
|
|
359
490
|
</div>
|
|
360
491
|
@endboostsnippet
|
|
@@ -366,7 +497,10 @@ Server-side patterns (Inertia::render, props, middleware) are covered in inertia
|
|
|
366
497
|
## Common Pitfalls
|
|
367
498
|
|
|
368
499
|
- Using traditional `<a>` links instead of Inertia's `<Link>` component (breaks SPA behavior)
|
|
500
|
+
- Using Svelte 4 syntax (`export let`, `on:click`, `$:`, `slot`) instead of Svelte 5 runes (`$props()`, `onclick`, `$derived()`, `{#snippet}`)
|
|
369
501
|
- Forgetting to add loading states (skeleton screens) when using deferred props
|
|
370
502
|
- Not handling the `undefined` state of deferred props before data loads
|
|
371
|
-
- Using `<form>` without preventing default submission (use `<Form>` component or `
|
|
503
|
+
- Using `<form>` without preventing default submission (use `<Form>` component or call `e.preventDefault()` in the `onsubmit` handler)
|
|
372
504
|
- Forgetting to check if `<Form>` component is available in your Inertia version
|
|
505
|
+
- Using `router.cancel()` instead of `router.cancelAll()` (v3 breaking change)
|
|
506
|
+
- Using `router.on('invalid', ...)` or `router.on('exception', ...)` instead of the renamed `httpException` and `networkError` events
|