debouncedsearch
Reactive icon

DebouncedSearch

Stable version 1.0.0 (Compatible with OutSystems 11)
Uploaded
 on 9 Sep (13 hours ago)
 by 
0.0
 (0 ratings)
debouncedsearch

DebouncedSearch

Documentation
1.0.0

Debounced Search Input

A drop-in search input for OutSystems Reactive Web that fires its search event only after the user pauses typing — so you get one backend call for "hello" instead of five for "h", "he", "hel", "hell", "hello". Includes a minimum-length threshold and a clear button. Pure client-side, no dependencies.


1. Overview

A naive search input fires on every keystroke. Type "hello" and the backend gets hit five times, results flicker as they arrive out of order, and the app feels heavy. Debounced Search Input waits for typing to pause for a configurable interval before firing its OnSearch event, so the backend gets called exactly once per pause with the current text. It's a tiny piece of code with an outsized impact on performance and perceived quality.

Key features

  • Fires OnSearch only after the user pauses typing (default 300ms).
  • Minimum-length threshold — don't search until the user has typed N characters (great for "type at least 2 letters" search).
  • Clear button — an X icon that appears when there's text; clears and refires with an empty term.
  • Enter fires immediately — power users expect Enter to search instantly, bypassing the debounce.
  • No same-value refires — pausing after "cat", then unpausing without changing anything, won't refire.
  • Accessible: keyboard-focusable, proper labels; the clear button is a real button with type=button.
  • No dependencies, no server calls.

2. Compatibility

ItemRequirement
OutSystems versionOutSystems 11 (O11)
App typeReactive Web
DependenciesNone

3. Installation

  1. Install Debounced Search Input from the Forge (or the .oap via Service Center).
  2. In Manage Dependencies, select the module and check the DebouncedSearchInput block.
  3. Apply and publish.

4. Quick start

DebouncedSearchInput
   Placeholder    = "Search products..."
   DebounceMs     = 400
   MinimumLength  = 2

Handle OnSearch(SearchTerm):
   MyQuery = SearchTerm
   Refresh MyAggregate   // your search results query

Drop the block on a screen, wire OnSearch to update your query variable, and refresh whatever list of results you show below. That's the whole integration.


5. Reference — the DebouncedSearchInput block

InputTypeDefaultDescription
ValueText""The current search text. Updated as the user types (post-debounce).
PlaceholderText"Search..."Placeholder text shown when empty.
DebounceMsInteger300Milliseconds to wait after the last keystroke before firing OnSearch.
MinimumLengthInteger0Don't fire until the user has typed at least this many characters. 0 fires from the first character.
ShowClearButtonBooleanTrueShow an X button to clear the input when there's text.
EventParametersWhen it fires
OnSearchSearchTerm (Text)The user paused typing for DebounceMs, or pressed Enter, or clicked Clear.

6. How it works

On every keystroke, the block cancels any pending "search" timer and starts a new one for DebounceMs. Only the last keystroke's timer survives to fire, and that's the one whose value gets passed to OnSearch. This is called debouncing — waiting for the user to pause before acting.

Two additional behaviors on top of the debounce:

  • Enter fires immediately, skipping the debounce entirely. Users expect the Enter key to mean "search now."
  • Same-value doesn't refire. If a search already fired with "cat" and the value hasn't changed since, subsequent focus/blur or non-editing keystrokes won't refire. This prevents duplicate calls for the same query.

7. Debounce vs. throttle (a common mix-up)

  • Debounce: wait for typing to pause. Great for search, resize, and other "when they're done" cases.
  • Throttle: fire at most every N ms. Great for scroll listeners and other high-frequency events you want to sample.

This block does debouncing, which is what you want for search. If you need throttling, you're building a different component.


8. Tuning DebounceMs

The default of 300ms is a good sweet spot — long enough that a normal typist gets one search per word, short enough that the result feels responsive. Guidance for other cases:

  • 100–200ms — Fast searches where the backend is quick and results are cheap; feels almost instant.
  • 300ms — Recommended default for most cases.
  • 500–800ms — Expensive searches (full-text on large datasets, external APIs with rate limits) or slow networks. Users won't mind the extra wait, and you dramatically cut load.

Above ~1000ms the delay starts to feel like lag rather than debouncing.


9. Using the minimum-length threshold

Setting MinimumLength = 2 or 3 is a common pattern for two reasons:

  • Reduces useless searches — a single character ("a") often matches too much to be useful and costs backend time.
  • Reduces UI noise — matches for "a" flooding a dropdown before the user has really started typing is disruptive.

For long queries or specific searches (people names, product codes), MinimumLength = 3 is a good default. For quick filters where any input is meaningful, keep it at 0.


10. The clear button

When ShowClearButton = True (the default), an X icon appears inside the input on the right whenever there's text. Clicking it:

  1. Clears the input,
  2. Fires OnSearch("") so the consumer can reset their results,
  3. Returns focus to the input for continued typing.

Set ShowClearButton = False if your design provides its own way to clear the search (or if you don't want it to be clearable that way).


11. Styling

The block uses these classes: dsi-wrap (the wrapper), dsi-input (the text input), dsi-clear (the clear button), and dsi-x-icon (the X inside the clear button). Override in your app theme to customize:

.dsi-input { border-radius: 999px; padding-left: 40px; }  /* pill shape */
.dsi-input:focus { border-color: #10b981; box-shadow: 0 0 0 3px rgba(16,185,129,.25); }
.dsi-clear { color: #ef4444; }

The X icon is drawn with CSS pseudo-elements and adopts the button's text color via currentColor, so themeing the color just works.


12. Accessibility

The input is a real HTML input, so it's keyboard-focusable, screen-reader-friendly, and honors browser autofill and spellcheck settings. The clear button is a real <button> with type="button" (won't submit forms) and an aria-label. Both are reachable via Tab and activate with Enter or Space.

For screen-reader users, consider pairing the block with an aria-live region elsewhere on your screen that announces "N results found" when results update — the standard pattern for accessible search.


13. Troubleshooting / FAQ

OnSearch fires on every keystroke.Check DebounceMs isn't 0. Also, if the block was re-initialized (a duplicate On Ready call), you may have two listeners attached — the block guards against this with an internal flag, but if you see doubles, republish to make sure the latest block code is loaded.

Nothing happens when I type.If MinimumLength is above 0, OnSearch won't fire until you've typed that many characters. Also confirm you're actually handling OnSearch on the consuming screen — the block fires the event; you have to react to it.

Enter doesn't work.Enter fires OnSearch immediately (skipping the debounce). If your form has other Enter behavior, that may take precedence — check the form's own submit handling.

Clear button doesn't appear.It only appears when there's text in the field (there's nothing to clear otherwise). Also confirm ShowClearButton = True.

Results still lag behind by a beat.That's how debouncing works — the last N milliseconds of typing haven't fired yet. Lower DebounceMs if you want faster feedback (at the cost of more backend calls).


14. Best practices

  • Start with DebounceMs = 300 and MinimumLength = 0; adjust based on your backend cost and UX feel.
  • For expensive searches, use MinimumLength = 2 or 3 to cut low-value queries.
  • Handle OnSearch("") explicitly — the clear button and empty state deserve a defined behavior (usually: reset to unfiltered results).
  • Pair with a loading spinner on your results area while the actual search runs — the block only handles input timing, not the network round-trip.
  • Test on a slow network to feel the difference — that's where debouncing pays for itself.

15. Version history

VersionNotes
1.0.0Initial release. Debounced input with configurable interval; minimum-length threshold; Enter-to-search-immediately; clear button; same-value dedup; accessible.

16. License

Published on the OutSystems Forge as open, reusable code. Free to use and adapt in your OutSystems projects.