Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upstream docs for v2.0 #148

Merged
merged 2 commits into from
Nov 13, 2024
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
33 changes: 25 additions & 8 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export default defineConfig({
items: [
{ text: 'Introduction', link: '/guide' },
{ text: 'Demo app', link: '/guide/demo-application' },
{ text: 'Upgrade guide', link: '/guide/upgrade-guide' },
],
},
{
Expand Down Expand Up @@ -101,28 +102,44 @@ export default defineConfig({
{ text: 'Forms', link: '/guide/forms' },
{ text: 'File uploads', link: '/guide/file-uploads' },
{ text: 'Validation', link: '/guide/validation' },
{ text: 'Shared data', link: '/guide/shared-data' },
],
},
{
text: 'Advanced',
text: 'Data & Props',
items: [
{ text: 'Events', link: '/guide/events' },
{ text: 'Testing', link: '/guide/testing' },
{ text: 'Shared data', link: '/guide/shared-data' },
{ text: 'Partial reloads', link: '/guide/partial-reloads' },
{ text: 'Scroll management', link: '/guide/scroll-management' },
{ text: 'Deferred props', link: '/guide/deferred-props' },
{ text: 'Polling', link: '/guide/polling' },
{ text: 'Prefetching', link: '/guide/prefetching' },
{ text: 'Load when visible', link: '/guide/load-when-visible' },
{ text: 'Merging props', link: '/guide/merging-props' },
{ text: 'Remembering state', link: '/guide/remembering-state' },
],
},
{
text: 'Security',
items: [
{ text: 'Authentication', link: '/guide/authentication' },
{ text: 'Authorization', link: '/guide/authorization' },
{ text: 'CSRF protection', link: '/guide/csrf-protection' },
{ text: 'Error handling', link: '/guide/error-handling' },
{ text: 'History encryption', link: '/guide/history-encryption' },
],
},
{
text: 'Advanced',
items: [
{ text: 'Asset versioning', link: '/guide/asset-versioning' },
{ text: 'Progress indicators', link: '/guide/progress-indicators' },
{ text: 'Remembering state', link: '/guide/remembering-state' },
{ text: 'Code splitting', link: '/guide/code-splitting' },
{ text: 'Error handling', link: '/guide/error-handling' },
{ text: 'Events', link: '/guide/events' },
{ text: 'Progress indicators', link: '/guide/progress-indicators' },
{ text: 'Scroll management', link: '/guide/scroll-management' },
{
text: 'Server-side rendering',
link: '/guide/server-side-rendering',
},
{ text: 'Testing', link: '/guide/testing' },
],
},
],
Expand Down
185 changes: 185 additions & 0 deletions docs/guide/deferred-props.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# 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 },
}
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 },
teams: InertiaRails.defer(group: 'attributes') { Team.all },
projects: InertiaRails.defer(group: 'attributes') { Project.all },
tasks: InertiaRails.defer(group: 'attributes') { Task.all },
}
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>
```

:::
58 changes: 58 additions & 0 deletions docs/guide/history-encryption.md
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()`.
4 changes: 4 additions & 0 deletions docs/guide/links.md
Original file line number Diff line number Diff line change
Expand Up @@ -522,3 +522,7 @@ export default () => {
You can perform exact match comparisons (`===`), `startsWith()` comparisons (useful for matching a subset of pages), or even more complex comparisons using regular expressions.

Using this approach, you're not limited to just setting class names. You can use this technique to conditionally render any markup on active state, such as different link text or even an SVG icon that represents the link is active.

## Data loading attribute

While a link is making an active request, a `data-loading` attribute is added to the link element. This allows you to style the link while it's in a loading state. The attribute is removed once the request is complete.
Loading