Algolia ×
Webflow Filters

A drop-in filtering solution for Webflow using Algolia. No 100-item CMS limit, full-text search, faceted filtering, pagination, and URL state sync — all driven by HTML data-* attributes.

Real-time data sync

Three parts, one solution

The library ships as three independent pieces. Each can be reused across projects with isolated configuration.

Next.js on Webflow Cloud

Sync App

Reads all CMS items via the Webflow API, resolves references and option fields to human-readable values, and pushes everything to Algolia. Handles full paginated syncs.

🔗

Cloudflare Worker

Webhook Worker

Listens for Webflow webhooks and keeps Algolia in sync incrementally — per-item changes on create/edit/delete, and triggers a full re-sync on site publish.

📦

Vanilla JS via jsDelivr

Client Library

A single <script> tag loaded in Webflow. Reads HTML data-* attributes, queries Algolia, renders results, and handles filtering, searching, pagination, and active tags.

Get running in 11 steps

Follow these steps in order. Each one builds on the previous. The whole setup takes about 30 minutes.

Prerequisites

A Webflow site with a CMS collection
A free Algolia account
A free Cloudflare account
A GitHub account

Go to github.com/felixeallan/algolia-webflow-filter and click "Use this template""Create a new repository". Name it (e.g. my-site-algolia) and make it public (required so jsDelivr can serve the script). Done — you have your own copy.

Sign up at algolia.com and create an application. Decide a name for your index (e.g. products, blog-posts, cars) — Algolia will create it automatically when the first sync runs, you do not need to create it manually.

Go to Settings → API Keys and copy these three values: - Application IDALGOLIA_APP_ID - Search API Key → used later in the script tag (safe to expose) - Write API KeyALGOLIA_ADMIN_API_KEY (keep secret, only used server-side)

Screenshot placeholder

Algolia dashboard → Settings → API Keys. Copy the Application ID, Search-Only API Key, and Admin API Key.

API token

Webflow → Site Settings → Apps & Integrations → API Access → Generate API Token. Permissions: CMS: Read (read-only is enough — the sync never writes back). Copy the token → WEBFLOW_API_TOKEN.

Collection ID

In Webflow → CMS → click your collection → Settings → copy the Collection ID → WEBFLOW_COLLECTION_ID.

Screenshot placeholder

Webflow → CMS → select your collection → Settings tab. The Collection ID appears at the bottom of the settings panel.

In your Webflow site → Site Settings → Webflow Cloud. Click "Install GitHub app" if needed, then "New app".

Configure: - Name: anything (e.g. algolia-sync) - Repository: your project repo from Step 1 - Directory path: apps/sync - GitHub branch: main - Path: /api (this becomes the URL prefix, e.g. yoursite.com/api/sync)

Once the environment is created, go to Environment Variables and add all 6 below. Click "Deploy latest commit". Wait for the deployment to go live.

The endpoint health check should return {"ok":true}:

VariableType
WEBFLOW_API_TOKENSecret
WEBFLOW_COLLECTION_IDText
ALGOLIA_APP_IDText
ALGOLIA_ADMIN_API_KEYSecret — this is the Write API Key
ALGOLIA_INDEX_NAMEText — the name you chose, e.g. "products"
SYNC_SECRETSecret — generate with: openssl rand -hex 32
Health check
curl https://YOUR_SITE.webflow.io/api/sync

Screenshot placeholder

Webflow Cloud → New App form. Fill in Name, Repository, Directory path (apps/sync), Branch, and Path (/api).

Trigger the first sync by hitting the endpoint with your secret. Check the Algolia dashboard → your index → Browse — all your CMS items should be there.

Trigger sync
curl -X POST https://YOUR_SITE.webflow.io/api/sync \
  -H "Authorization: Bearer YOUR_SYNC_SECRET"
Expected response
{ "success": true, "synced": 1234 }

The sync app handles the initial bulk sync. The webhook worker keeps Algolia in sync as users publish/edit/delete individual CMS items. Webflow does not allow webhooks pointing to *.webflow.io domains, so we need an external worker.

  1. Go to dash.cloudflare.comWorkers & PagesCreateCreate WorkerHello World
  2. Name it (e.g. algolia-webflow-webhook) → Deploy
  3. Click "Edit code", delete everything, and paste the contents of apps/webhook-worker/src/index.jsDeploy
  4. Go to Settings → Variables and Secrets and add the variables below
  5. Note your worker URL — looks like https://algolia-webflow-webhook.YOUR-USERNAME.workers.dev
VariableType
ALGOLIA_APP_IDText
ALGOLIA_ADMIN_API_KEYSecret — the Write API Key
ALGOLIA_INDEX_NAMEText
WEBFLOW_COLLECTION_IDText — so webhooks from other collections get ignored
SYNC_ENDPOINTText — https://YOUR_SITE.webflow.io/api/sync
SYNC_SECRETSecret — same as Webflow Cloud

Screenshot placeholder

Cloudflare Workers & Pages → Create Worker → Hello World template. After deploy, use "Edit code" to paste the webhook worker script.

Webflow → Site Settings → Apps & Integrations → Webhooks → Add Webhook for each event below. The URL is the worker URL from Step 6. You do not need a webhook secret — leave that field blank.

EventPurpose
collection_item_createdNew items appear instantly in Algolia
collection_item_changedEdits sync instantly
collection_item_deletedDeletes remove from Algolia
collection_item_unpublishedUnpublished items removed from Algolia
site_publishTriggers a full re-sync (for schema changes, new fields, reference updates)

Screenshot placeholder

Webflow → Site Settings → Integrations → Webhooks. Add one webhook per event, all pointing at the same Cloudflare Worker URL.

In Algolia → your index → Configuration:

Facets — fields you want to filter by: - Add every attribute that will be used as a filter (e.g. category, brand, color, year) - Reference fields show up as the referenced item's name automatically (e.g. car-brand: "Quasar")

Searchable Attributes — fields used by the search input: - Set to Ordered mode - Top of the list = higher relevance - Add fields like name, title, description

Click "Review and Save settings".

Screenshot placeholder

Algolia → your index → Configuration → Facets. Add each filterable field slug (e.g. car-brand, year, featured). Then click "Review and Save settings".

Sorting in Algolia works through replica indexes — pre-sorted copies of the main index. Each user-facing sort option = one replica. Algolia keeps replicas in sync automatically.

9.1 — Create one replica per sort option

In Algolia → your main index → Configuration → Replicas → Create Replica. Choose Virtual replica (free, no extra storage). Name it using the format INDEX_FIELD_DIRECTION:

9.2 — Configure each replica's Sort-by rule

For each replica, click its name to open it → Configuration → Relevant sort → add exactly one Sort-by rule matching the replica's name → click Review and Save settings.

**Important:** Each replica must have **exactly one** Sort-by rule. Multiple rules turn additional ones into tiebreakers. One replica = one dropdown option.

9.3 — Add the sort dropdown to your page

Use a native <select> element (not Webflow's navigation "Dropdown" component — that's a div-based menu and won't fire change events).

9.4 — Caveat: sorting numbers stored as strings

For sorting to behave numerically, the field must be a number in Algolia. Use a Webflow Number field (not a Plain text field formatted to look like a number) for any field you plan to sort numerically.

Sort optionReplica name
Name A → Zcars_name_asc
Name Z → Acars_name_desc
Price ↑cars_price_asc
Price ↓cars_price_desc
Year newestcars_year_desc
Year oldestcars_year_asc
Sort dropdown
<select data-algolia-sort>
  <option value="">Default (relevance)</option>
  <option value="cars_name_asc">Name: A → Z</option>
  <option value="cars_name_desc">Name: Z → A</option>
  <option value="cars_price_asc">Price: Low → High</option>
  <option value="cars_price_desc">Price: High → Low</option>
  <option value="cars_year_desc">Year: Newest first</option>
  <option value="cars_year_asc">Year: Oldest first</option>
</select>

Screenshot placeholder

Algolia → your index → Configuration → Replicas. Create a Virtual Replica per sort option. Each replica gets exactly one Sort-by rule.

In Webflow → Site Settings → Custom Code → Footer, add the script tag below.

Always pin to a version tag (e.g. @v0.8.2). Do not use @main — jsDelivr aggressively caches branch URLs.

Script tag (Webflow footer)
<script src="https://cdn.jsdelivr.net/gh/felixeallan/algolia-webflow-filter@v0.8.2/packages/library/dist/algolia-webflow.min.js"></script>

Add the wrapper div with your Algolia credentials, then place filter inputs, result list, and pagination inside it. See the HTML Structure and Data Attribute Reference sections below for the complete reference.

Full HTML example
<div data-algolia data-algolia-app-id="YOUR_APP_ID" data-algolia-api-key="YOUR_SEARCH_ONLY_KEY" data-algolia-index="cars" data-algolia-hits-per-page="12" data-algolia-url-state
>

  <!-- Search input -->
  <input data-algolia-search type="text" placeholder="Search...">

  <!-- Filters: checkbox = multi-select (OR within group, AND between groups) -->
  <label data-algolia-filter="car-brand" data-algolia-value="Quasar">
    <input type="checkbox"><span>Quasar</span>
  </label>

  <!-- Filters: radio = single-select within the group -->
  <label data-algolia-filter="color-theme" data-algolia-value="White">
    <input type="radio" name="color"><span>White</span>
  </label>

  <!-- Dropdown filter -->
  <select data-algolia-filter-select="year">
    <option value="">All years</option>
    <option value="2024">2024</option>
    <option value="2023">2023</option>
  </select>

  <!-- Sort dropdown (uses Algolia index replicas) -->
  <select data-algolia-sort>
    <option value="">Relevance</option>
    <option value="cars_price_asc">Price ↑</option>
    <option value="cars_price_desc">Price ↓</option>
  </select>

  <!-- Clear buttons -->
  <button data-algolia-clear>Clear all</button>
  <button data-algolia-clear="car-brand">Clear brands</button>

  <!-- Active filter tags -->
  <div data-algolia-tags>
    <div data-algolia-tag-template class="tag">
      <span data-algolia-tag-label></span>
      <span data-algolia-tag-remove>×</span>
    </div>
  </div>

  <!-- Result list with template -->
  <div data-algolia-list>
    <div data-algolia-template class="card">
      <img data-algolia-bind="image.url" data-algolia-attr="src">
      <h3 data-algolia-bind="name"></h3>
      <p data-algolia-bind="car-brand"></p>
      <img data-algolia-bind="car-brand__logo.url" data-algolia-attr="src">
      <p data-algolia-bind="price"></p>
      <p data-algolia-bind="year"></p>

      <!-- Repeat for array/multi-reference fields -->
      <div data-algolia-hide-empty="authors" data-algolia-repeat="authors">
        <span data-algolia-repeat-item class="author-tag"></span>
      </div>

      <a data-algolia-bind="slug" data-algolia-attr="href">View</a>
    </div>
  </div>

  <!-- Stats -->
  <p><span data-algolia-count></span> results</p>

  <!-- Empty state -->
  <div data-algolia-empty style="display:none">No results found.</div>

  <!-- Pagination -->
  <button data-algolia-prev>← Previous</button>
  <span data-algolia-page-info></span>
  <button data-algolia-next>Next →</button>

</div>

Webflow Setup

Everything here is configured directly in Webflow — no code required. Add these data-* attributes to your Webflow elements to connect them to Algolia.

Don't set up attributes manually — clone the Webflow template

All attributes below are already configured in the template. Pick from 4 examples: Standard Filter, Custom Pagination, Load More, or Range Slider Auto Bounds.

Clone Template
Required

Required Setup

These three groups are the minimum needed for filtering to work.

Wrapper

AttributeOnDescription
data-algoliaRequired
any (wrapper div)Marks the root. Everything else must be inside.
data-algolia-app-id="..."Required
wrapperAlgolia Application ID
data-algolia-api-key="..."Required
wrapperAlgolia Search-Only API key (safe to expose)
data-algolia-index="..."Required
wrapperAlgolia index name
data-algolia-hits-per-page="12"
wrapperNumber of results per page (default 12)
data-algolia-url-state
wrapper(optional) Sync filters/search/page to URL params for shareable links
data-algolia-match-mode="or"
wrapper(optional) Cross-group matching. Default = AND (item must match every group). "or" = OR (item matches any group). Ranges always stay AND with facets.
data-algolia-debounce="300"
wrapper(optional) Debounce delay in ms for search/range inputs. Default 300.
data-algolia-stagger="50"
wrapper(optional) Entrance animation delay in ms between result items. Default 0.

Filters & Search

Every attribute you filter by must be added as a Facet in Algolia (your index → Configuration → Facets). Without this, filters silently return no results. Radio buttons in the same filter group must share the same name attribute so the browser treats them as mutually exclusive. Checkboxes do not need a shared name.
AttributeOnDescription
data-algolia-searchRequired
<input type="text">Full-text search input. Debounced 300ms by default. Results update as the user types.
data-algolia-filter="attr" + data-algolia-value="val"
<label> + <input type="checkbox">Checkbox multi-select filter. Multiple checked values in the same attr are OR; across different attrs they are AND.
data-algolia-filter="attr" + data-algolia-value="val"
<label> + <input type="radio">Radio single-select filter. Only one value per attr group can be active. All radios in the group must share the same name attribute.
data-algolia-filter-all="attr"
<label> + <input type="radio">"All" option for a radio group. Auto-activates on load and whenever no specific value is selected in that group.
data-algolia-filter-select="attr"
<select>Dropdown (select) filter. An option with value="" resets the filter for that attr.

Result List

AttributeOnDescription
data-algolia-listRequired
containerWhere result items get injected.
data-algolia-templateRequired
<div> or <template>Cloned once per result, then injected into the list. A regular <div> is fine — it's hidden automatically.
data-algolia-bind="field"Required
any (inside template)Sets element text content from the Algolia hit field. Supports dot notation: image.url, car-brand__logo.url.
data-algolia-bind="field" + data-algolia-attr="name"
any (inside template)Sets an HTML attribute (src, href, alt, etc.) instead of text content.
data-algolia-hide-empty="field"
any (inside template)Hides this element when the bound field is empty, null, or an empty array.
Optional

Optional

Add any of these to extend your filter UI.

Pagination

Load More and Pagination are independent options — pick one. Avoid combining them. You can combine Prev/Next with Page counter and/or Numbered pages. Algolia caps pagination at 1,000 total results by default. Increase paginationLimitedTo in Algolia → your index → Configuration → Pagination if you need more.

Load More

A single button that appends the next page of results to the existing list. Auto-hides when there are no more pages.

AttributeOnDescription
data-algolia-load-more
anyAppend next page to existing results. Auto-hides when no more pages.

Prev / Next

Replaces the current results with the previous or next page. Standard navigation-style pagination.

AttributeOnDescription
data-algolia-prev
anyGo to previous page. Auto-disabled on the first page.
data-algolia-next
anyGo to next page. Auto-disabled on the last page.

Page counter

Displays "Page X of Y". Optional companion to Prev / Next.

AttributeOnDescription
data-algolia-page-info
anyShows the current page and total page count, e.g. "Page 2 of 12".

Numbered pages

Renders a row of clickable page-number buttons. Can be combined with Prev / Next and Page counter.

AttributeOnDescription
data-algolia-pages
containerWhere numbered page buttons are injected.
data-algolia-page-button-template
child of pages containerCloned for each visible page number. Active page receives data-active="" for styling.
data-algolia-page-dots-template
child of pages container(Optional) Cloned to render the "…" separator when page numbers are skipped.
data-algolia-page-siblings="2,1,1,0"
pages containerPages shown on each side of the current page. Comma-separated per Webflow breakpoint (Desktop, Tablet, Landscape, Portrait). Default 1.
data-algolia-page-boundaries="1,1,1,0"
pages containerPages always shown at the start and end of the list. Same breakpoint syntax. Default 1.

Sort

AttributeOnDescription
data-algolia-sort
<select>Each option's value must be an Algolia index replica name (e.g. cars_price_asc). Must be a native <select> — Webflow's "Dropdown" nav component is div-based and will not work.

Clear Buttons

AttributeOnDescription
data-algolia-clear
anyClears ALL filters, search query, and sort selection.
data-algolia-clear="attr"
anyClears only the specified filter group (e.g. data-algolia-clear="car-brand").

Empty State

AttributeOnDescription
data-algolia-empty
anyShown only when the current query returns zero results. Hidden otherwise.

Active Filter Tags

AttributeOnDescription
data-algolia-tags
containerWhere active filter tags get injected.
data-algolia-tag-template
child of tags containerCloned once per active filter. Hidden until filters are active.
data-algolia-tag-label
any (inside tag template)Gets the active filter value as text (e.g. "Quasar", "White").
data-algolia-tag-remove
any (inside tag template)Clicking removes that filter. If omitted, the whole tag element is clickable.

Scroll Anchor

AttributeOnDescription
data-algolia-scroll-anchor
anyScrolls smoothly to this element on every filter, search, sort, or pagination change. Can be inside or outside the [data-algolia] wrapper. Skipped on initial page load.

Stats

AttributeOnDescription
data-algolia-count
anyDisplays the total number of results matching the current query (e.g. 1,786). Updates on every filter change.

Range Filters & Slider

The slider must coexist with data-algolia-range-min / data-algolia-range-max inputs for the same attribute somewhere in the wrapper. The slider drives those inputs — they are the source of truth. You can hide them with display:none if you only want the slider visible. Two-way sync is automatic: dragging a handle updates the number inputs; typing into the inputs moves the handles; clearing resets both. Auto-bounds fires one extra Algolia query on load and uses the real min/max from your data. Static bounds let you set clean round numbers but must be updated as your data grows.
AttributeOnDescription
data-algolia-range-min="attr"Required
<input type="number">Lower bound input of a numeric range filter. The attr must be a number field in Algolia.
data-algolia-range-max="attr"Required
<input type="number">Upper bound input of a numeric range filter.
data-algolia-range-label="Price"
range input(optional) Custom label prefix for the active filter tag (e.g. "Price: Any – 2000"). Add to either min or max input.
data-algolia-range-slider="attr"Required
wrapper divBinds the slider to the same attr as the number inputs.
data-algolia-range-slider-min="0"Required
slider wrapperStatic lower bound of the slider scale. Required unless using auto-bounds.
data-algolia-range-slider-max="100000"Required
slider wrapperStatic upper bound. Required unless using auto-bounds.
data-algolia-range-slider-auto-bounds
slider wrapper(optional) Fetch the real min/max from Algolia facet stats on init. Replaces -slider-min/-slider-max.
data-algolia-range-slider-step="100"
slider wrapper(optional) Snap increment. Default 1.
data-algolia-range-slider-format
slider wrapper(optional) Format display spans using browser locale. Add a BCP 47 tag to force a locale (e.g. "fr-FR").
data-algolia-range-slider-trackRequired
child of slider wrapperThe track bar element.
data-algolia-range-slider-fill
child of track(optional) The highlighted fill between the two handles.
data-algolia-range-slider-handle="min"Required
child of trackThe lower drag handle.
data-algolia-range-slider-handle="max"Required
child of trackThe upper drag handle.
data-algolia-range-slider-display="min"
anyLive text element showing the current lower value.
data-algolia-range-slider-display="max"
anyLive text element showing the current upper value.

Code examples

html
<div class="filter_block">
  <!-- Number inputs the library reads. Keep visible for typing, or hide with display:none -->
  <input type="number" data-algolia-range-min="price-number-2" data-algolia-range-label="Price" placeholder="min">
  <input type="number" data-algolia-range-max="price-number-2" placeholder="max">

  <div class="rangeslider_wrapper" data-algolia-range-slider="price-number-2" data-algolia-range-slider-min="0" data-algolia-range-slider-max="100000" data-algolia-range-slider-step="100" data-algolia-range-slider-format="en-US">

    <div class="rangeslider_track" data-algolia-range-slider-track>
      <div class="rangeslider_handle" data-algolia-range-slider-handle="min"></div>
      <div class="rangeslider_handle" data-algolia-range-slider-handle="max"></div>
      <div class="rangeslider_fill" data-algolia-range-slider-fill></div>
    </div>

    <div class="range_values">
      <div>$<span data-algolia-range-slider-display="min">0</span></div>
      <div>$<span data-algolia-range-slider-display="max">100,000</span></div>
    </div>
  </div>
</div>

Active Filter Styling

AttributeOnDescription
[data-algolia-filter][data-active]
CSS selectorWhen a filter is selected, the library adds data-active="" to the label. Target this in Webflow's custom CSS to style the active state.
w--redirected-checked
CSS classFor Webflow's native checkbox/radio components, the library also toggles this class for visual styling compatibility.
css
/* Style active filter labels */
[data-algolia-filter][data-active] {
  background: var(--blue-pale);
  border-color: var(--blue);
  color: var(--blue);
}

/* Style active page in numbered pagination */
[data-algolia-page-item][data-active] {
  background: #2B4EF0;
  color: white;
}
Advanced

Nested List

Render array / multi-reference fields as repeated child elements inside a result card.

Nested List (Repeat)

AttributeOnDescription
data-algolia-repeat="field"
containerRenders one child element per value in an array field (multi-reference, Option multi-select, etc.).
data-algolia-repeat-item
child (inside repeat container)Template element cloned for each array value. Its text content is set to each value in turn.

Common patterns

Practical recipes for the most common implementation tasks.

The Webflow API returns field slugs (lowercase, hyphenated), not display names. Use these slugs in data-algolia-bind, data-algolia-filter, etc.

Webflow Designer (display name)API slug (Algolia field)
Product Namename
Car Makecar-make
Featured?featured

The sync resolves single Reference fields to the referenced item's name automatically. It also stores all sub-fields as field__subfield. Multi-references become arrays.

html
<!-- Brand logo from the referenced "Car Brand" collection -->
<img data-algolia-bind="car-brand__logo.url" data-algolia-attr="src">

<!-- Brand name string -->
<p data-algolia-bind="car-brand"></p>

Option fields are stored as the option's name (e.g. "White"), not its internal ID. Use the name in your filter values.

html
<label data-algolia-filter="color-theme" data-algolia-value="White">
  <input type="checkbox"><span>White</span>
</label>

Webflow "Switch" fields are stored as real booleans in Algolia (true / false). Use the string "true" or "false" as the filter value.

html
<label data-algolia-filter="featured" data-algolia-value="true">
  <input type="checkbox">
  <span>Featured only</span>
</label>

Image fields are stored as objects with a url property. Use dot notation to bind the URL to a src attribute.

html
<img data-algolia-bind="image.url" data-algolia-attr="src">

For arrays / multi-reference fields where each value should render as a separate element, use data-algolia-repeat. Use data-algolia-hide-empty to hide the wrapper when the field is empty.

html
<div data-algolia-hide-empty="authors" data-algolia-repeat="authors">
  <span data-algolia-repeat-item class="author-tag"></span>
</div>

data-algolia-hide-empty hides the element when the bound field is empty, null, or an empty array — useful for optional fields in cards.

html
<div data-algolia-hide-empty="authors" data-algolia-repeat="authors">
  <span data-algolia-repeat-item></span>
</div>

Use data-algolia-filter-all for a radio "All" option (auto-activates when nothing is selected). Or add checked to a specific input to set a default. URL state always takes priority.

html
<!-- Default to "All" -->
<label data-algolia-filter-all="car-brand">
  <input type="radio" name="brand">
  <span>All brands</span>
</label>

<!-- Default to a specific value -->
<label data-algolia-filter="car-brand" data-algolia-value="Quasar">
  <input type="radio" name="brand" checked>
  <span>Quasar</span>
</label>

Add data-algolia-url-state to the wrapper to sync filters, search, and page number to URL params. Users can copy the URL to share their filtered view.

html
<div data-algolia data-algolia-app-id="YOUR_APP_ID" data-algolia-api-key="YOUR_KEY" data-algolia-index="cars" data-algolia-url-state
>
  <!-- filter elements -->
</div>

Add data-algolia-scroll-anchor to any element above the results. The page scrolls to it on every filter, search, sort, or pagination change.

html
<div data-algolia-scroll-anchor></div>

<div data-algolia-list>
  <!-- results -->
</div>

Each project only needs its own Algolia index, Webflow Cloud app, and Cloudflare Worker with isolated env vars. Fork only if you need to modify the sync logic or pin a different version per project.

bash
# Per-project resources — always create fresh:
# - A dedicated Algolia index
# - A dedicated Webflow Cloud app (with its own env vars)
# - A dedicated Cloudflare Worker (with its own env vars)
# - Webflow webhooks pointing at that project's Cloudflare Worker URL

# Shared across projects (no duplication needed):
# - The client <script> tag (same jsDelivr URL)
# - The webhook worker code (apps/webhook-worker/src/index.js)
# - The sync app code (apps/sync)

Built-in Inspector

The library ships with an Inspector that audits your page for configuration mistakes. It only loads on staging (*.webflow.io) and local development hosts, and only when you explicitly opt in.

Activate it

Append ?algolia-debug to your staging URL:

url
https://your-site.webflow.io/cars?algolia-debug

You will see a floating Algolia Inspector badge in the bottom-right corner. A red or yellow dot indicates issues; green means clean. All elements with data-algolia* attributes get a cyan outline and tooltip. Click the badge to open the diagnostic panel.

Controls

ActionResult
?algolia-debug in URLLoads the Inspector for this page
Remove the param (or refresh without it)Inspector unloads
Click the floating badgeOpens / closes the diagnostic panel
Toggle "Outline" checkbox in the panelShows / hides the cyan outlines + tooltips
Shift + ? keyboard shortcutSame as toggling outline
Click any issue in the panelSmoothly scrolls to the offending element and pulses an outline around it

What it checks

Wrapper

[data-algolia] exists; required data-algolia-app-id, data-algolia-api-key, data-algolia-index present; data-algolia-match-mode is "and" or "or"

Templates

[data-algolia-list] and [data-algolia-template] both exist; template contains at least one data-algolia-bind; data-algolia-attr always paired with data-algolia-bind; data-algolia-bind / -hide-empty not left with empty values; data-algolia-repeat-item lives inside a data-algolia-repeat

Filters

data-algolia-filter paired with data-algolia-value; orphan data-algolia-value flagged; radio groups consistent — if any radio is wired up, every radio in that name group must be too; radios in the same filter share a name; non-empty values for -filter-select / -filter-all

Range

Every data-algolia-range-min="attr" has a matching data-algolia-range-max="attr" (and vice versa)

Range Slider

Has either static min/max OR auto-bounds; track + both handles present; matching number inputs in the wrapper

Pagination

Load More not combined with numbered Pages; [data-algolia-pages] has a button template; page templates live inside [data-algolia-pages]

Tags

[data-algolia-tags] has a [data-algolia-tag-template] child; tag children (-tag-label, -tag-remove) live inside the template

🔒

The Inspector never runs in production. Even with ?algolia-debug on a custom-domain site, nothing happens. Safe to ship.

Runbook

Common maintenance tasks after your initial deployment.

After adding new fields, new collections, or modifying references in Webflow, run a full sync. Publishing the site also triggers this automatically via the site_publish webhook.

bash
curl -X POST https://YOUR_SITE.webflow.io/api/sync \
  -H "Authorization: Bearer YOUR_SYNC_SECRET"

In Algolia → your index → Manage index → Clear index → type CLEAR. Then re-run the sync.

bash
# After clearing in the Algolia dashboard, re-sync:
curl -X POST https://YOUR_SITE.webflow.io/api/sync \
  -H "Authorization: Bearer YOUR_SYNC_SECRET"

When a new version is released, update the version tag in the script URL. Then hard refresh (Cmd/Ctrl+Shift+R) to bypass the browser cache.

html
<script src="https://cdn.jsdelivr.net/gh/felixeallan/algolia-webflow-filter@v0.8.2/packages/library/dist/algolia-webflow.min.js"></script>

Fetch a single record from your Algolia index to verify field names and values after a sync.

bash
curl -s "https://YOUR_APP_ID-dsn.algolia.net/1/indexes/YOUR_INDEX?hitsPerPage=1" \
  -H "X-Algolia-Application-Id: YOUR_APP_ID" \
  -H "X-Algolia-API-Key: YOUR_SEARCH_KEY"

Use the PUT endpoint to return the raw Webflow collection schema — useful for debugging field types and resolving reference issues.

bash
curl -X PUT https://YOUR_SITE.webflow.io/api/sync \
  -H "Authorization: Bearer YOUR_SYNC_SECRET"

Common issues

SymptomCauseFix
Page shows wrong filter values, only ~100 itemsOld version of @main cached by jsDelivrAlways pin to a @v0.x.x tag, not @main
New library changes not appearingBrowser cacheHard refresh (Cmd/Ctrl+Shift+R)
Webhook URL rejected: "Invalid hostname"Webflow blocks *.webflow.io webhooksUse the Cloudflare Worker URL instead
Items from other collections appear in AlgoliaWebhook fires for all collectionsSet WEBFLOW_COLLECTION_ID in the Cloudflare Worker
Reference field shows an ID instead of nameOld sync, or new reference fieldRe-run sync (or publish the site)
Webflow Cloud deploy fails (Cannot find package esbuild)Webflow Cloud installs with --omit=devAll build-time deps must be regular dependencies (already configured in the template)
Webflow Cloud deploy succeeds but routes 500Next.js 16.2+ Turbopack output crashes on WorkersAlready pinned to ~16.1 with next build --webpack

Ready to build?

Clone the Webflow template, follow the 11 steps, and have Algolia-powered filtering live on your Webflow site today.