> For the complete documentation index, see [llms.txt](https://whoisandywhite.gitbook.io/porterwp/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://whoisandywhite.gitbook.io/porterwp/documentation/ajax-filters.md).

# Ajax Filters

The **Filters** block that ships with **PorterWP Premium** can work in two modes:

| Mode                 | How it works                                                                                                                               |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| **Static** (default) | The page reloads and the next `Query Loop` with class `.use-filters` receives the updated query args.                                      |
| **AJAX**             | A JavaScript helper calls `Porter_Filters_Ajax_Handler` so only the results list is repainted, giving users a faster, app‑like experience. |

Below is a step‑by‑step guide to bolt the AJAX version onto any archive or landing page.

***

## 1 – Create an output template

Add a PHP file inside **`porter/inc/templates`**.\
The file name **must** follow the pattern `filter-template-{slug}.php` – the slug is what you will reference in the block settings.

```php
<?php
/**
 * Filter template: {slug}
 *
 * Receives:
 *  - $filters (array)         Current filter values keyed by parameter
 *  - $query   (WP_Query)      The query that will be (re)run
 *  - $attrs   (array)         Block attributes
 *
 * Return a string of HTML.
 */

defined( 'ABSPATH' ) || exit;

// Example: build a list of taxonomy terms as checkboxes.
ob_start();

$terms = get_terms( [
    'taxonomy'   => 'sector',
    'hide_empty' => false,
] );
?>
<form class="porter-filters" data-filters-form>
    <?php foreach ( $terms as $term ) : ?>
        <label>
            <input type="checkbox"
                   name="sector[]"
                   value="<?php echo esc_attr( $term->slug ); ?>"
                   <?php checked( in_array( $term->slug, $filters['sector'] ?? [], true ) ); ?> />
            <?php echo esc_html( $term->name ); ?>
        </label>
    <?php endforeach; ?>

    <button type="submit">Apply</button>
</form>
<?php

return ob_get_clean();
```

*🔍 Tip:* Keep *all* markup self‑contained – no wrapper divs are injected by the handler.

***

## 2 – Add the JavaScript

Create a file inside **`assets/src/js`** (or wherever you keep custom scripts) – e.g. `filters-{slug}.js`.

```js
/**
 * Minimal AJAX driver for PorterWP Filters
 * Assumes there is ONE Filters block per page.
 */
document.addEventListener('DOMContentLoaded', () => {
  const form = document.querySelector('[data-filters-form]');
  if (!form) return;

  form.addEventListener('submit', async (e) => {
    e.preventDefault();

    const params = new URLSearchParams(new FormData(form));
    // Tell Porter which template to use
    params.append('template', '{slug}');

    const res = await fetch(window.porterFiltersAjax || '/wp-json/porterwp/v1/filter', {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
      body: params.toString(),
    });

    if (!res.ok) return console.error('Filters AJAX failed');

    const { html } = await res.json();           // HTML string for the Query Loop
    const target = document.querySelector('.use-filters');
    if (target) target.innerHTML = html;
  });
});
```

*Compile & watch* – the existing Gulp task `compileJS` will copy the minified file into `assets/dist/js/`.

***

## 3 – Reference the template inside the block

1. Insert a **Filters** block in the editor.
2. In the **Block › Inspector Panel › Template tail** field, enter just the slug:

   ```
   {slug}
   ```

   PorterWP concatenates this with the `filter-template-` prefix and `.php` suffix to locate the file you created in step 1.
3. Add the **Query Loop** block you want to reload *immediately* after the Filters block and give it the class **`use-filters`**.

***

## 4 – Enqueue the script

The simplest way is via your theme’s `functions.php`:

```php
add_action( 'wp_enqueue_scripts', function () {
    if ( is_archive() || is_post_type_archive( 'my_cpt' ) ) {
        wp_enqueue_script(
            'filters-{slug}',
            get_theme_file_uri( 'assets/dist/js/filters-{slug}.js' ),
            [ 'wp-element' ],
            null,
            true
        );
    }
} );
```

When the page loads, your script intercepts form submits and talks to the REST endpoint provided by **`Porter_Filters_Ajax_Handler`**.

***

## 5 – Caching & CLS considerations

* The HTML fragment returned by the REST endpoint is auto‑escaped and cacheable (if you’re on `production` and object‑cache is set up).
* To avoid layout shift, add a min‑height to the `.use-filters` container in CSS.

***

## Summary

| Piece                            | Location                                         | Responsibility                                |
| -------------------------------- | ------------------------------------------------ | --------------------------------------------- |
| **`filter-template-{slug}.php`** | `porter/inc/templates/`                          | Renders the filter UI; returns string.        |
| **`filters-{slug}.js`**          | `assets/src/js/` → compiled to `assets/dist/js/` | Handles submit, calls REST, injects HTML.     |
| **Template tail**                | ACF field inside Filters block                   | Tells PorterWP which template file to load.   |
| **`.use-filters` Query Loop**    | Editor                                           | Container that receives the refreshed markup. |

That’s all – you now have lightning‑fast, infinitely chainable faceted search 🎉


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://whoisandywhite.gitbook.io/porterwp/documentation/ajax-filters.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
