register()

ajax.register(config) is the main API. Call it once per interaction type — each call wires up event listeners and teaches Ajax what to fetch and swap.

import ajax from "@erikt/ajax"

ajax.register({
  target: "#link-about",
  swaps: [{ replace: "#main" }],
})

Ajax observes the DOM, so elements matching target that are added after the call is made are picked up automatically.


AjaxConfig

target

target: string

A CSS selector for the element(s) that trigger requests. Every element in the document matching this selector gets an event listener attached.

ajax.register({ target: "a[data-ajax]", swaps: [...] })
ajax.register({ target: "#nav a", swaps: [...] })

swaps

swaps: TargetConfig[]

An array of swap instructions. Each entry describes one element to update in the current page using content from the fetched page. Multiple swaps run in order — useful for updating a main content area and a sidebar in a single request.

See TargetConfig below.


trigger

trigger?: string | string[]

The DOM event(s) to listen for on matching elements. Defaults to "click" for links and "submit" for forms. Pass a string or an array to listen for multiple events.

// fire on both focus and click
ajax.register({ target: "#btn", trigger: ["mouseenter", "click"], swaps: [...] })

transitions

transitions?: string[]

Type names passed to the View Transition API (document.startViewTransition({ types })). Use these to drive CSS @keyframes tied to ::view-transition-* pseudo-elements.

If the browser does not support startViewTransition, the swap runs without any transition — no polyfill, no error.

ajax.register({
  target: "#link-about",
  transitions: ["slide-left"],
  swaps: [...],
})
@keyframes slide-in {
  from { translate: 100% 0; }
}

::view-transition-new(*):active-view-transition-type(slide-left) {
  animation: slide-in 200ms ease;
}

plugins

plugins?: Plugin[]

Per-registration plugins that run in addition to (or instead of) globally registered plugins. A per-registration plugin with the same key as a global plugin replaces it for this registration only.

import ajax, { loading } from "@erikt/ajax"

ajax.use(loading())  // default: spinner on the triggering element

ajax.register({
  target: "#search-form",
  plugins: [loading("#search-spinner")],  // override for this registration only
  swaps: [{ replace: "#results" }],
})

See the Plugins page for the full plugin interface.


prevent

prevent?: boolean

Whether to call e.preventDefault() on the triggering event. Defaults to true, which is the right behaviour for links and forms (stops the browser from navigating). Set to false only if you need the default browser action to also run.


TargetConfig

Each entry in swaps is a TargetConfig:

replace

replace: string

CSS selector for the element(s) in the current page to update. All matching elements are updated.


with

with?: string | string[]

CSS selector(s) to find the replacement content in the fetched page. The first selector that matches wins. Defaults to the value of replace when omitted.

Pass an array as a fallback chain — useful when the fetched page might not always include the same element:

{ replace: "#sidebar", with: ["#sidebar-full", "#sidebar"] }

match

match?: string

When replace matches multiple elements, match names an attribute used to pair each current element with its counterpart in the fetched page, instead of applying the first found element to all of them. Elements are paired when current.getAttribute(match) === next.getAttribute(match). A current element with no matching counterpart is left untouched.

{ replace: "#list li", match: "id" }

mode

mode?: 'innerHTML' | 'outerHTML' | 'beforebegin' | 'afterbegin' | 'beforeend' | 'afterend'

How content is inserted. Defaults to "innerHTML".

Mode Effect
innerHTML Replace the inner content, keep the wrapper element
outerHTML Replace the element itself
beforebegin Insert before the element
afterbegin Insert as the first child
beforeend Insert as the last child
afterend Insert after the element

if

if?: (current: Element, next: Element) => boolean

A guard function called for each matched element before the swap runs. Return false to skip the swap for that element. Useful for conditional updates — for example, skipping a swap when the new content is identical:

{
  replace: "#cart-count",
  if: (current, next) => current.textContent !== next.textContent,
}