The new deferred props feature of Inertia v2 eliminates the need to write our own boilerplate code to delay loading data. Let's see how it works.
One way to speed up your applications is to defer data loading until the page has been rendered. When used wisely, this results in faster load times without affecting user experience.
In Inertia v1, we could mark props as lazy
, meaning they're not evaluated until requested by Inertia's router. Here's what a response from a controller would look like:
return inertia()->render('Dashboard', [
'users' => inertia()->lazy(function () {
return User::get();
}),
]);
It's worth noting that the
lazy
method has been deprecated in Inertia v2, and you should now useoptional
instead.
If you were to access the users
prop in your page, the value would be empty:
<script setup>
defineProps({
users: Array
})
</script>
<template>
<div>
<!-- Nothing here -->
{{ users }}
</div>
</template>
So for Inertia v1, we'd have to manually request this data with the router
like this:
<script setup>
import { router } from '@inertiajs/vue3'
import { onMounted } from 'vue'
defineProps({
users: Array
})
onMounted(() => {
router.reload({ only: ['users'] })
})
</script>
<template>
<div>
{{ users }}
</div>
</template>
This would, once the page has loaded, reload the page with only the props you need.
Sure, this looks pretty simple — but if we wanted to take it a step further and show a loading indicator or make several separate requests to fetch props, we're talking about much more boilerplate code.
So, in Inertia v2, we now have the concept of deferred props, which nicely wraps up everything we'd have to build manually.
Let's take a look.
We don't use lazy
(or even the new optional
method) anymore. Instead, we use defer
when returning our response. There's a good reason for this, which we'll talk about next.
Here's what it looks like:
return inertia()->render('Dashboard', [
'users' => inertia()->defer(function () {
return User::get();
}),
]);
Pretty much exactly the same thing, but now, Inertia is aware of this client-side — which brings us to the new Deferred
component:
<script setup>
import { Deferred } from '@inertiajs/vue3'
defineProps({
users: Array
})
</script>
<template>
<Deferred :data="['users']">
<template #fallback>
<div>
Loading...
</div>
</template>
<div>
{{ users }}
</div>
</Deferred>
</template>
Our original boilerplate code has gone (onMounted
and the call to router.reload
) and has been replaced with a simple component which takes in the data
(props) you want to load, and a fallback template that will be displayed while the data is loading.
This means we don't have to mess around and create an isLoading
ref with Vue to determine whether the request is loading!
For clarity, here's what we'd have to do before the Deferred
component to get the exact same result:
<script setup>
import { router } from '@inertiajs/vue3'
import { onMounted, ref } from 'vue'
defineProps({
users: Array
})
const isLoading = ref(false)
onMounted(() => {
router.reload({
only: ['users'],
onStart: () => isLoading.value = true,
onFinish: () => isLoading.value = false,
})
})
</script>
<template>
<div>
<div v-if="isLoading">
Loading...
</div>
<div v-else>
{{ users }}
</div>
</div>
</template>
Clearly, using the Deferred
component is better here. And that's without factoring in what happens if the request fails or if we have multiple props that we need to load asynchronously.
Deferring props is more powerful than it seems because we can group props together, letting Inertia know which props should be loaded in the same request.
Here's an example of an Inertia response with two different groups:
return inertia()->render('Dashboard', [
'users' => inertia()->defer(fn () => User::get(), 'one'),
'posts' => inertia()->defer(fn () => Post::get(), 'two'),
'articles' => inertia()->defer(fn () => Article::get(), 'two'),
]);
I've named these one
and two
for clarity in this article, but you can give your deferred props any string name — whatever makes sense for your use case.
And here's how we're able to load them:
<script setup>
import { Deferred } from '@inertiajs/vue3'
defineProps({
users: Array,
posts: Array,
articles: Array,
})
</script>
<template>
<Deferred :data="['users']">
<template #fallback>
<div>
Loading...
</div>
</template>
<div>
{{ users }}
</div>
</Deferred>
<Deferred :data="['posts', 'articles']">
<template #fallback>
<div>
Loading...
</div>
</template>
<div>
{{ posts }}
{{ articles }}
</div>
</Deferred>
</template>
Inertia will now make two asynchronous requests to load users
in one request and posts
and articles
in another request. If we hadn't grouped them, they'd all be loaded in one request.
Our Deferred
component above will show the data once these props become available.
Although these are simple examples, that's it for deferred props in Inertia. You can tweak your strategy to load and display the data you need after the page has been rendered.
If you'd like to learn more about what's new in Inertia, we have a course covering all the latest features of Inertia here!