Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

d3lus10n's avatar

Filter records with infinite pagination in Inertia

Hewo, I just completed the Infinite Pagination course with Inertia and Vue from Laracast and it does not contemplate the implementation of filters, I have done them as usual through request and a scope that filters the query on the back-end, but I am very confused how to make it work on the front-end, the files are as follows:

// Index.vue

<script setup>
import { ref, watch } from 'vue'
import ListItem from '@/Components/ListItem.vue'
import Filters from '@/Components/Filters.vue'
import { useInfiniteScroll } from '../../Composables/useInfiniteScroll'
import { pickBy, debounce } from 'lodash'
import { router } from '@inertiajs/vue3'

const props = defineProps({
  recipes: {
    type: Object,
    default() {
      return {}
    },
  },
  filters: {
    type: Object,
    default: null,
  },
})

const params = ref({
  filters: {
    search: props.filters.search,
  },
})

const landmark = ref(null)

let { items, canLoadMoreItems } = useInfiniteScroll('recipes', landmark)

let refetch = debounce(function () {
  router.get('/recipes', pickBy({ ...params.value.filters }), {
    replace: true,
    preserveState: true,
  })
}, 200)

watch(params, () => refetch(), { deep: true })
</script>

<template>
<!-- ... -->
  <section class="hidden 2xl:block 2xl:w-full">
    <Filters v-model="params.filters.search" />
  </section>
            <ListItem
              v-for="recipe in items" />
<!-- ... -->
</template>
// useInfiniteScroll.vue composable

import { computed, ref } from 'vue'
import { router, usePage } from '@inertiajs/vue3'
import { useIntersect } from '@/Composables/useIntersect'

export function useInfiniteScroll(propName, landmark = null) {
  const value = () => usePage().props[propName]

  const items = ref(value().data)

  const initialUrl = usePage().url

  const canLoadMoreItems = computed(() => value().next_page_url !== null)

  const loadMoreItems = () => {
    if (!canLoadMoreItems.value) {
      return
    }

    router.get(
      value().next_page_url,
      {},
      {
        preserveState: true,
        preserveScroll: true,
        onSuccess: () => {
          window.history.replaceState({}, '', initialUrl)
          items.value = [...items.value, ...value().data]
        },
      },
    )
  }

  if (landmark !== null) {
    useIntersect(landmark, loadMoreItems, {
      rootMargin: '0px 0px 150px 0px',
    })
  }

  return {
    items,
    loadMoreItems,
    resetItems: () => (items.value = value().data),
    canLoadMoreItems,
  }
}

I have tried so many things but I don't have much experience in Vue and I would appreciate any help, thanks!

0 likes
2 replies
LaryAI's avatar
Level 58

To implement filters with infinite pagination in Inertia, you can follow these steps:

  1. Update the Index.vue file to include the filter component and pass the params.filters.search value as a prop to the filter component:
<template>
  <!-- ... -->
  <section class="hidden 2xl:block 2xl:w-full">
    <Filters v-model="params.filters.search" />
  </section>
  <!-- ... -->
</template>
  1. Modify the Filters.vue component to emit an event whenever the filter value changes:
<template>
  <input type="text" v-model="search" @input="updateFilter" />
</template>

<script>
export default {
  props: ['value'],
  emits: ['update:modelValue'],
  data() {
    return {
      search: this.value,
    }
  },
  methods: {
    updateFilter() {
      this.$emit('update:modelValue', this.search)
    },
  },
}
</script>
  1. Update the Index.vue file to watch for changes in the filter value and trigger a refetch of the data:
<script setup>
// ...
watch(params, () => refetch(), { deep: true })
</script>
  1. Modify the useInfiniteScroll composable to include the filter value in the request:
export function useInfiniteScroll(propName, landmark = null) {
  // ...
  const loadMoreItems = () => {
    if (!canLoadMoreItems.value) {
      return
    }

    router.get(
      value().next_page_url,
      {
        filters: {
          search: params.value.filters.search,
        },
      },
      {
        preserveState: true,
        preserveScroll: true,
        onSuccess: () => {
          window.history.replaceState({}, '', initialUrl)
          items.value = [...items.value, ...value().data]
        },
      },
    )
  }
  // ...
}

With these changes, the filter value will be passed to the back-end on each request, allowing you to filter the records accordingly.

d3lus10n's avatar

@LaryAI I appreciate your help cutie AI, in the composable I can do what you indicate in the following way, but it only works if I reload the page completely, not while writing, this behavior also works like this in my code base (everything else I already implemented although I didn't share it)

 filters: {
  search: usePage().props['filters'].search,
},

Please or to participate in this conversation.