# Make your theme or plugin AjaxPress-friendly

> A checklist for theme and plugin authors: what already works, the few patterns that break, and how to test.

Source: https://arraystory.com/docs/ajaxpress/make-your-theme-or-plugin-ajaxpress-friendly/

Good news first: code written for ordinary WordPress pages already works with AjaxPress. Each page loads as a complete document, inside a frame, with all of its scripts. `DOMContentLoaded`, `load` and jQuery's `ready` fire on every page. There's nothing to re-initialise.

This checklist covers the few patterns that do need care. For how the frame and the top window fit together, read [Where your code runs](https://arraystory.com/docs/ajaxpress/javascript-events-and-markup-reference/#where-your-code-runs) first.

## Don't break out of the frame

AjaxPress shows your pages inside a frame on the same site. These all force a full page load, or a blank page:

- **Frame-busting scripts**, such as `if (top !== self) top.location = self.location;`. Remove them, or check the origin: `window.top.location.origin !== location.origin`.
- **`X-Frame-Options: DENY`** or **`frame-ancestors 'none'`** headers. Use `SAMEORIGIN` and `'self'`. See [Security plugins and headers](https://arraystory.com/docs/ajaxpress/security-plugins-and-headers/).
- **`target="_top"`** on ordinary links and forms. Only use it where you really want a full page load.
- **Navigating with `window.top.location`** instead of `window.location`. For an ordinary redirect, set `window.location.href`; AjaxPress picks the new page up.

## Keep actions off plain GET links

[Prefetch](https://arraystory.com/docs/ajaxpress/prefetch/) loads a page when its link is hovered. A link that changes something when loaded (log out, add to cart, delete, unsubscribe, mark as read) can run before the visitor clicks.

- Use a **form with `method="post"`**, or a **button** handled by your script, for actions.
- If it has to be a link, add a confirmation step, or give site owners the pattern to add to **Exclude Links**. From your own code, add it with the `bypassPatterns` filter:

```js
(window.ajaxpressModules = window.ajaxpressModules || []).push((api) => {
  api.addFilter('bypassPatterns', (patterns) => [...patterns, 'action=my_plugin_logout']);
});
```

## Handle your own form submissions properly

If your script submits a form over ajax, call `event.preventDefault()` in its `submit` handler. AjaxPress checks `defaultPrevented`, sees the form is taken care of, and stays out, so the form isn't submitted twice. AjaxPress's own listener runs after page-level handlers, including jQuery delegated ones.

If your handler is attached late (after the visitor could already submit), or doesn't call `preventDefault()`, put `data-ajaxpress-ignore-submit` on the form. See [Forms](https://arraystory.com/docs/ajaxpress/forms/).

## Persistent elements

If your plugin shows a player (or anything else that should survive page changes):

- Print it on **every page**, from a site-wide hook such as `wp_footer`, not inside post content.
- Mark the **outer wrapper**: `data-ajaxpress-persist`.
- Give it a fixed identity: `data-persist-key="my-plugin-player"`. Never put per-page values (timestamps, cache busters) in its `id` or its media `src`.
- **Position it yourself** with `position: fixed`. Once lifted, it leaves its parent, and parent-scoped CSS stops applying.

See [Keep audio or video playing across pages](https://arraystory.com/docs/ajaxpress/keep-audio-or-video-playing-across-pages/).

## Code that must follow the visitor across pages

Almost nothing needs this. If yours does (a player's state, a chat session), run it once, in the top window:

```js
if (window.self === window.top) {
  document.addEventListener('ajaxpress:ready', (e) => {
    // e.detail.url is the page now on screen.
  });
}
```

Don't add listeners to `window.top.document` from inside the page: every page would add another. See the [events reference](https://arraystory.com/docs/ajaxpress/javascript-events-and-markup-reference/#events).

## Analytics

Your page view code runs on every page, so page views are already counted. Don't send extra page views from `ajaxpress:ready` or the `ajaxpress_page_view` dataLayer event, or every page is counted twice.

## Detect AjaxPress

```js
// Is AjaxPress active on this page?
const hasAjaxPress = typeof window.AjaxPress !== 'undefined';

// Is this page shown inside AjaxPress's frame?
const inAjaxPressFrame = window.self !== window.top &&
  !!window.top.document.getElementById('ajaxpress-persist');
```

## Test checklist

1. Click through five or more pages, including your plugin's pages.
2. Submit each of your forms once. Check for exactly one submission.
3. Press Back and Forward a few times.
4. With AjaxPress Pro and prefetch on, hover over your plugin's links. Nothing should change until you click.
5. Check the browser console for errors on each page.
6. Test logged in and logged out, and on a phone.

Found something AjaxPress should handle better? Email [support@arraystory.com](mailto:support@arraystory.com). We're happy to work with plugin authors.
