Pagination with Inertia and Vue

May 22nd, 2024

In this article, you'll learn how to add pagination to your Inertia apps with a simple, custom, re-usable pagination component.

The first step is to send paginated data down to your page component. If you're not using API resources to structure this data, here's what it looks like.

class UserIndexController extends Controller
{
    public function __invoke()
    {
        return inertia()->render('User/Index', [
            'users' => UserResource::collection(
                User::latest()->paginate(10)
            ),
        ]);
    }
}

The UserResource just contains a bunch of information about the user. Since we're calling the paginate method on our query builder, we'll get some nicely structured data (wrapped in a data property) and some meta, which contains our pagination data.

Here's what that data looks like. I've slimmed it down a little so it's easier to grasp.

{
    "data": [],
    "meta": {
        "current_page": 2,
        "from": 11,
        "last_page": 101,
        "links": [
            {
                "url": "http://inertia-pagination.test/users?page=1",
                "label": "Previous",
                "active": false
            },
            {
                "url": "http://inertia-pagination.test/users?page=1",
                "label": "1",
                "active": false
            },
            {
                "url": "http://inertia-pagination.test/users?page=3",
                "label": "Next",
                "active": false
            }
        ],
        "path": "http://inertia-pagination.test/users",
        "per_page": 10,
        "to": 20,
        "total": 1001
    }
}

Well, technically, it's a Vue component... but it will be Inertia-specific since we'll need to rely on Inertia's router to fetch the chosen page.

Here's an example of iterating over the data from our controller.

<script setup>
    defineProps({
        users: Object
    })
</script>

<template>
    <div v-if="users.data.length">
        <div v-for="user in users.data" :key="user.id">
            #{{ user.id }}: {{ user.name }}
        </div>

        <Pagination :pagination="users.meta" />
    </div>
</template>

And here's what that Pagination component looks like.

<script setup>
    import { Link } from '@inertiajs/vue3'

    defineProps({
        pagination: Object
    })
</script>

<template>
    <nav class="relative flex justify-center">
        <template v-for="link in pagination.links" :key="link.label">
            <Link
                preserve-scroll
                :href="link.url ?? ''"
                v-html="link.label"
                class="flex items-center justify-center px-3 py-2 text-sm rounded-lg text-gray-600"
                :class="{ 'bg-gray-200': link.active, '!text-gray-300': !link.url }"
            />
        </template>
    </nav>
</template>

Due to the structure of the meta we get back from Laravel's pagination, it's straightforward to iterate over and show every pagination element needed.

Here's the result of the pagination component in action.

Screenshot 2024-05-22 at 08.41.30.png

If you don't need the complexity of numbered pagination, here's how to implement simple pagination with Inertia.

First, switch your paginate method to simplePaginate.

public function __invoke()
    {
        return inertia()->render('User/Index', [
            'users' => UserResource::collection(
                User::latest()->simplePaginate(10)
            ),
        ]);
    }

This provides the following structure to our Vue components.

{
    "data": [],
    "links": {
        "first": "http://inertia-pagination.test/users?page=1",
        "last": null,
        "prev": null,
        "next": "http://inertia-pagination.test/users?page=2"
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "path": "http://inertia-pagination.test/users",
        "per_page": 10,
        "to": 10
    }
}

Create a new SimplePagination component and pass down the link from our pagination response to this component.

<SimplePagination :pagination="users.links" />

And here's what the SimplePagination component looks like. Since we're just showing two links, there's no need to iterate over anything.

<script setup>
import { Link } from '@inertiajs/vue3'

defineProps({
    pagination: Object,
})
</script>


<template>
    <nav class="relative flex items-center justify-between">
        <Link
            preserve-scroll
            :href="pagination.prev ?? ''"
            class="flex items-center justify-center px-3 py-2 text-sm rounded-lg text-gray-600"
            :class="{ 'bg-gray-200': pagination.prev, '!text-gray-300': !pagination.prev }"
        >
            Previous
        </Link>

        <Link
            preserve-scroll
            :href="pagination.next ?? ''"
            class="flex items-center justify-center px-3 py-2 text-sm rounded-lg text-gray-600"
            :class="{ 'bg-gray-200': pagination.next, '!text-gray-300': !pagination.next }"
        >
            Next
        </Link>
    </nav>
</template>

Now that we're done, feel free to grab the code above and customise it for your needs. The Laravel paginator returns useful data, so you should have plenty to play with!

Happy paginating.

Thanks for reading! If you found this article helpful, you might enjoy our practical screencasts too.
Author
Alex Garrett-Smith
Share :

Comments

No comments, yet. Be the first to leave a comment.