Skip to content

Commit c79339d

Browse files
committed
Merge branch 'main' into form-precognition
2 parents 9345282 + c02c558 commit c79339d

File tree

9 files changed

+359
-1
lines changed

9 files changed

+359
-1
lines changed

docs.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@
121121
"v2/data-props/partial-reloads",
122122
"v2/data-props/deferred-props",
123123
"v2/data-props/merging-props",
124+
"v2/data-props/once-props",
124125
"v2/data-props/polling",
125126
"v2/data-props/prefetching",
126127
"v2/data-props/load-when-visible",

v2/core-concepts/the-protocol.mdx

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ The following headers are automatically sent by Inertia when making requests. Yo
143143
Indicates whether the requested data should be appended or prepended when using [Infinite scroll](/v2/data-props/infinite-scroll).
144144
</ParamField>
145145

146+
<ParamField header="X-Inertia-Except-Once-Props" type="string">
147+
Comma-separated list of non-expired [once prop](/v2/data-props/once-props) keys already loaded on the client. The server will skip resolving these props unless explicitly requested via a partial reload or force refreshed server-side.
148+
</ParamField>
149+
146150
The following headers are used for [Precognition](/v2/the-basics/forms#precognition) validation requests.
147151

148152
<ParamField header="Precognition" type="boolean">
@@ -235,6 +239,10 @@ Inertia shares data between the server and client via a page object. This object
235239
Configuration for client-side [lazy loading of props](/v2/data-props/deferred-props).
236240
</ParamField>
237241

242+
<ParamField body="onceProps" type="object">
243+
Configuration for [once props](/v2/data-props/once-props) that should only be resolved once and reused on subsequent pages. Each entry maps a key to an object containing the `prop` name and optional `expiresAt` timestamp (in milliseconds).
244+
</ParamField>
245+
238246
On standard full page visits, the page object is JSON encoded into the `data-page` attribute in the root `<div>`. On Inertia visits (as indicated by the presence of the `X-Inertia` header), the page object is returned as the JSON payload.
239247

240248
### Basic Page Object
@@ -384,6 +392,78 @@ When using [Infinite scroll](/v2/data-props/infinite-scroll), the page object in
384392
}
385393
```
386394

395+
### Page Object with Once Props
396+
397+
When using [once props](/v2/data-props/once-props), the page object includes an `onceProps` configuration. Each entry maps a key to the prop name and an optional expiration timestamp.
398+
399+
```json
400+
{
401+
"component": "Billing/Plans",
402+
"props": {
403+
"errors": {},
404+
"plans": [
405+
{
406+
"id": 1,
407+
"name": "Basic"
408+
},
409+
{
410+
"id": 2,
411+
"name": "Pro"
412+
}
413+
]
414+
},
415+
"url": "/billing/plans",
416+
"version": "6b16b94d7c51cbe5b1fa42aac98241d5",
417+
"clearHistory": false,
418+
"encryptHistory": false,
419+
"onceProps": {
420+
"plans": {
421+
"prop": "plans",
422+
"expiresAt": null
423+
}
424+
}
425+
}
426+
```
427+
428+
When navigating to a subsequent page that includes the same once prop, the client sends the loaded keys in the `X-Inertia-Except-Once-Props` header. The server skips resolving these props and excludes them from the response. The client reuses the previously loaded values.
429+
430+
```http
431+
REQUEST
432+
GET: https://example.com/billing/upgrade
433+
Accept: text/html, application/xhtml+xml
434+
X-Requested-With: XMLHttpRequest
435+
X-Inertia: true
436+
X-Inertia-Version: 6b16b94d7c51cbe5b1fa42aac98241d5
437+
X-Inertia-Except-Once-Props: plans
438+
439+
RESPONSE
440+
HTTP/1.1 200 OK
441+
Content-Type: application/json
442+
443+
{
444+
"component": "Billing/Upgrade",
445+
"props": {
446+
"errors": {},
447+
"currentPlan": {
448+
"id": 1,
449+
"name": "Basic"
450+
}
451+
},
452+
"url": "/billing/upgrade",
453+
"version": "6b16b94d7c51cbe5b1fa42aac98241d5",
454+
"clearHistory": false,
455+
"encryptHistory": false,
456+
"onceProps": {
457+
"plans": {
458+
"prop": "plans",
459+
"expiresAt": null
460+
}
461+
}
462+
}
463+
```
464+
465+
Note that `plans` is included in `onceProps` but not in `props` since it was already loaded on the client. The `onceProps` key identifies the once prop across pages, while `prop` specifies the actual prop name. These may differ when using [custom keys](/v2/data-props/once-props#custom-keys).
466+
387467
## Asset Versioning
388468

389469
One common challenge with single-page apps is refreshing site assets when they've been changed. Inertia makes this easy by optionally tracking the current version of the site's assets. In the event that an asset changes, Inertia will automatically make a full-page visit instead of an XHR visit.

v2/data-props/deferred-props.mdx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ export default () => (
109109

110110
</CodeGroup>
111111

112+
## Multiple Deferred Props
113+
112114
If you need to wait for multiple deferred props to become available, you can specify an array to the `data` prop.
113115

114116
<CodeGroup>
@@ -173,3 +175,15 @@ export default () => (
173175
```
174176

175177
</CodeGroup>
178+
179+
## Combining with Once Props
180+
181+
You may chain the `once()` modifier onto a deferred prop to ensure the data is resolved only once and remembered by the client across subsequent navigations.
182+
183+
```php
184+
return Inertia::render('Dashboard', [
185+
'stats' => Inertia::defer(fn () => Stats::generate())->once(),
186+
]);
187+
```
188+
189+
For more information on once props, see the [once props](/v2/data-props/once-props) documentation.

v2/data-props/merging-props.mdx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ You can also merge props directly on the client side without making a server req
109109

110110
## Combining with Deferred Props
111111

112-
You can also combine [deferred props](/v2/data-props/deferred-props) with mergeable props to defer the loading of the prop and ultimately mark it as mergeable once it's loaded.
112+
You may combine [deferred props](/v2/data-props/deferred-props) with mergeable props to defer the loading of the prop and ultimately mark it as mergeable once it's loaded.
113113

114114
```php
115115
Route::get('/users', function () {
@@ -122,6 +122,18 @@ Route::get('/users', function () {
122122
});
123123
```
124124

125+
## Combining with Once Props
126+
127+
You may chain the `once()` modifier onto a merge prop to ensure the data is resolved only once and remembered by the client across subsequent navigations.
128+
129+
```php
130+
return Inertia::render('Users/Index', [
131+
'activity' => Inertia::merge(fn () => $user->recentActivity())->once(),
132+
]);
133+
```
134+
135+
For more information on once props, see the [once props](/v2/data-props/once-props) documentation.
136+
125137
## Resetting Props
126138

127139
On the client side, you can indicate to the server that you would like to reset the prop. This is useful when you want to clear the prop value before merging new data, such as when the user enters a new search query on a paginated list.

v2/data-props/once-props.mdx

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
---
2+
title: Once Props
3+
---
4+
5+
Some data rarely changes, is expensive to compute, or is simply large. Rather than including this data in every response, you may use _once props_. These props are remembered by the client and reused on subsequent pages that include the same prop. This makes them ideal for [shared data](/v2/data-props/shared-data).
6+
7+
## Creating Once Props
8+
9+
To create a once prop, use the `Inertia::once()` method when returning your response. This method receives a callback that returns the prop data.
10+
11+
```php
12+
return Inertia::render('Billing', [
13+
'plans' => Inertia::once(fn () => Plan::all()),
14+
]);
15+
```
16+
17+
After the client has received this prop, subsequent requests will skip resolving the callback and exclude the prop from the response. The client only remembers once props while navigating between pages that include them.
18+
19+
Navigating to a page without the once prop will forget the remembered value, and it will be resolved again on the next page that has it. In practice, this is rarely an issue since once props are typically used as shared data or within a specific section of your application.
20+
21+
## Forcing a Refresh
22+
23+
You may force a once prop to be refreshed using the `fresh()` method.
24+
25+
```php
26+
return Inertia::render('Billing', [
27+
'plans' => Inertia::once(fn () => Plan::all())->fresh(),
28+
]);
29+
```
30+
31+
This method also accepts a boolean, allowing you to conditionally refresh the prop.
32+
33+
```php
34+
return Inertia::render('Billing', [
35+
'plans' => Inertia::once(fn () => Plan::all())->fresh($condition),
36+
]);
37+
```
38+
39+
## Refreshing from the Client
40+
41+
You may refresh a once prop from the client-side using a [partial reload](/v2/data-props/partial-reloads). The server will always resolve a once prop when explicitly requested.
42+
43+
<CodeGroup>
44+
45+
```js Vue icon="vuejs"
46+
import { router } from '@inertiajs/vue3'
47+
48+
router.reload({ only: ['plans'] })
49+
```
50+
51+
```js React icon="react"
52+
import { router } from '@inertiajs/react'
53+
54+
router.reload({ only: ['plans'] })
55+
```
56+
57+
```js Svelte icon="s"
58+
import { router } from '@inertiajs/svelte'
59+
60+
router.reload({ only: ['plans'] })
61+
```
62+
63+
</CodeGroup>
64+
65+
## Expiration
66+
67+
You may set an expiration time using the `until()` method. This method accepts a `DateTimeInterface`, `DateInterval`, or an integer (seconds). The prop will be refreshed on a subsequent visit after the expiration time has passed.
68+
69+
```php
70+
return Inertia::render('Dashboard', [
71+
'rates' => Inertia::once(fn () => ExchangeRate::all())->until(now()->addDay()),
72+
]);
73+
```
74+
75+
## Custom Keys
76+
77+
You may assign a custom key to the prop using the `as()` method. This is useful when you want to share data across multiple pages while using different prop names.
78+
79+
```php
80+
// Team member list...
81+
return Inertia::render('Team/Index', [
82+
'memberRoles' => Inertia::once(fn () => Role::all())->as('roles'),
83+
]);
84+
85+
// Invite form...
86+
return Inertia::render('Team/Invite', [
87+
'availableRoles' => Inertia::once(fn () => Role::all())->as('roles'),
88+
]);
89+
```
90+
91+
Both pages share the same underlying data because they use the same custom key, so the prop is only resolved for whichever page you visit first.
92+
93+
## Sharing Once Props
94+
95+
You may share once props globally using the `Inertia::share()` method.
96+
97+
```php
98+
Inertia::share('countries', Inertia::once(fn () => Country::all()));
99+
```
100+
101+
Or, for convenience, you may use the `shareOnce()` method.
102+
103+
```php
104+
Inertia::shareOnce('countries', fn () => Country::all());
105+
```
106+
107+
You may also chain `as()`, `fresh()`, and `until()` onto the `shareOnce` method.
108+
109+
```php
110+
Inertia::shareOnce('countries', fn () => Country::all())->until(now()->addDay());
111+
```
112+
113+
Additionally, you may define a dedicated `shareOnce()` method in your middleware. The middleware will evaluate both `share()` and `shareOnce()`, merging the results.
114+
115+
```php
116+
class HandleInertiaRequests extends Middleware
117+
{
118+
public function shareOnce(Request $request): array
119+
{
120+
return array_merge(parent::shareOnce($request), [
121+
'countries' => fn () => Country::all(),
122+
]);
123+
}
124+
}
125+
```
126+
127+
## Prefetching
128+
129+
Once props are compatible with [prefetching](/v2/data-props/prefetching). The client automatically includes any remembered once props in prefetched responses, so navigating to a prefetched page will already have the once props available.
130+
131+
Prefetched pages containing an expired once prop will be invalidated from the cache.
132+
133+
## Combining with Other Prop Types
134+
135+
The `once()` modifier may be chained onto [deferred](/v2/data-props/deferred-props), [merge](/v2/data-props/merging-props), and [optional](/v2/data-props/partial-reloads#lazy-data-evaluation) props.
136+
137+
```php
138+
return Inertia::render('Dashboard', [
139+
'permissions' => Inertia::defer(fn () => Permission::all())->once(),
140+
'activity' => Inertia::merge(fn () => $user->recentActivity())->once(),
141+
'categories' => Inertia::optional(fn () => Category::all())->once(),
142+
]);
143+
```

v2/data-props/partial-reloads.mdx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,3 +163,15 @@ Here's a summary of each approach:
163163
| <span style={{whiteSpace: 'nowrap'}}>`fn () => User::all()`</span> | Always | Optionally | Only when needed | |
164164
| <span style={{whiteSpace: 'nowrap'}}>`Inertia::optional(fn () => User::all())`</span> | Never | Optionally | Only when needed | |
165165
| <span style={{whiteSpace: 'nowrap'}}>`Inertia::always(fn () => User::all())`</span> | Always | Always | Always | |
166+
167+
## Combining with Once Props
168+
169+
You may chain the `once()` modifier onto an optional prop to ensure the data is resolved only once and remembered by the client across subsequent navigations.
170+
171+
```php
172+
return Inertia::render('Users/Index', [
173+
'users' => Inertia::optional(fn () => User::all())->once(),
174+
]);
175+
```
176+
177+
For more information on once props, see the [once props](/v2/data-props/once-props) documentation.

v2/data-props/shared-data.mdx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,42 @@ Shared data should be used sparingly as all shared data is included with every r
4747

4848
Page props and shared data are merged together, so be sure to namespace your shared data appropriately to avoid collisions.
4949

50+
## Sharing Once Props
51+
52+
You may share data that is resolved only once and remembered by the client across subsequent navigations using [once props](/v2/data-props/once-props).
53+
54+
```php
55+
class HandleInertiaRequests extends Middleware
56+
{
57+
public function share(Request $request)
58+
{
59+
return array_merge(parent::share($request), [
60+
'countries' => Inertia::once(fn () => Country::all()),
61+
]);
62+
}
63+
}
64+
```
65+
66+
Alternatively, you may define a dedicated `shareOnce()` method in the middleware. The middleware will evaluate both `share()` and `shareOnce()`, merging the results.
67+
68+
```php
69+
class HandleInertiaRequests extends Middleware
70+
{
71+
public function shareOnce(Request $request): array
72+
{
73+
return array_merge(parent::shareOnce($request), [
74+
'countries' => fn () => Country::all(),
75+
]);
76+
}
77+
}
78+
```
79+
80+
You may also share once props manually using the `Inertia::shareOnce()`.
81+
82+
```php
83+
Inertia::shareOnce('countries', fn () => Country::all());
84+
```
85+
5086
## Accessing Shared Data
5187

5288
Once you have shared the data server-side, you will be able to access it within any of your pages or components. Here's an example of how to access shared data in a layout component.

v2/installation/client-side-setup.mdx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,3 +247,20 @@ createInertiaApp({
247247
```
248248

249249
If you change the `id` of the root element, be sure to update it [server-side](/v2/installation/server-side-setup#root-template) as well.
250+
251+
## Script Element for Page Data
252+
253+
By default, Inertia stores the initial page data in a `data-page` attribute on the root element. You may configure Inertia to use a `<script type="application/json">` element instead, which is slightly faster and easier to inspect in your browser's dev tools.
254+
255+
```js
256+
createInertiaApp({
257+
// ...
258+
defaults: {
259+
future: {
260+
useScriptElementForInitialPage: true,
261+
},
262+
},
263+
})
264+
```
265+
266+
Be sure to also update your [SSR entry point](/v2/advanced/server-side-rendering#add-server-entry-point) if you're using server-side rendering. Laravel users should also set the `inertia.use_script_element_for_initial_page` config value to `true` so the `@inertia` Blade directive outputs a script element.

0 commit comments

Comments
 (0)