Slater.app CMS

CMS Filter and Search in Webflow with Vanilla JS (No Library) using Slater

Here’s a lightweight, library-free pattern for CMS search and multi-tag filtering on Webflow—scoped safely and deployable via Slater.

You can add fast, client-side search and multi-tag filtering to a Webflow Collection List with a few data attributes and a small vanilla JS module published from Slater.

What you’ll build

Assumed markup (Designer)

Vanilla JS module (paste into a Slater file)

(function initCmsFilter() {
  const root = document.querySelector('[data-cms="list"]');
  if (!root) return; // page doesn’t have the list
  if (root.dataset.inited) return; // avoid double init
  root.dataset.inited = '1';

  const items = Array.from(root.querySelectorAll('[data-cms="item"]'));
  const state = { q: '', tags: new Set() };

  function itemMatches(el) {
    const text = el.textContent.toLowerCase();
    if (state.q && !text.includes(state.q)) return false;
    if (state.tags.size) {
      const tags = (el.dataset.tags || '')
        .toLowerCase()
        .split(',')
        .map(s => s.trim())
        .filter(Boolean);
      for (const t of state.tags) if (!tags.includes(t)) return false; // AND logic
    }
    return true;
  }

  function render() {
    for (const el of items) el.style.display = itemMatches(el) ? '' : 'none';
  }

  const search = document.querySelector('[data-filter="search"]');
  if (search) search.addEventListener('input', (e) => {
    state.q = e.target.value.toLowerCase().trim();
    render();
  });

  document.addEventListener('change', (e) => {
    const cb = e.target;
    if (cb && cb.matches('[data-filter="tag"]')) {
      const v = (cb.value || '').toLowerCase();
      cb.checked ? state.tags.add(v) : state.tags.delete(v);
      render();
    }
  });

  render();
})();

Optional: sorting (A→Z)

(function addSortAZ() {
  const root = document.querySelector('[data-cms="list"]');
  const sortBtn = document.querySelector('[data-sort="az"]');
  if (!root || !sortBtn) return;
  const wrapper = root.parentElement; // container that holds the list
  sortBtn.addEventListener('click', () => {
    const items = Array.from(root.querySelectorAll('[data-cms="item"]'))
      .sort((a, b) => a.textContent.localeCompare(b.textContent));
    const frag = document.createDocumentFragment();
    items.forEach(i => frag.appendChild(i));
    root.appendChild(frag);
  });
})();

Performance and UX tips

Publishing

FAQ