starti.app
SDK Reference

Gestures

Handle swipe gestures in your own page, so you can run your own back transition instead of the app's built-in swipe navigation.

Access: startiapp.Gesture

The app has its own swipe navigation — left-to-right for back, right-to-left for forward, both on by default. It runs the equivalent of history.back() and history.forward(), which in a single-page app is a full document navigation: the page jumps, or reloads, and whatever the user was looking at is gone.

startiapp.Gesture tells your page about the swipe instead, so you can run your own transition.

await startiapp.initialize({ allowSwipeNavigation: false });

startiapp.Gesture.addEventListener("swipe-back", () => {
  myRouter.back();
});

Listening for a gesture does not disable the app's swipe navigation, and the app's version covers both directions. Turn it off yourself, or one swipe gives the user your transition and a document navigation underneath it. See Turning the app's gesture off.

Events

swipe-back

The user swiped left-to-right, the direction both iOS and Android reserve for going back.

Event data: GestureDetail

startiapp.Gesture.addEventListener("swipe-back", (event) => {
  myRouter.back();
});

swipe-forward

The user swiped right-to-left. The mirror of swipe-back, carrying the same GestureDetail.

startiapp.Gesture.addEventListener("swipe-forward", () => {
  wizard.next();
});

Types

GestureDetail

interface GestureDetail {
  distance: number;
  duration: number;
  velocity: number;
  startX: number;
  startY: number;
  target: EventTarget | null;
}
PropertyTypeDescription
distancenumberHow far the finger travelled horizontally, in CSS pixels. Always positive — the event name carries the direction.
durationnumberMilliseconds between the finger going down and coming up.
velocitynumberSpeed in CSS pixels per millisecond, or 0 if the whole gesture landed in the same millisecond.
startXnumberWhere the finger went down, in viewport coordinates.
startYnumberWhere the finger went down, in viewport coordinates.
targetEventTarget | nullThe element the finger went down on.

Usage Patterns

Turning the app's gesture off

For a site that routes with history.pushState, the app's swipe navigation is wrong everywhere, so turn it off once for the whole app.

Prefer the Manager for this. allowSwipeNavigation is one of the initialization settings you can set for your brand under Development setup → Initialization settings, which keeps it out of your code. Pass it to initialize() only if the value has to be decided at runtime.

await startiapp.initialize({ allowSwipeNavigation: false });

If it is wrong only on one screen — a carousel, a map, a signature pad — push it for that screen and pop it again on the way out. See App.pushOptions.

startiapp.App.pushOptions({ allowSwipeNavigation: false });

// Leaving the screen again.
startiapp.App.popOptions();

Only from the screen edge

iOS starts a back gesture only near the edge. startX is what lets you match that, and it is also the cheapest way to stop a swipe meant for something else on the page from navigating.

startiapp.Gesture.addEventListener("swipe-back", (event) => {
  if (event.detail.startX > 40) return;

  myRouter.back();
});

Ignoring a swipe that belongs to something else

startiapp.Gesture.addEventListener("swipe-back", (event) => {
  const target = event.detail.target as Element | null;
  if (target?.closest(".carousel")) return;

  myRouter.back();
});

Removing the listener with a screen

The recognizer is started when the first listener for a gesture arrives and stopped when the last one goes, so a page that never mentions gestures carries no touch listeners for them. Remove your listener when the screen it belongs to goes away, and an AbortSignal is the tidiest way — one controller tears down every listener the screen added.

const screen = new AbortController();

startiapp.Gesture.addEventListener("swipe-back", () => myRouter.back(), {
  signal: screen.signal,
});

// When the screen is torn down — this also stops the recognizer.
screen.abort();

Platform notes

A touch becomes a swipe only if all of these hold:

  • it travelled at least 60 px horizontally,
  • it travelled at least twice as far horizontally as it did vertically — so scrolling the page and drifting sideways never counts,
  • it took no longer than 800 ms — a slow drag is not a flick,
  • it was a single finger the whole way. A second finger landing cancels it, and so does the browser taking the touch over (a pinch-zoom, a native scroll).

The direction is geometric, not reading-order: left-to-right is always swipe-back, in a right-to-left layout too.

Two limits are worth knowing before you design around these events:

  • The gesture is known when the finger lifts, not while it moves. There is deliberately no per-frame work here, which is what makes the recognizer nearly free — but it means you cannot drive a transition that follows the finger from these events. Track touches yourself for that.
  • Touch only. A trackpad or mouse drag in a desktop browser fires nothing. Use a phone, or a browser's device-emulation mode, to try it.

See also

On this page