Deferred props in Inertia

November 18th, 2024 • 4 minutes read time

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 use optional 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!

If you found this article helpful, you'll love our practical screencasts.
Author
Alex Garrett-Smith
Share :

Comments

No comments, yet. Be the first!