npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

expo-transition-router

v1.0.1

Published

High-performance, asynchronous page transitions for Expo Router.

Readme

Easily add high-performance, asynchronous page transitions to your Expo Router apps. Built for speed, flexibility, and a premium "native" feel.

Features

  • ✅ Async Transitions: Support for custom leave and enter animations (perfect for Reanimated or Moti).
  • ✅ Expo Router Integration: Seamless wrapper around the standard Expo Router API.
  • ✅ Dynamic Animation Selection: Choose between different animation styles per navigation call.
  • ✅ History-Aware Back Support: Automatically remember which animation was used to enter a screen and replay it on exit.
  • ✅ Hardware Back Button Support: Built-in integration with Android's BackHandler.
  • ✅ Hot Reload Resilience: Smart lifecycle detection ensures animations don't trigger during development reloads.
  • ✅ Zero Faint/Flashes: Prevents common screen unmounting flashes by orchestrating the handoff between screens.

Installation

npm install expo-transition-router
# Ensure you have the required peer dependencies
npx expo install expo-router react-native-reanimated

Quick Start

1. Wrap your Root Layout

In your app/_layout.jsx, wrap your root component with the TransitionProvider.

import { TransitionProvider } from 'expo-transition-router';
import { Stack } from 'expo-router';

export default function Layout() {
  return (
    <TransitionProvider
      leave={(next) => setTimeout(next, 500)} // Orchestrate navigation timing
      enter={(next) => setTimeout(next, 500)}
      defaultAnimation="staggered"
      backBehavior="pop"
    >
      <Stack screenOptions={{ headerShown: false }} />
      {/* Add your overlay component here */}
      <TransitionManager /> 
    </TransitionProvider>
  );
}

2. Use the Custom Router

Instead of useRouter from expo-router, use useTransitionRouter to trigger transitions.

import { useTransitionRouter } from 'expo-transition-router';

export function Home() {
  const router = useTransitionRouter();

  return (
    <Button 
      title="Go to About" 
      onPress={() => router.push('/about', { animation: 'circular' })} 
    />
  );
}

API Reference

TransitionProvider

The core provider that manages transition state.

| Prop | Type | Default | Description | | --- | --- | --- | --- | | leave | TransitionCallback | next => next() | Async function called before navigation. | | enter | TransitionCallback | next => next() | Async function called after navigation. | | defaultAnimation| string | 'staggered' | The fallback animation key. | | backBehavior | 'pop' \| 'default' | 'pop' | pop remembers the enter animation; default always uses the global style. |

useTransitionRouter

A hook that returns an object with push, replace, and back methods, compatible with Expo Router.

const router = useTransitionRouter();
router.push('/target', { animation: 'grid' });

Link

A drop-in replacement for @expo/router's Link.

import { Link } from 'expo-transition-router';

<Link href="/test">Transition Link</Link>

Advanced: History-Aware Transitions

If backBehavior="pop" is enabled (default), the library maintains an internal stack of animations. When router.back() is called, it re-plays the same style that was used to enter the current page.

Creating Custom Animations

You can build your own transition components by listening to the library's internal state. This allows for high-performance, complex overlays using libraries like Reanimated or Moti.

1. Understanding the Transition Lifecycle

The navigation process is split into three distinct phases:

  • leaving: Triggered when a navigation action starts. The library waits for your leave() callback to finish before executing the actual route change.
  • entering: Triggered immediately after the route has changed and the new screen is mounted. The library waits for your enter() callback to finish.
  • none: The steady state when no navigation is occurring.

2. The useTransitionState() Hook

This hook provides the essential state for your transition components:

const { stage, activeAnimation, isBack } = useTransitionState();

| Property | Type | Description | | --- | --- | --- | | stage | 'none' \| 'leaving' \| 'entering' | The current lifecycle phase. | | activeAnimation | string | The animation key requested (e.g., 'circular'). | | isBack | boolean | true if the transition was triggered by a back navigation. |


Step-by-Step Guide: Building a "Circular Reveal"

To create a custom transition that actually works, follow these steps:

Step 1: Configure the TransitionProvider

Ensure your leave and enter callback durations match your animation's speed.

<TransitionProvider
  leave={(next) => setTimeout(next, 600)} // Must match animation duration
  enter={(next) => setTimeout(next, 600)}
>
  <Stack />
  <TransitionManager /> {/* Your custom overlay component */}
</TransitionProvider>

Step 2: Create a Transition Component

Initialize your shared values and use useEffect to trigger animations when the stage changes.

import { useTransitionState } from 'expo-transition-router';
import Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing } from 'react-native-reanimated';

export function CircularReveal() {
  const { stage } = useTransitionState();
  const scale = useSharedValue(0);
  const opacity = useSharedValue(0);

  useEffect(() => {
    const config = { duration: 600, easing: Easing.bezier(0.4, 0, 0.2, 1) };

    if (stage === 'leaving') {
      opacity.value = 1;
      scale.value = withTiming(1, config);
    } else if (stage === 'entering') {
      scale.value = withTiming(0, config, () => {
        opacity.value = 0;
      });
    }
  }, [stage]);

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ scale: scale.value }],
    opacity: opacity.value,
  }));

  if (stage === 'none') return null;

  return (
    <Animated.View 
      pointerEvents="none"
      style={[{ position: 'absolute', inset: 0, zIndex: 9999 }, animatedStyle]} 
    />
  );
}

Step 3: Implement the Transition Manager

Use the activeAnimation property to switch between different animation styles dynamically.

export function TransitionManager() {
  const { stage, activeAnimation } = useTransitionState();

  if (stage === 'none') return null;

  switch (activeAnimation) {
    case 'circular': return <CircularReveal />;
    default: return <DefaultFade />;
  }
}

Flexibility & Opting Out

1. Bypassing Transitions

If you want to skip animations for a specific navigation, you can either:

  • Use the original useRouter from expo-router.
  • Pass animation: 'none' to the useTransitionRouter methods.
router.push('/test', { animation: 'none' }); // Instant navigation

2. Android Back Button

By default, the library intercepts the hardware back button to play a "leave" transition. You can opt-out of this by setting autoHandleAndroidBack={false} on the TransitionProvider.

<TransitionProvider autoHandleAndroidBack={false}>
  {/* Components */}
</TransitionProvider>

License

MIT