Skip to content

Commit

Permalink
Add v2 docs
Browse files Browse the repository at this point in the history
  • Loading branch information
skryukov committed Nov 1, 2024
1 parent 498001f commit 7fc4e55
Show file tree
Hide file tree
Showing 14 changed files with 1,385 additions and 21 deletions.
34 changes: 26 additions & 8 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export default defineConfig({
['meta', { property: 'og:image', content: image }],
['meta', { property: 'og:description', content: description }],
],

themeConfig: {
// https://vitepress.dev/reference/default-theme-config
nav: [
Expand Down Expand Up @@ -68,6 +69,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 @@ -98,28 +100,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
195 changes: 195 additions & 0 deletions docs/guide/deferred-props.md
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>
```

:::
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

0 comments on commit 7fc4e55

Please sign in to comment.