Transitions
Built-in presets, switching them, and writing your own — including swipe gestures
A transition is the animation between screens. flemo ships four presets and lets you build your own with the same primitives.
Built-in presets
cupertino
iOS-style horizontal slide — edge swipe back included, default
material
Slides up from below — drag-down to dismiss with rubber-band
layout
Light fade tuned for layoutId morphs
none
Instant cut — no animation
Set the global default on <Router>.
<Router defaultTransitionName="material">…</Router>Override per navigation.
navigate.push("/photos/:id", { id }, { transitionName: "layout" });Writing your own
Use createTransition for the common shape (six phases), or createRawTransition when you want
full control over every navigate state.
createTransition
import { createTransition } from "flemo";
export const myFade = createTransition({
name: "myFade",
initial: { opacity: 0 },
idle: { value: { opacity: 1 }, options: { duration: 0 } },
enter: { value: { opacity: 1 }, options: { duration: 0.3 } },
enterBack: { value: { opacity: 0 }, options: { duration: 0.3 } },
exit: { value: { opacity: 0 }, options: { duration: 0.3 } },
exitBack: { value: { opacity: 1 }, options: { duration: 0.3 } }
});| Phase | When it plays |
|---|---|
initial | The screen's initial style before any animation |
idle | At rest, when no transition is happening |
enter | The active screen during a push or replace |
exit | The previous screen during a push or replace |
enterBack | The active screen during a pop |
exitBack | The previous screen during a pop |
Register it on the <Router>.
<Router transitions={[myFade]} defaultTransitionName="myFade">
…
</Router>…and add it to the type registry so defaultTransitionName and transitionName autocomplete.
Most projects drop this at the bottom of the file where <Router> is mounted.
declare module "flemo" {
interface RegisterTransition {
myFade: "myFade";
}
}createRawTransition
If you need different animations for push vs replace vs pop on each side, use the raw factory.
createRawTransition({
name: "advanced",
initial: { x: "100%" },
idle: { value: { x: 0 }, options: { duration: 0 } },
pushOnEnter: { value: { x: 0 }, options: { duration: 0.4 } },
pushOnExit: { value: { x: -50 }, options: { duration: 0.4 } },
replaceOnEnter: { value: { x: 0 }, options: { duration: 0.2 } },
replaceOnExit: { value: { x: 0 }, options: { duration: 0.2 } },
popOnEnter: { value: { x: 0 }, options: { duration: 0.3 } },
popOnExit: { value: { x: "100%" }, options: { duration: 0.3 } },
completedOnEnter: { value: { x: 0 }, options: { duration: 0 } },
completedOnExit: { value: { x: -50 }, options: { duration: 0 } }
});Swipe gestures
A transition can be driven by a drag gesture by setting swipeDirection and providing three
handlers in options.
createTransition({
name: "myFade",
// …phases…
options: {
swipeDirection: "x", // "x" or "y"
onSwipeStart: async () => true, // return true to begin the swipe
onSwipe: (event, info, ctx) => {
const progress = Math.max(0, Math.min(100, (info.offset.x / 200) * 100));
ctx.onProgress?.(true, progress);
return progress;
},
onSwipeEnd: async (event, info, ctx) => {
const triggered = info.offset.x > 100;
ctx.onStart?.(triggered);
// animate the screens to their final position
return triggered;
}
}
});The handlers receive currentScreen, prevScreen, an animate(target, value, options)
function, and the dragControls. You're in charge of moving the elements during the gesture.
Decorators
Decorators sit between the previous screen and the current screen. The built-in overlay
decorator is what creates the dim during a cupertino swipe.
import { createDecorator } from "flemo";
const dim = createDecorator({
name: "dim",
initial: { opacity: 0 },
enter: { value: { opacity: 0.4 }, options: { duration: 0.3 } },
exit: { value: { opacity: 0 }, options: { duration: 0.3 } }
});Attach a decorator to a transition by name.
createTransition({
name: "myFade",
// …phases…
options: { decoratorName: "dim", swipeDirection: "x" /* … */ }
});…then register the decorator on <Router>.
<Router decorators={[dim]} transitions={[myFade]}>
…
</Router>For full per-state control, createRawDecorator mirrors createRawTransition.