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

react-router-dom-animate

v1.2.2

Published

Route transitions and KeepAlive caching for React Router

Readme

react-router-dom-animate

react-router-dom v7 提供路由转场与页面缓存,基于 React 19.2 <Activity>

npm install react-router-dom-animate

要求:React 19.2+、React Router 7、createBrowserRouter + <RouterProvider>(Data Router)。仅浏览器端,不支持 SSR — Next.js / Remix 请将相关组件标记 'use client'


在线示例

  • StackBlitz(仅加载 demo/stackblitz 示例目录):Open Demo
  • 示例使用 npm 包 react-router-dom-animate,不会引用仓库内 src 源码。
  • 可直接体验:全局转场切换、列表→详情(stack)、KeepAlive Tabs 状态保留、页面激活/离开日志。

快速上手

把根 Layout 的 <Outlet /> 换成 <AnimatedOutlet />,默认即有 iOS 风格 cover 转场:

import { AnimatedOutlet } from 'react-router-dom-animate'

export function RootLayout() {
  return <AnimatedOutlet />
}

指定动画:路由 handle 或跳转时传 state 均可,navigate(-1) 自动反向:

{ path: 'detail/:id', handle: { transition: 'slide' }, element: <DetailPage /> }
navigate('/detail/1', { state: { transition: 'modal' } })

| 动画 | 效果 | |------|------| | cover | 右滑覆盖(默认) | | slide | 同向对滑 | | fade / scale / modal / none | 淡入淡出 / 缩放 / 底部弹出 / 无动画 |


常见场景

列表 → 详情(保留状态 + 滚动位置)

import { AnimatedOutlet, KeepAlive } from 'react-router-dom-animate'

<KeepAlive>
  <AnimatedOutlet transition="cover" />
</KeepAlive>

PUSH 时背景页留在 DOM,POP 时完整恢复。stack 模式按返回栈管理,不需要 max

底部 Tab(切换保留状态)

Tab 栏与 <AnimatedOutlet /> 同级,不参与转场:

export function TabsLayout() {
  return (
    <>
      <KeepAlive mode="switch">
        <AnimatedOutlet transition="slide" />
      </KeepAlive>
      <nav>{/* Tab 按钮 */}</nav>
    </>
  )
}

路由加 tabIndex 可让 slide / cover 有方向感(小 → 大 = 向右):

{ path: 'home', handle: { tabIndex: 0 }, element: <HomeTab /> }

| 需求 | 写法 | |------|------| | 无动画即时切换 | <AnimatedOutlet mode="switch" /> | | 有动画但不缓存 | <AnimatedOutlet mode="switch" transition="slide" /> | | 有动画 + 保留状态 | <KeepAlive mode="switch"><AnimatedOutlet transition="slide" /></KeepAlive> |

根 Layout 统一缓存策略

根层 stack 保活 + 菜单层 switch 即时切换,是移动端 App 的推荐组合:

// 根 Layout — 一次声明
<KeepAlive include={['HomeTab', 'DiscoverTab', 'ProfileTab']}>
  <AnimatedOutlet />
</KeepAlive>

// 路由 handle — 给需要缓存的页面命名
export const handle = { keepAliveName: 'HomeTab', tabIndex: 0 }

// Tab Layout — 只覆盖导航模式,不继承根层 cover 动画
<AnimatedOutlet mode="switch" />

也可在路由 handle 中启用栈模式缓存,无需 <KeepAlive> 包裹:handle: { keepAlive: true }


API 速查

<AnimatedOutlet>

| Prop | 默认 | 说明 | |------|------|------| | transition | 'cover' | 内置或自定义预设名 | | mode | 'stack' | stack 有方向感;switch 平级切换。在 <KeepAlive> 内继承其 mode | | className | — | 外层容器 class | | onTransitionStart | - | 转场开始时触发(含即时切换)。可用于显示 loading、埋点 | | onTransitionEnd | - | 转场完成时触发(settled location 已提交)。与 onTransitionStart 配对 |

<KeepAlive>

| Prop | 默认 | 说明 | |------|------|------| | mode | 'stack' | stack 列表→详情;switch Tab 缓存 | | max | 30(switch) / 10(stack) | switch: LRU 上限;stack: 返回栈深度上限,超出时从底部驱逐最旧页面 | | include / exclude | — | 白/黑名单,匹配 pathname 或 keepAliveName(仅 switch) | | aliveRef | - | 命令式控制缓存(两种模式均支持) |

aliveRef 方法(stack 和 switch 通用):

aliveRef.current?.remove('/list')    // 移除指定 pathname 的缓存页面
aliveRef.current?.removeAll()        // 移除所有非活跃缓存页面
aliveRef.current?.getCached()        // 获取当前缓存的 pathname 列表

运行时切换 mode 会清空所有缓存。include / exclude / max 内部用 ref 存储,内联函数不会触发 Context 重建。

生命周期

keepAlive 模式下组件不 remount,用 hook 感知页面显隐:

useActivated(() => fetchData())    // 页面变为活跃(含首次 mount)
useDeactivated(() => cleanup())    // 页面隐藏或卸载

<Activity>display:none 隐藏页面,<video>/<audio> 会暂停。


自定义动画

CSS 变量(推荐):--fr-duration(全局)、--fr-duration-cover 等(按类型)。

JSsetAnimDuration('modal', 450) — 优先级高于 CSS。

注册预设

import { registerAnimPreset, unregisterAnimPreset } from 'react-router-dom-animate'

registerAnimPreset({ type: 'my-flip', forward: { ... }, back: { ... }, durationMs: 600 })
<AnimatedOutlet transition="my-flip" />

unregisterAnimPreset('my-flip')  // HMR 场景移除

系统启用"减少动态效果"时自动 0ms 转场。

CSS 变量速查

| 变量 | 默认 | 说明 | |------|------|------| | --fr-duration | 300ms | 全局动画时长 | | --fr-duration-{type} | - | 按类型覆盖,如 --fr-duration-modal: 450ms | | --fr-ease | cubic-bezier(.25,.46,.45,.94) | 默认缓动 | | --fr-ease-spring | cubic-bezier(.32,.72,0,1) | 弹性缓动(modal/slide-up) | | --fr-ease-tab | cubic-bezier(.4,0,.2,1) | Tab 切换缓动 | | --fr-page-bg | #f9fafb | 页面背景色(浅色) | | --fr-page-bg-dark | #030712 | 页面背景色(深色) | | --fr-modal-overlay | rgba(15,23,42,.55) | modal 遮罩色 | | --fr-pending-bg | rgba(255,255,255,.4) | loader loading 时的 overlay 色 |

路由 Loading 状态

当 React Router 7 的路由 loader 正在加载时,容器自动添加 data-pending 属性,CSS 显示半透明 overlay 防止白屏闪烁:

/* 自定义 overlay 效果 */
.animated-outlet-group[data-pending]::after {
  background: url('/spinner.svg') center / 24px no-repeat;
}

也可通过 --fr-pending-bg 变量只改颜色:

:root { --fr-pending-bg: rgba(0, 0, 0, 0.3); }

动画事件回调

<AnimatedOutlet
  onTransitionStart={() => NProgress.start()}
  onTransitionEnd={() => NProgress.done()}
/>

即时切换(duration=0prefers-reduced-motion)也会触发回调,startend 之间可能无延迟。


Demo

npm run demo   # http://localhost:5180

源码结构(贡献者参考)

src/
├── index.ts          # 公开 API 入口(导出 + 注入 CSS + warmDurationMs)
├── types.ts          # 所有 TypeScript 类型定义
├── transition.ts     # 动画预设注册、planTransition、CSS 时长读取
├── anim.css          # 内置动画样式 + CSS 变量
├── context.tsx       # React Context 定义(LocCtxValue、DepthContext、KeepAliveContext 等)
├── common.tsx        # 共享工具组件(PageScope、FrozenOutlet、PageTransition、snap 等)
├── hooks.ts          # 生命周期钩子(useActivated、useDeactivated)
├── stack-root.tsx    # BackgroundPreserveRoot(stack 模式 + iOS 滚动恢复)
├── switch-root.tsx   # KeepAliveRoot(switch 模式 LRU 缓存)
├── animated-root.tsx # AnimatedRoot + LayoutScopeRegistrar(无缓存模式)
└── outlet.tsx        # 入口层:KeepAlive / AnimatedOutlet 组件

AnimatedOutlet 内部分发逻辑:

AnimatedOutlet
├── children + transition ──→ PageScope(注册页面级动画覆盖)
├── keepAlive=true
│   ├── mode=switch ────────→ KeepAliveRoot(LRU tab 缓存,Activity show/hide)
│   └── mode=stack  ────────→ BackgroundPreserveRoot(返回栈,PUSH/POP)
└── 默认 ───────────────────→ AnimatedRoot(TransitionGroup,无缓存)

完整变更记录见 CHANGELOG.md。MIT