Inertia 2.0 announced! 🎉 Get 20% off ANY package with the coupon INERTIA20

Inertia 2.0: What's new?

Taylor Otwell just announced Inertia 2.0 – the biggest update packed with exciting features since the package launch.

This post highlights the features introduced with Inertia 2.0. The update is set to be released in early October.

Learn advanced concepts and make apps with Laravel and Inertia.js a breeze to build and maintain with Advanced Inertia. Use the coupon INERTIA20 to get 20% off any package.

Async requests

One of the most significant improvements in Inertia v2 is the concept of async requests:

  • Non-blocking requests
  • Do not show progress indicator
  • All reload requests are now async by default

Inertia was designed with the classic HTTP page visits in mind. This means that only a single request can be performed at once. Essentially, you cannot perform two partial reload requests at once.

While this approach makes sense for simpler applications, some complex solutions may require more flexible patterns, such as optimistic UI updates, when an action's outcome should be visibly reflected even before the request is finished.

<template>
<Link
method="put"
:href="`/settings/notifications/${channel}`"
:data="{ enabled: !enabled }"
async
>
<!-- ... -->
</Link>
</template>

Async requests power every other feature coming with the update.

Inertia Polling

Inertia v2.0 introduces the easiest way to add polling to your Inertia applications.

Previously, you could implement data polling three different ways:

  • Inertia.reload method
  • XHR-requests to a dedicated API endpoint
  • WebSockets

None these options aren perfect, as each adds some extra overhead in data management.

The new usePoll helper is used to fetch data from the current page, just like partial reload requests, designed specifically for non-blocking data polling. The method can be run automatically, or manually for a more fine-grained control.

// Automatic polling:
usePoll(3000, { only: ['leaderboard'] })
// Manual polling:
const { stop, start } = usePoll(
3000,
{ only: ['leaderboard'] },
{ autoStart: false }
)

Once you leave the page, polling will automatically stop, so there's no need to manually manage intervals.

Some common use-case examples for the feature:

  • Refreshing live statistics
  • Updating stock prices in a financial dashboard
  • Checking for new messages in your revolutionary GPT wrapper

For example, in a chat application, you can use polling to check for new messages.

<script setup>
const { messages } = defineProps(['messages'])
 
usePoll(5000, { only: ['messages'] })
</script>
 
<template>
<div v-for="message in messages" :key="message.id">
{{ message.content }}
</div>
</template>

WhenVisible

A convenient wrapper around the Intersection Observer API that allows you to easily trigger prop loading when an element becomes visible on the page.

Another addition to the update is the WhenVisible component, which allows you to load props when an element becomes visible on the page.

The component provides an elegant way to optimize your application's performance by deferring the loading of content until it's needed. This can lead to faster initial page loads and a smoother user experience, especially for content-heavy pages.

On the backend, use the Inertia::optional() method to instruct Inertia that the data should not be evaluated on the initial page visit.

inertia('dashboard', [
'users' => Inertia::optional(
fn () => User::all()
),
]);

On the frontend, use the new WhenVisible component. The load request will only be performed when the component enters the viewport.

<template>
<WhenVisible data="users">
<template #fallback>
<div>Loading users...</div>
</template>
<div v-for="user in users" :key="user.id">
{{ user.name }}
</div>
</WhenVisible>
 
<!-- Multiple properties -->
<WhenVisible :data="['users', 'teams']">
<!-- -->
</WhenVisible>
 
<!-- Custom reload params -->
<WhenVisible
:params="{
only: ['users'],
onFinish() {
// ...
}
}">
<!-- -->
</WhenVisible>
</template>

To provide a more user-friendly experience, you can make the component start loading data in advance, once the user reaches a certain distance threshold to the element.

<template>
<!-- Start making the request when we're 200px away -->
<WhenVisible data="users" :buffer="200">
<!-- -->
</WhenVisible>
</template>

Infinite scrolling

New primitives that allow you to build infinite scrolling experiences with Inertia.

Built upon the WhenVisible component, Inertia v2.0 now provides a straightforward way to implement infinite scrolling.

The infinite scrolling implementation offers a smooth, seamless user experience for browsing large datasets without the need for manual pagination controls. It's an excellent way to improve engagement and reduce perceived load times in content-heavy applications.

This feature is particularly useful for:

  • Long lists of items (e.g., social media feeds)
  • Product catalogs
  • Search results pages

On the backend, instruct Inertia to merge the data results using the Inertia::merge() method.

inertia('feed', [
'items' => Inertia::merge(function () {
// Returns items that needs the be merged with the existing items
return Post::paginate();
}),
]);

On the client side, use the WhenVisible component with an extra page parameter. You can also preserve the current URL, which is a pretty common practice in such cases.

<template>
<WhenVisible
:once="false"
:params="{
data: {
page: page + 1,
},
only: ['items', 'page'],
preserveUrl: true,
}"
>
<Spinner />
</WhenVisible>
</template>

In this example, when the WhenVisible component becomes visible (i.e., when the user scrolls to the bottom of the page), it triggers a request for the next page of articles.

The :once="false" prop ensures that this process repeats each time the component becomes visible.

The preserveUrl: false option prevents the URL from changing as new items are loaded, maintaining a clean browsing experience.

Prefetching

Make your apps feel faster by prefetching pages in the background and instantly loading them at the right time.

The new release features a powerful prefetching mechanism, allowing you to load pages before the user navigates to them.

This could significantly improve perceived performance by:

  • Reducing wait times on navigation
  • Improving the overall responsiveness of your application
  • Supporting preloading of likely destinations

Prefetching works with the built-in Link component, and can be done immediately on page load, or on link hover.

<template>
<!-- Start fetching the page on hover -->
<Link href="/" prefetch>Home</Link>
 
<!-- Start fetching the page on mount. Falls back to hover once the cache expires -->
<Link href="/" prefetch="mount">Home</Link>
</template>

You can manually control the cache TTL. Even if expired, a page will still be loaded immediately on click but will be updated once opened

<!-- Specify a custom cache duration -->
<Link href="/photos" prefetch cacheFor="10s">Photos</Link>
 
<!-- Fetch on mount then hover when it's stale using stale-while-revalidate -->
<Link href="/" prefetch="mount" :cacheFor="['5s', '10s']">Home</Link>

Deferred props

Props that are not evaluated on initial page load but are requested immediately after a page is loaded, automatically.

Inertia v2.0 introduces the concept of deferred props, allowing you to load certain data in the background after the initial page render. This means that you can load the critical parts of data immediately, and then handle heavier datasets asynchronously.

The approach allows you to:

  • Improve initial page load times
  • Display important information quickly
  • Handle complex data fetching without blocking the initial render

To make a prop "deferred", use the Inertia::defer(...) method. Such props will not be evaluated on initial page visits.

inertia('dashboard', [
'user_count' => Inertia::defer(fn () => User::count()),
'order_count' => Inertia::defer(fn () => Order::count()),
'average_purchases' => Inertia::defer(fn () => Order::average('total')),
'users' => Inertia::defer(fn () => User::all()),
]);

On the frontend, you can use the new built-in Deferred component to specify your loading state and what should render when the data is ready.

<template>
<!-- Wait for one property -->
<Deferred data="user_count">
<template #fallback>
<div>Loading...</div>
</template>
 
<div>Total users: {{ user_count }}</div>
</Deferred>
 
<!-- Wait for multiple properties -->
<Deferred :data="['user_count', 'order_count']">
<template #fallback>
<div>Loading...</div>
</template>
 
<!-- -->
</Deferred>
</template>

Inertia also lets you group deferred props. This enables you to load certain deferred groups independently from others, so that quicker queries will not have to wait until the slower ones are completed.

inertia('dashboard', [
'user_count' => Inertia::defer(fn () => User::count(), 'stats'),
'order_count' => Inertia::defer(fn () => Order::count(), 'stats'),
'average_purchases' => Inertia::defer(fn () => Order::average('total'), 'stats'),
'users' => Inertia::defer(fn () => User::all()),
]);

Use Inertia.js like a boss

Learn advanced concepts and make apps with Laravel and Inertia.js a breeze to build and maintain.

© 2023 Boris Lepikhin. All rights reserved.