Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ export default defineConfig({
{ text: 'Prefetching', link: '/guide/prefetching' },
{ text: 'Load when visible', link: '/guide/load-when-visible' },
{ text: 'Merging props', link: '/guide/merging-props' },
{ text: 'Once props', link: '/guide/once-props' },
{ text: 'Infinite scroll', link: '/guide/infinite-scroll' },
{ text: 'Remembering state', link: '/guide/remembering-state' },
],
Expand Down
18 changes: 12 additions & 6 deletions docs/cookbook/handling-validation-error-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ import type { InertiaFormProps as OriginalProps } from '@inertiajs/vue3'
type FormDataType = Record<string, FormDataConvertible>

declare module '@inertiajs/vue3' {
interface InertiaFormProps<TForm extends FormDataType>
extends Omit<OriginalProps<TForm>, 'errors' | 'setError'> {
interface InertiaFormProps<TForm extends FormDataType> extends Omit<
OriginalProps<TForm>,
'errors' | 'setError'
> {
errors: Partial<Record<FormDataKeys<TForm>, string[]>>

setError(field: FormDataKeys<TForm>, value: string[]): this
Expand Down Expand Up @@ -60,8 +62,10 @@ import type { InertiaFormProps as OriginalProps } from '@inertiajs/react'
type FormDataType = Record<string, FormDataConvertible>

declare module '@inertiajs/react' {
interface InertiaFormProps<TForm extends FormDataType>
extends Omit<OriginalProps<TForm>, 'errors' | 'setError'> {
interface InertiaFormProps<TForm extends FormDataType> extends Omit<
OriginalProps<TForm>,
'errors' | 'setError'
> {
errors: Partial<Record<FormDataKeys<TForm>, string[]>>

setError(field: FormDataKeys<TForm>, value: string[]): void
Expand Down Expand Up @@ -92,8 +96,10 @@ import type { Writable } from 'svelte/store'
type FormDataType = Record<string, FormDataConvertible>

declare module '@inertiajs/svelte' {
interface InertiaFormProps<TForm extends FormDataType>
extends Omit<OriginalProps<TForm>, 'errors' | 'setError'> {
interface InertiaFormProps<TForm extends FormDataType> extends Omit<
OriginalProps<TForm>,
'errors' | 'setError'
> {
errors: Partial<Record<FormDataKeys<TForm>, string[]>>

setError(field: FormDataKeys<TForm>, value: string[]): this
Expand Down
20 changes: 20 additions & 0 deletions docs/guide/deferred-props.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ export default () => (

:::

## Multiple Deferred Props

If you need to wait for multiple deferred props to become available, you can specify an array to the `data` prop.

:::tabs key:frameworks
Expand Down Expand Up @@ -189,3 +191,21 @@ export default () => (
```

:::

## Combining with Once Props

@available_since rails=master core=2.2.20

You may pass the `once: true` argument to a deferred prop to ensure the data is resolved only once and remembered by the client across subsequent navigations.

```ruby
class DashboardController < ApplicationController
def index
render inertia: {
stats: InertiaRails.defer(once: true) { Stats.generate },
}
end
end
```

For more information on once props, see the [once props](/guide/once-props) documentation.
20 changes: 19 additions & 1 deletion docs/guide/merging-props.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ You can also merge props directly on the client side without making a server req

## Combining with deferred props

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

```ruby
class UsersController < ApplicationController
Expand All @@ -146,6 +146,24 @@ class UsersController < ApplicationController
end
```

## Combining with Once Props

@available_since rails=master core=2.2.20

You may pass the `once: true` argument to a deferred prop to ensure the data is resolved only once and remembered by the client across subsequent navigations.

```ruby
class UsersController < ApplicationController
def index
render inertia: {
activity: InertiaRails.merge(once: true) { @user.recent_activity },
}
end
end
```

For more information on once props, see the [once props](/guide/once-props) documentation.

## Resetting props

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.
Expand Down
137 changes: 137 additions & 0 deletions docs/guide/once-props.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Once Props

@available_since rails=master core=2.2.20

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](/guide/shared-data).

## Creating Once Props

To create a once prop, use the `InertiaRails.once` method when returning your response. This method receives a block that returns the prop data.

```ruby
class BillingController < ApplicationController
def index
render inertia: {
plans: InertiaRails.once { Plan.all },
}
end
end
```

After the client has received this prop, subsequent requests will skip resolving the block and exclude the prop from the response. The client only remembers once props while navigating between pages that include them.

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.

## Forcing a Refresh

You may force a once prop to be refreshed using the `fresh` parameter.

```ruby
class BillingController < ApplicationController
def index
render inertia: {
plans: InertiaRails.once(fresh: true) { Plan.all },
}
end
end
```

## Refreshing from the Client

You may refresh a once prop from the client-side using a [partial reload](/guide/partial-reloads). The server will always resolve a once prop when explicitly requested.

:::tabs key:frameworks
== Vue

```js
import { router } from '@inertiajs/vue3'

router.reload({ only: ['plans'] })
```

== React

```js
import { router } from '@inertiajs/react'

router.reload({ only: ['plans'] })
```

== Svelte 4|Svelte 5

```js
import { router } from '@inertiajs/svelte'

router.reload({ only: ['plans'] })
```

:::

## Expiration

You may set an expiration time using the `expires_in` parameter. This parameter accepts an `ActiveSupport::Duration` or an integer (seconds). The prop will be refreshed on a subsequent visit after the expiration time has passed.

```ruby
class DashboardController < ApplicationController
def index
render inertia: {
plans: InertiaRails.once(expires_in: 1.day) { Plan.all },
}
end
end
```

## Custom Keys

You may assign a custom key to the prop using the `key` parameter. This is useful when you want to share data across multiple pages while using different prop names.

```ruby
class TeamsController < ApplicationController
def index
render inertia: {
memberRoles: InertiaRails.once(key: 'roles') { Role.all },
}
end

def invite
render inertia: {
availableRoles: InertiaRails.once(key: 'roles') { Role.all },
}
end
end
```

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.

## Sharing Once Props

You may share once props globally using the `inertia_share` controller method.

```ruby
class ApplicationController < ActionController::Base
inertia_share countries: InertiaRails.once { Country.all }
end
```

## Prefetching

Once props are compatible with [prefetching](/guide/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.

Prefetched pages containing an expired once prop will be invalidated from the cache.

## Combining with Other Prop Types

The `once` option can be passed to [deferred](/guide/deferred-props), [merge](/guide/merging-props), and [optional](/guide/partial-reloads#lazy-data-evaluation) props.

```ruby
class DashboardController < ApplicationController
def index
render inertia: {
memberRoles: InertiaRails.once(key: 'roles') { Role.all },
permissions: InertiaRails.defer(once: true) { Permission.all },
activity: InertiaRails.merge(once: true) { @user.recent_activity },
categories: InertiaRails.optional(once: true) { Category.all },
}
end
end
```
33 changes: 14 additions & 19 deletions docs/guide/partial-reloads.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,29 +249,24 @@ end

Here's a summary of each approach:

| Approach | Standard Visits | Partial Reloads | Evaluated |
| :---------------------------------------------------------------------------- | :-------------- | :-------------- | :--------------- |
| <span style="white-space: nowrap">`User.all`</span> | Always | Optionally | Always |
| <span style="white-space: nowrap">`-> { User.all }`</span> | Always | Optionally | Only when needed |
| <span style="white-space: nowrap">`InertiaRails.optional { User.all }`</span> | Never | Optionally | Only when needed |
| <span style="white-space: nowrap">`InertiaRails.always { User.all }`</span> | Always | Always | Always |

## Combining with Once Props

@available_since rails=master core=2.2.20

You may pass the `once: true` argument to a deferred prop to ensure the data is resolved only once and remembered by the client across subsequent navigations.

```ruby
class UsersController < ApplicationController
def index
render inertia: {
# ALWAYS included on standard visits
# OPTIONALLY included on partial reloads
# ALWAYS evaluated
users: User.all,

# ALWAYS included on standard visits
# OPTIONALLY included on partial reloads
# ONLY evaluated when needed
users: -> { User.all },

# NEVER included on standard visits
# OPTIONALLY included on partial reloads
# ONLY evaluated when needed
users: InertiaRails.optional { User.all },

# ALWAYS included on standard visits
# ALWAYS included on partial reloads
# ALWAYS evaluated
users: InertiaRails.always { User.all },
users: InertiaRails.optional(once: true) { User.all },
}
end
end
Expand Down
14 changes: 14 additions & 0 deletions docs/guide/shared-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,20 @@ end
> [!NOTE]
> Page props and shared data are merged together, so be sure to namespace your shared data appropriately to avoid collisions.
## Sharing Once Props

@available_since rails=master core=2.2.20

You may share data that is resolved only once and remembered by the client across subsequent navigations using [once props](/guide/once-props).

```ruby
class ApplicationController < ActionController::Base
inertia_share countries: InertiaRails.once { Country.all }
end
```

For more information on once props, see the [once props](/guide/once-props) documentation.

## Accessing shared data

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.
Expand Down
Loading