-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
1,385 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
# Deferred props | ||
|
||
Inertia's deferred props feature allows you to defer the loading of certain page data until after the initial page render. This can be useful for improving the perceived performance of your app by allowing the initial page render to happen as quickly as possible. | ||
|
||
## Server side | ||
|
||
To defer a prop, you can use the defer method when returning your response. This method receives a callback that returns the prop data. The callback will be executed in a separate request after the initial page render. | ||
|
||
```ruby | ||
class UsersController < ApplicationController | ||
def index | ||
render inertia: 'Users/Index', props: { | ||
users: -> { User.all }, | ||
roles: -> { Role.all }, | ||
permissions: InertiaRails.defer { Permission.all }, | ||
|
||
# Also works with a lambda: | ||
# permissions: InertiaRails.defer(-> { Permission.all }), | ||
|
||
# Also works with a simple value, | ||
# but this way the prop is always evaluated, | ||
# even if not included: | ||
# permissions: InertiaRails.defer(Permission.all), | ||
} | ||
end | ||
end | ||
``` | ||
|
||
### Grouping requests | ||
|
||
By default, all deferred props get fetched in one request after the initial page is rendered, but you can choose to fetch data in parallel by grouping props together. | ||
|
||
```ruby | ||
class UsersController < ApplicationController | ||
def index | ||
render inertia: 'Users/Index', props: { | ||
users: -> { User.all }, | ||
roles: -> { Role.all }, | ||
permissions: InertiaRails.defer { Permission.all }, | ||
# using block: | ||
teams: InertiaRails.defer(group: 'attributes') { Team.all }, | ||
# using lambda: | ||
projects: InertiaRails.defer(-> { Project.all }, group: 'attributes'), | ||
tasks: InertiaRails.defer(-> { Task.all }, group: 'attributes'), | ||
} | ||
end | ||
end | ||
``` | ||
|
||
In the example above, the `teams`, `projects`, and `tasks` props will be fetched in one request, while the `permissions` prop will be fetched in a separate request in parallel. Group names are arbitrary strings and can be anything you choose. | ||
|
||
## Client side | ||
|
||
On the client side, Inertia provides the `Deferred` component to help you manage deferred props. This component will automatically wait for the specified deferred props to be available before rendering its children. | ||
|
||
:::tabs key:frameworks | ||
== Vue | ||
|
||
```vue | ||
<script setup> | ||
import { Deferred } from '@inertiajs/vue3' | ||
</script> | ||
<template> | ||
<Deferred data="permissions"> | ||
<template #fallback> | ||
<div>Loading...</div> | ||
</template> | ||
<div v-for="permission in permissions"> | ||
<!-- ... --> | ||
</div> | ||
</Deferred> | ||
</template> | ||
``` | ||
|
||
== React | ||
|
||
```jsx | ||
import { Deferred } from '@inertiajs/react' | ||
|
||
export default () => ( | ||
<Deferred data="permissions" fallback={<div>Loading...</div>}> | ||
<PermissionsChildComponent /> | ||
</Deferred> | ||
) | ||
``` | ||
|
||
== Svelte 4 | ||
|
||
```svelte | ||
<script> | ||
import { Deferred } from '@inertiajs/svelte' | ||
export let permissions | ||
</script> | ||
<Deferred data="permissions"> | ||
<svelte:fragment slot="fallback"> | ||
<div>Loading...</div> | ||
</svelte:fragment> | ||
{#each permissions as permission} | ||
<!-- ... --> | ||
{/each} | ||
</Deferred> | ||
``` | ||
|
||
== Svelte 5 | ||
|
||
```svelte | ||
<script> | ||
import { Deferred } from '@inertiajs/svelte' | ||
let { permissions } = $props() | ||
</script> | ||
<Deferred data="permissions"> | ||
{#snippet fallback()} | ||
<div>Loading...</div> | ||
{/snippet} | ||
{#each permissions as permission} | ||
<!-- ... --> | ||
{/each} | ||
</Deferred> | ||
``` | ||
|
||
::: | ||
|
||
If you need to wait for multiple deferred props to become available, you can specify an array to the `data` prop. | ||
|
||
:::tabs key:frameworks | ||
== Vue | ||
|
||
```vue | ||
<script setup> | ||
import { Deferred } from '@inertiajs/vue3' | ||
</script> | ||
<template> | ||
<Deferred :data="['teams', 'users']"> | ||
<template #fallback> | ||
<div>Loading...</div> | ||
</template> | ||
<!-- Props are now loaded --> | ||
</Deferred> | ||
</template> | ||
``` | ||
|
||
== React | ||
|
||
```jsx | ||
import { Deferred } from '@inertiajs/react' | ||
|
||
export default () => ( | ||
<Deferred data={['teams', 'users']} fallback={<div>Loading...</div>}> | ||
<ChildComponent /> | ||
</Deferred> | ||
) | ||
``` | ||
|
||
== Svelte 4 | ||
|
||
```svelte | ||
<script> | ||
import { Deferred } from '@inertiajs/svelte' | ||
export let teams | ||
export let users | ||
</script> | ||
<Deferred data={['teams', 'users']}> | ||
<svelte:fragment slot="fallback"> | ||
<div>Loading...</div> | ||
</svelte:fragment> | ||
<!-- Props are now loaded --> | ||
</Deferred> | ||
``` | ||
|
||
== Svelte 5 | ||
|
||
```svelte | ||
<script> | ||
import { Deferred } from '@inertiajs/svelte' | ||
let { teams, users } = $props() | ||
</script> | ||
<Deferred data={['teams', 'users']}> | ||
{#snippet fallback()} | ||
<div>Loading...</div> | ||
{/snippet} | ||
<!-- Props are now loaded --> | ||
</Deferred> | ||
``` | ||
|
||
::: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
# History encryption | ||
|
||
Imagine a scenario where your user is authenticated, browses privileged information on your site, then logs out. If they press the back button, they can still see the privileged information that is stored in the window's history state. This is a security risk. To prevent this, Inertia.js provides a history encryption feature. | ||
|
||
## How it works | ||
|
||
When you instruct Inertia to encrypt your app's history, it uses the browser's built-in [`crypto` api](https://developer.mozilla.org/en-US/docs/Web/API/Crypto) to encrypt the current page's data before pushing it to the history state. We store the corresponding key in the browser's session storage. When the user navigates back to a page, we decrypt the data using the key stored in the session storage. | ||
|
||
Once you instruct Inertia to clear your history state, we simply clear the existing key from session storage roll a new one. If we attempt to decrypt the history state with the new key, it will fail an Inertia will make a fresh request back to your server for the page data. | ||
|
||
> [!NOTE] | ||
> History encryption relies on `window.crypto.subtle` which is only available in secure environments (sites with SSL enabled). | ||
## Opting in | ||
|
||
History encryption is an opt-in feature. There are several methods for enabling it: | ||
|
||
### Global encryption | ||
|
||
If you'd like to enable history encryption globally, set the `encrypt_history` config value to `true`. | ||
|
||
You are able to opt out of encryption on specific pages by passing `false` to the `encrypt_history` option: | ||
|
||
```ruby | ||
render inertia: 'Homepage', props: {}, encrypt_history: false | ||
``` | ||
|
||
### Per-request encryption | ||
|
||
To encrypt the history of an individual request, simply pass `true` to the `encrypt_history` option: | ||
|
||
```ruby | ||
render inertia: 'Dashboard', props: {}, encrypt_history: true | ||
``` | ||
|
||
### Controller-level encryption | ||
|
||
You can also enable history encryption for all actions in a controller by setting the `encrypt_history` config value in the controller: | ||
|
||
```ruby | ||
class DashboardController < ApplicationController | ||
inertia_config(encrypt_history: true) | ||
|
||
# ... | ||
end | ||
``` | ||
|
||
## Clearing history | ||
|
||
To clear the history state, you can pass the `clear_history` option to the `render` method: | ||
|
||
```ruby | ||
render inertia: 'Dashboard', props: {}, clear_history: true | ||
``` | ||
|
||
Once the response has rendered on the client, the encryption key will be rotated, rendering the previous history state unreadable. | ||
|
||
You can also clear history on the client site by calling `router.clearHistory()`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.