๐Ÿ” Projects

Live Search and Filters in Vanilla JS โ€” The Pattern Behind Every List UI

๐Ÿ“… Jul 5, 2026 โฑ 3 min read

Search box + filter chips + a grid โ€” the pattern behind dashboards, product lists and this very blog's index page.

The single-function architecture

One state object, one applyFilters() that re-evaluates everything. Never write separate "search logic" and "filter logic" that fight:

const state = { query: "", category: "all" };

function applyFilters() {
  let visible = 0;
  cards.forEach((card) => {
    const matchesQuery = !state.query ||
      card.dataset.search.includes(state.query);       // pre-lowercased text
    const matchesCat = state.category === "all" ||
      card.dataset.cat === state.category;
    const show = matchesQuery && matchesCat;
    card.hidden = !show;
    if (show) visible++;
  });
  emptyMsg.hidden = visible > 0;
  countEl.textContent = `${visible} results`;
}

searchInput.addEventListener("input", debounce(() => {
  state.query = searchInput.value.trim().toLowerCase();
  applyFilters();
}, 150));

chipBar.addEventListener("click", (e) => {
  const chip = e.target.closest("[data-cat]");
  if (!chip) return;
  state.category = chip.dataset.cat;
  chips.forEach(c => c.classList.toggle("active", c === chip));
  applyFilters();
});

Polish that separates portfolios

See it live on our blog index โ€” 100+ posts, instant filtering, exactly this code.

โ† All Articles