Debouncing with `$effect` in Svelte

Introduction to Debouncing in Svelte

In the previous lesson, you learned how to manage form state in Svelte using the $state rune, creating reactive forms that update in real time. Now, we’ll explore another practical use case for reactivity: debouncing. Debouncing is a technique used to limit how often a function or action is executed, particularly in response to frequent events like typing in a search input. Without debouncing, every keystroke could trigger an update or API call, which can be inefficient and overwhelming for your application.

In this lesson, you’ll learn how to implement debouncing in Svelte using the $effect rune. By the end, you’ll be able to build a debounced search input that only updates after the user has stopped typing for a short period. This builds on your knowledge of $state and $effect, applying these concepts to a real-world scenario.

Let’s get started!

Understanding the Problem

Imagine you’re building a search feature for a website. As the user types into the search box, you want to display results dynamically. However, if you update the results with every keystroke, it can lead to unnecessary updates or API calls, especially if the user types quickly. This can slow down your application and create a poor user experience.

Debouncing solves this problem by delaying the update until the user has paused typing. For example, if the user types “apple,” the search results won’t update until they’ve stopped typing for 500 milliseconds. This ensures that the application only processes the final input, reducing unnecessary work.

To illustrate the issue, here’s a simple search input without debouncing:

Svelte
<script>
    let searchQuery = $state('');
</script>

<input
    type="text"
    placeholder="Search..."
    bind:value={searchQuery}
/>

<p>Searching for: {searchQuery}</p>

In this example, the searchQuery updates with every keystroke, which is not ideal. Let’s fix this by adding debouncing.

Implementing Debouncing with `$effect`

To implement debouncing, we’ll use the $effect rune to delay the update of the search query. Here’s the complete code for a debounced search input:

Svelte
<script>
    let searchQuery = $state(''); // Reactive state for the search input
    let debouncedQuery = $state(''); // Reactive state for the debounced value

    // Use $effect to debounce the search query
    $effect(() => {
        if (!searchQuery) {
            debouncedQuery = ''; // Clear debounced query if search query is empty
            return;
        }

        // Set a timeout to update the debounced query after 500ms
        const handler = setTimeout(() => {
            debouncedQuery = searchQuery;
        }, 500);

        // Cleanup function to clear the timeout if the search query changes before the timeout completes
        return () => clearTimeout(handler);
    });
</script>

<!-- Search input field bound to the searchQuery state -->
<input
    type="text"
    placeholder="Search..."
    bind:value={searchQuery}
/>

<!-- Display the debounced search query -->
<p>Searching for: {debouncedQuery}</p>

Let’s break this down step by step:

  1. Reactive States: We define two reactive states using $state: searchQuery for the user’s input and debouncedQuery for the debounced value.
  2. Debouncing with $effect: Inside the $effect rune, we set a timeout to update debouncedQuery after 500 milliseconds. If the user types again before the timeout completes, the cleanup function (clearTimeout) ensures the previous timeout is canceled.
  3. Binding the Input: The bind:value directive connects the input field to searchQuery, ensuring the state updates with every keystroke.
  4. Displaying the Debounced Value: The debouncedQuery is displayed in a paragraph, showing the final search term after the user has paused typing.
Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal