---
title: SwipeController
description: 'Reports a left, right, up or down swipe on release.'
order: 11
---

import { PrimitiveExample } from '@/components/primitive-example';

export const config = {
	dependencies: {
		components: [PrimitiveExample],
		scripts: ['./swipe.script.ts'],
	},
};

# SwipeController

<p class="docs-lede">Reports a left, right, up or down swipe on release.</p>

Reach for it for a carousel or pager.

## Import

```ts
import { SwipeController, createSwipeController } from '@ecopages/radiant-ui/controllers';
```

## Try it

Press on the box and either move at least 48 px sideways or flick it at least 16 px faster than 0.5 px per ms, then release. A drag that is not at least twice as far sideways as it is up or down is ignored. The callback reports the direction.

<PrimitiveExample name="swipe">
	<div data-box>Swipe me</div>
	<p data-status>No swipe yet</p>
</PrimitiveExample>

```html
<div data-example="swipe">
	<div data-box>Swipe me</div>
	<p data-status>No swipe yet</p>
</div>
```

```ts
import { SwipeController } from '@ecopages/radiant-ui/controllers';

export function mount(root: HTMLElement): () => void {
	const box = root.querySelector<HTMLElement>('[data-box]')!;
	const status = root.querySelector('[data-status]')!;

	const swipe = new SwipeController({
		getTarget: () => box,
		minVelocity: 0.5,
		dominance: 2,
		onSwipe: (direction) => (status.textContent = `Swiped ${direction}`),
	});
	swipe.attach();
	return () => swipe.destroy();
}
```

`mount` runs once for each example element on the page, and the function it returns undoes it; see [how the examples run](/docs/controllers/overview#how-the-examples-run).

## Config

| Option      | Description                                                                                                                                               |
| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `getTarget` | Resolved on every `attach()`. `null` listens to nothing.                                                                                                  |
| `axis`      | Which travel counts: `x` (default), `y` or `both`.                                                                                                        |
| `threshold` | Minimum travel in px along the swipe axis. Default 48.                                                                                                    |
| `minVelocity` | Speed in px per ms along the axis that lets a shorter travel count, a flick. The travel must still reach 16 px (`MIN_FLICK_TRAVEL_PX`). Default: none, only `threshold` decides. |
| `dominance` | How many times larger the travel along the axis must be than across it. Default 1, which is simply larger.                                                |
| `getHandle` | Limits gestures to this element's subtree, resolved on every press; use it with the host as `getTarget` when the surface is a child that can be replaced. |
| `exclude`   | A gesture that starts on a target this returns `true` for is ignored, such as a button inside the surface.                                                |
| `onSwipe`   | Called on the pointer-up that ends a swipe, with the direction and the event.                                                                             |

## Lifecycle

`attach()` listens for a press on the target; release and cancel are then listened for on `document`, so a release outside the element still ends the gesture. It reports once, on release, and does not follow the pointer; a surface that moves with the pointer needs [PointerDragController](/docs/controllers/pointer-drag). A cancel or `destroy()` drops a gesture in progress, and `destroy()` removes the listeners.

With a host, `createSwipeController(host, config)` does the same on connect and disconnect; see [bindController](/docs/controllers/bind-controller).

## Protected hooks

Extend the class and override these to change how it decides. A component never extends a controller; a subclass is registered with `bindController`.

| Hook                       | Description                                                                                                                                                                                                             |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `resolveDirection(dx, dy, elapsedMs)` | Decides which swipe, if any, a gesture was. Runs on the pointer-up with the travel from the press point and the milliseconds since the press, taken from the `timeStamp` of the two pointer events. Return `null` for no swipe. The default is `resolveSwipeDirection` with the configured `axis`, `threshold`, `minVelocity` and `dominance`. |
