0
0
Remixframework~8 mins

Search implementation in Remix - Performance & Optimization

Choose your learning style9 modes available
Performance: Search implementation
MEDIUM IMPACT
This affects page load speed and interaction responsiveness by how search queries are handled and results rendered.
Implementing a search feature that updates results as the user types
Remix
import { useState, useEffect } from 'react';
import { useDebounce } from 'use-debounce';

export function Search() {
  const [query, setQuery] = useState('');
  const [debouncedQuery] = useDebounce(query, 300);
  const [results, setResults] = useState([]);

  useEffect(() => {
    if (debouncedQuery) {
      fetch(`/api/search?q=${debouncedQuery}`)
        .then(res => res.json())
        .then(data => setResults(data));
    } else {
      setResults([]);
    }
  }, [debouncedQuery]);

  return (
    <>
      <input value={query} onChange={e => setQuery(e.target.value)} aria-label="Search input" />
      <ul>{results.map(r => <li key={r.id}>{r.name}</li>)}</ul>
    </>
  );
}
Debouncing delays the fetch until the user stops typing for 300ms, reducing network calls and re-renders, improving input responsiveness.
📈 Performance GainReduces network requests by up to 90%, lowers re-renders, improves INP significantly.
Implementing a search feature that updates results as the user types
Remix
import React from 'react';
export function Search() {
  const [query, setQuery] = React.useState('');
  const [results, setResults] = React.useState([]);

  React.useEffect(() => {
    fetch(`/api/search?q=${query}`)
      .then(res => res.json())
      .then(data => setResults(data));
  }, [query]);

  return (
    <>
      <input value={query} onChange={e => setQuery(e.target.value)} />
      <ul>{results.map(r => <li key={r.id}>{r.name}</li>)}</ul>
    </>
  );
}
This triggers a fetch request on every keystroke, causing many network calls and re-renders, leading to slow input responsiveness.
📉 Performance CostTriggers multiple network requests and re-renders per keystroke, increasing INP and blocking main thread.
Performance Comparison
PatternDOM OperationsReflowsPaint CostVerdict
Fetch on every keystrokeMany network calls, frequent state updatesMultiple reflows per keystrokeHigh paint cost due to frequent updates[X] Bad
Debounced fetchFewer network calls, batched updatesSingle reflow per user pauseLower paint cost[OK] Good
Render full large listThousands of DOM nodesSingle but heavy reflowHigh paint cost[X] Bad
Virtualized list renderingFew DOM nodes at a timeMinimal reflowsLow paint cost[OK] Good
Rendering Pipeline
Search input changes trigger JavaScript events that may cause network requests and DOM updates. These updates flow through style calculation, layout, paint, and composite stages.
JavaScript Execution
Layout
Paint
Composite
⚠️ BottleneckLayout and Paint stages become expensive when many DOM nodes update or large lists render.
Core Web Vital Affected
INP
This affects page load speed and interaction responsiveness by how search queries are handled and results rendered.
Optimization Tips
1Avoid fetching search results on every keystroke; use debouncing.
2Limit DOM nodes rendered for search results using virtualization.
3Minimize layout and paint work by batching updates and reducing reflows.
Performance Quiz - 3 Questions
Test your performance knowledge
What is the main performance problem with fetching search results on every keystroke?
AIt increases the initial page load time
BIt causes many network requests and slows input responsiveness
CIt causes layout shifts on page load
DIt reduces bundle size
DevTools: Performance
How to check: Record a performance profile while typing in the search input. Look for frequent scripting and layout events.
What to look for: High scripting time and many layout events indicate inefficient search updates. Lower and fewer events indicate good performance.