Skip to main content
The <VueListSearch> component provides a search input that automatically triggers data fetching when the user types. It includes built-in debouncing to avoid excessive API calls.

Basic usage

<template>
  <VueList endpoint="users">
    <VueListSearch />
    
    <VueListItems #default="{ items }">
      <div v-for="user in items" :key="user.id">
        {{ user.name }}
      </div>
    </VueListItems>
  </VueList>
</template>

Props

debounceTime
number
default:"1000"
Debounce delay in milliseconds. The search won’t trigger until the user stops typing for this duration.
<VueListSearch :debounce-time="500" />

Slot

The default slot receives a scope object with the search state and setter:
<VueListSearch #default="{ search, setSearch }">
  <input
    type="search"
    :value="search"
    @input="setSearch($event.target.value)"
    placeholder="Search users..."
    class="search-input"
  />
</VueListSearch>

Scope object

  • search - Current search query string
  • setSearch(value) - Function to update the search (already debounced)

Behavior

  • Debounced by default: Waits for the user to stop typing before fetching data
  • Resets to page 1: When search changes, the list automatically goes back to the first page
  • Accessible via requestHandler: The search value is passed to your requestHandler as context.search

Examples

Styled search input

<VueListSearch :debounce-time="300">
  <template #default="{ search, setSearch }">
    <div class="search-container">
      <input
        type="search"
        :value="search"
        @input="setSearch($event.target.value)"
        placeholder="Search..."
        class="px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500"
      />
    </div>
  </template>
</VueListSearch>

With search icon

<VueListSearch>
  <template #default="{ search, setSearch }">
    <div class="relative">
      <svg class="absolute left-3 top-3 w-5 h-5 text-gray-400" fill="none" stroke="currentColor">
        <path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
      </svg>
      <input
        type="search"
        :value="search"
        @input="setSearch($event.target.value)"
        placeholder="Search products..."
        class="pl-10 pr-4 py-2 border rounded"
      />
    </div>
  </template>
</VueListSearch>

With clear button

<VueListSearch>
  <template #default="{ search, setSearch }">
    <div class="search-with-clear">
      <input
        type="search"
        :value="search"
        @input="setSearch($event.target.value)"
        placeholder="Search..."
        class="search-input"
      />
      <button
        v-if="search"
        @click="setSearch('')"
        class="clear-btn"
        type="button"
      >
        ×
      </button>
    </div>
  </template>
</VueListSearch>

With loading indicator

<VueList endpoint="users" #default="{ isLoading }">
  <VueListSearch>
    <template #default="{ search, setSearch }">
      <div class="relative">
        <input
          type="search"
          :value="search"
          @input="setSearch($event.target.value)"
          placeholder="Search users..."
          class="w-full px-4 py-2 border rounded"
        />
        <div v-if="isLoading" class="absolute right-3 top-3">
          <svg class="animate-spin h-5 w-5 text-blue-500" fill="none" viewBox="0 0 24 24">
            <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
            <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
          </svg>
        </div>
      </div>
    </template>
  </VueListSearch>
  
  <!-- ... items -->
</VueList>

Advanced search with filters

<template>
  <VueList endpoint="products" v-model:filters="filters">
    <div class="search-bar">
      <VueListSearch :debounce-time="500">
        <template #default="{ search, setSearch }">
          <input
            type="search"
            :value="search"
            @input="setSearch($event.target.value)"
            placeholder="Search products..."
          />
        </template>
      </VueListSearch>
      
      <select v-model="filters.category">
        <option value="">All categories</option>
        <option value="electronics">Electronics</option>
        <option value="clothing">Clothing</option>
      </select>
      
      <label>
        <input type="checkbox" v-model="filters.inStock" />
        In stock only
      </label>
    </div>
    
    <VueListItems #default="{ items }">
      <!-- render items -->
    </VueListItems>
  </VueList>
</template>

<script setup>
import { ref } from 'vue'

const filters = ref({
  category: '',
  inStock: false
})
</script>

Handling search in requestHandler

The search value is passed to your request handler as context.search:
app.use(VueList, {
  requestHandler(context) {
    const { endpoint, page, perPage, search } = context
    
    return axios.get(`/api/${endpoint}`, {
      params: {
        page,
        limit: perPage,
        q: search, // Pass search to your API
      }
    })
    .then(({ data }) => ({
      items: data.results,
      count: data.total
    }))
  }
})
Adjust debounceTime based on your use case:
  • 300-500ms: Fast, responsive search for local or cached data
  • 1000ms (default): Good balance for most APIs
  • 1500-2000ms: Reduces load for expensive search operations
Searching automatically resets the list to page 1 to show the first page of search results.

Next steps

Filtering and sorting

Combine search with filters and sorting

Request handler

Learn how to handle search in your API

Build docs developers (and LLMs) love