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

@arms/rum-browser-nextjs

v0.1.1

Published

Next.js plugin for @arms/rum-browser SDK (App Router / Pages Router view tracking + Next.js error capture with digest)

Readme

@arms/rum-browser-nextjs

Next.js 插件 for @arms/rum-browser RUM SDK —— 同时支持 App Router(Next.js 13+)与 Pages Router。

Overview

本包为 Next.js 应用提供框架级可观测适配:自动检测 App Router 与 Pages Router 路由变化、将动态路由收敛为模板化 view 名称、采集 Next.js 错误边界异常并携带 digest 关联键。结合 @arms/rum-browser 基座能力,可追踪 Core Web Vitals、资源加载、用户操作与会话回放,定位性能瓶颈与用户旅程中的问题。

能力范围:

  • 路由视图追踪(view.name 模板收敛):开启手动 view 模式后,view 的创建、命名与计时完全由路由变化驱动(内部调用 shell.startView)。view.name 自动收敛为路由模板 —— App Router 经「具体 pathname + 参数」反推(/user/42 → /user/[id],catch-all /docs/a/b/c → /docs/[...slug]);Pages Router 直接采用 router.pathname(本身就是模板)
  • Next.js 错误采集:addNextjsError 面向 app/error.tsx / app/global-error.tsx 接收的错误参数上报,snapshots 自动携带 digest(生产模式下关联服务端日志的唯一键)与 framework: 'nextjs' 标识;附 ErrorBoundary 组件
  • 纯客户端集成档:本包为轻量客户端适配层,不含服务端监控、next.config 包装、sourcemap 上传(见「已知限制」与演进说明)

安装

本插件不独立工作 —— 需与主包 @arms/rum-browser(必需 peer dependency)同时安装,并先完成主包初始化(见「App Router 接入」):

npm install @arms/rum-browser @arms/rum-browser-nextjs

@arms/rum-browser-react 是本包的内部依赖(非 peer),npm 会自动安装,用户无需单独安装或关心。

版本要求

| 依赖 | 版本要求 | 必选 | 说明 | | ------------------- | ---------- | ---- | ---------------------------------------------------------------------------------------------------------------------- | | @arms/rum-core | >=0.1.11 | 是 | 含 Shell.startView 与 IConfiguration.trackViewsManually 的发版(已发布) | | @arms/rum-browser | >=0.1.16 | 是 | 含 PvCollector 手动模式防护(trackViewsManually 早退)的发版(已发布) | | react | >=18 | 否 | optional:组件本质是 React 组件,但 peer 标记为 optional 以兼容边缘消费场景(npm 7+ 自动解析传递 peer) | | next | >=13.0.0 | 否 | optional:仅 ./app-router(next/navigation)与 ./pages-router(next/router)两个子入口使用;主入口零 next 依赖 |

选择你的 Router

入口决策:App Router(项目含 app/ 目录)→ @arms/rum-browser-nextjs/app-router;Pages Router(项目含 pages/ 目录)→ @arms/rum-browser-nextjs/pages-router;两者并存时 App Router 优先。不确定项目用的是哪种 Router?看项目根目录:有 app/ 即为 App Router,有 pages/ 且无 app/ 即为 Pages Router。

快速开始(5 分钟最短接入)

适用 Next.js 15.3+ App Router 项目的最短路径(三步)。每步仅列必填项,详细字段见下方「App Router 接入」。

一键生成(可选):在项目根目录执行 npx -p @arms/rum-browser-nextjs rum-nextjs-init,自动探测 Router 类型(App/Pages、src/ 布局)与 Next 版本(15.3+ / 13~14 降级),生成 instrumentation-client.ts、app/error.tsx 并输出 layout.tsx / _app.tsx 待合并片段;已存在的文件默认跳过(--dry-run 预览、--force 覆盖)。以下三步为手工接入的等价说明。

1. 安装(2 个包)

npm install @arms/rum-browser @arms/rum-browser-nextjs

2. 初始化并重导出路由钩子 —— instrumentation-client.ts(项目根)

import armsRum from '@arms/rum-browser';
import { initNextjsPlugin } from '@arms/rum-browser-nextjs';

armsRum.init({ endpoint: 'https://<你的 RUM 上报端点>' });
initNextjsPlugin(armsRum);
export { onRouterTransitionStart } from '@arms/rum-browser-nextjs';

3. 根布局挂载检测组件 + error.tsx 上报 —— app/layout.tsx、app/error.tsx

// app/layout.tsx
import { ArmsAppRouter } from '@arms/rum-browser-nextjs/app-router';
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html>
      <body>
        <ArmsAppRouter />
        {children}
      </body>
    </html>
  );
}
// app/error.tsx
'use client';
import { useEffect } from 'react';
import { addNextjsError } from '@arms/rum-browser-nextjs';
export default function Error({
  error,
}: {
  error: Error & { digest?: string };
}) {
  useEffect(() => {
    addNextjsError(error);
  }, [error]);
  return <button onClick={() => location.reload()}>重试</button>;
}

完成后启动 next dev,访问任一路由即可在平台看到首条 view 事件。Pages Router 或 Next.js 13/14 项目见下文对应章节。

App Router 接入(推荐路径,Next.js 15.3+)

1. 初始化插件并重导出路由过渡钩子 —— instrumentation-client.ts

在项目根(或 src/)目录新建 instrumentation-client.ts(Next.js 15.3+ 的客户端监测入口,hydration 前执行,不阻塞首屏渲染):

// instrumentation-client.ts
import armsRum from '@arms/rum-browser';
import { initNextjsPlugin } from '@arms/rum-browser-nextjs';

armsRum.init({
  endpoint: 'https://<你的 RUM 上报端点>', // 必填
  sessionConfig: {},
  collectors: {
    pv: { enable: true },
    jsError: { enable: true },
    // ... 其余采集器按需开启(与 @arms/rum-browser 配置一致)
  },
});

// 插件接入不再依赖 init 完成,可在 init 前后任意时序同步调用
initNextjsPlugin(armsRum);

// 如需自定义路由集成(方式 a,可选):
// import { createRouterConfig } from '@arms/rum-browser-react/react-router-v6';
// initNextjsPlugin(armsRum, { router: createRouterConfig({ routes }) });

// 重导出 Next.js 路由过渡钩子(必须):
// App Router 导航时 React 渲染先于 pushState 执行,渲染期读取 window.location
// 拿到的是上一个路由的旧值(url 滞后一拍)。Next.js 在路由过渡开始(渲染前)
// 回调该钩子,插件捕获导航目标 URL 并在下一次 view 上报中一次性消费 ——
// 不重导出则 view 事件的 url 将滞后一次导航。
export { onRouterTransitionStart } from '@arms/rum-browser-nextjs';

2. 根布局挂载 view 检测组件 —— app/layout.tsx

// app/layout.tsx
import { ArmsAppRouter } from '@arms/rum-browser-nextjs/app-router';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="zh-CN">
      <body>
        <ArmsAppRouter />
        {children}
      </body>
    </html>
  );
}

ArmsAppRouter 在渲染期通过 usePathname + useParams 检测路由变化,反推路由模板后调用 startView,组件返回 null(无渲染输出、无 hydration mismatch)。

3. 错误组件上报 —— app/error.tsx / app/global-error.tsx

// app/error.tsx(app/global-error.tsx 同理)
'use client';
import { useEffect } from 'react';
import { addNextjsError } from '@arms/rum-browser-nextjs';

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  useEffect(() => {
    // 生产模式下 Server Component 抛出的错误 message 会被 generic 化
    // (如 "An error occurred in the Server Components render"),
    // digest 是关联服务端日志的唯一稳定键 —— addNextjsError 自动携带
    addNextjsError(error);
  }, [error]);

  return (
    <div>
      <h2>页面出错了</h2>
      <button onClick={() => reset()}>重试</button>
    </div>
  );
}

验证提醒:digest 仅在生产构建(next build && next start)下是可预期的错误关联形态(dev 下 message 保留原文、digest 可能缺失),联调验证请以 prod 模式为准。

Pages Router 接入

在 pages/_app.tsx 挂载 ArmsPagesRouter;初始化方式与 App Router 相同(Next.js 15.3+ 推荐仍经 instrumentation-client.ts,13/14 移入客户端组件 useEffect):

// pages/_app.tsx
import { useEffect } from 'react';
import type { AppProps } from 'next/app';
import armsRum from '@arms/rum-browser';
import { initNextjsPlugin } from '@arms/rum-browser-nextjs';
import { ArmsPagesRouter } from '@arms/rum-browser-nextjs/pages-router';

export default function App({ Component, pageProps }: AppProps) {
  useEffect(() => {
    armsRum.init({
      endpoint: 'https://<你的 RUM 上报端点>', // 必填
    });
    initNextjsPlugin(armsRum);
  }, []);

  return (
    <>
      <ArmsPagesRouter />
      <Component {...pageProps} />
    </>
  );
}

Pages Router 下 onRouterTransitionStart 重导出非必需:ArmsPagesRouter 检测到导航时以 router.asPath 作为显式 URL 传入(优先级高于预捕获值),url 无滞后问题。

hydration 前提:ArmsPagesRouter 为客户端组件,view 检测以 React hydration(客户端接管)启动为前提 —— hydration 启动后渲染期路由比较才会执行。个别环境(部分 Electron / WebView 壳浏览器)不启动 Pages Router 的 hydration,此时 ArmsPagesRouter 不渲染、view 不会上报 —— 这是环境限制而非插件缺陷(App Router 不受影响)。接入前如需确认目标环境行为,可先在页面组件的 useEffect 中设置标记(如 window.__HYDRATED__ = true)验证 hydration 是否启动。

Next.js 13 / 14 接入(退化指引)

Next.js 13/14 无 instrumentation-client.ts 入口,初始化移入任意客户端组件的 useEffect:

// app/rum-init.tsx(在根 layout 中挂载:<RumInit />)
'use client';
import { useEffect } from 'react';
import armsRum from '@arms/rum-browser';
import { initNextjsPlugin } from '@arms/rum-browser-nextjs';

export function RumInit() {
  useEffect(() => {
    armsRum.init({
      endpoint: 'https://<你的 RUM 上报端点>', // 必填
    });
    initNextjsPlugin(armsRum);
  }, []);
  return null;
}

行为差异:

  • view.name 收敛不受影响 —— 渲染期路由比较机制全版本可用;
  • 仅丢失 url 精确捕获:无路由过渡钩子可重导出,路由切换时 view 的 url 退化为渲染期的 window.location.href(可能滞后一次导航);
  • 插件以行为信号检测该场景(路由切换未捕获到预捕获 URL,无论成因是 Next < 15.3 还是 15.3+ 忘记 re-export),打印一次性的 logger.warn 提示 —— 属预期降级提示,非错误。

路由追踪说明

警告:接入本插件后 view/PV 完全由路由组件驱动,主包自动 PV 被禁用

插件(经转发的 initReactPlugin)注入 trackViewsManually: true:主包 PvCollector 跳过首屏 PV 与 history 拦截(避免双发),view 的创建、命名、计时完全依赖 ArmsAppRouter / ArmsPagesRouter 在渲染期检测路由变化驱动(内部调用 shell.startView)。这意味着 view 上报以路由检测组件真实挂载为前提 —— 未挂载 ArmsAppRouter / ArmsPagesRouter 时 view 将不会上报(无报错、无告警)。

view 上报行为:

  • view.name 收敛:App Router 经 computeViewNameFromParams 由「具体 pathname + 参数」反推模板(算法见下文 API 参考);Pages Router 直接采用 router.pathname(本身就是路由模板)
  • url 优先级:Pages Router 显式 asPath > onRouterTransitionStart 预捕获值(一次性消费,防串用)> 渲染期 window.location.href 兜底;最终统一绝对化后上报
  • loading_type:首个 view 为 initial_load,此后为 route_change
  • 同名动态路由的不同具体 URL(/user/42 → /user/43)经 pathname/asPath 变化检测正常触发上报调用,view 是否新建遵循 Shell.startView 同名去重语义(模板相同不新建,与 react-router 场景一致)
// 访问 /user/42 时上报的 view 事件:
// { event_type: 'view', type: 'pv', name: '/user/[id]', loading_type: 'initial_load', url: 'https://...', ... }

错误采集说明

addNextjsError(error, errorInfo?) 上报的事件结构与 @arms/rum-browser-react 的 addReactError 对齐(source: 'react',复用既有存储/查询链路),差异仅在 snapshots 内附加 digest 与 framework: 'nextjs':

{
  event_type: 'exception',
  type: 'error',
  source: 'react',
  name: 'Error',                  // error.name(非 Error 值自动 normalize)
  message: "An error occurred in the Server Components render...",
  stack: '...',
  // 扩展信息收敛为单一 JSON 字符串字段(值为 undefined 的内部键不会出现)
  snapshots: JSON.stringify({
    component_stack: 'at UserProfile\nat ...',  // errorInfo.componentStack(传入时)
    handling: 'handled',
    digest: '3669057531',                       // error.digest(存在时)
    framework: 'nextjs',
  }),
  times: 1,
}

ErrorBoundary 组件包裹组件树时自动走 addNextjsError 上报(用法与 @arms/rum-browser-react 的 ErrorBoundary 一致,fallback 接收 error 与 resetError)。

错误聚合注意事项

core 的 reporter 对 exception 类事件按 getErrorID(message + stack) 聚合,聚合 key 不包含 source 字段。当同一错误同时被全局异常采集通道(window.onError)与 addNextjsError 捕获时,addNextjsError 上报的 source='react' 富上下文事件(含 digest / framework: 'nextjs')可能被先到的全局事件通过 times++ 合并吞掉,无法独立可见。典型场景是 app/global-error.tsx:错误在客户端被重放时会先触发 window.onError 入队。

规避方法:通过 init 配置的 filters.exception 过滤对应错误 —— filters 仅作用于 collector 自动采集通道,不影响 addNextjsError(sendEvent 直发)上报;过滤全局通道后 source='react' 事件即可独立上报(times=1)。参考示例:examples/browser-nextjs/instrumentation-client.ts 中有实际用法。

API 参考

主入口 @arms/rum-browser-nextjs

| 导出 | 说明 | | -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | initNextjsPlugin(shell: Shell, options?: NextjsPluginOptions): void | 初始化插件:缓存 shell + 转发 initReactPlugin(继承 trackViewsManually 注入/首屏防御/幂等守卫)。options.router 透传给 initReactPlugin(方式 a,可选) | | onRouterTransitionStart(url: string): void | 路由过渡钩子 —— 须在 instrumentation-client.ts 中 re-export;捕获导航目标 URL,解决渲染期 url 滞后一拍 | | addNextjsError(error: unknown, errorInfo?: ErrorInfo): void | 手动上报 Next.js 渲染异常(app/error.tsx / global-error.tsx 接收的 error 参数);snapshots 附 digest 与 framework: 'nextjs' | | ErrorBoundary: ComponentType<ErrorBoundaryProps> | Next.js 场景错误边界(createErrorBoundary(addNextjsError) 工厂组装),上报自动携带 digest | | computeViewNameFromParams(pathname: string, params: RouteParams): string | 路由模板反推工具(高级用法/测试可用) |

类型导出:NextjsError(Error & { digest?: string })、RouteParams(Record<string, string | string[] | undefined>)、ErrorBoundaryProps / ErrorBoundaryFallback(自 @arms/rum-browser-react type-only re-export)。

刻意不导出 useRum 等通用 React 能力 —— 需要者直接从 @arms/rum-browser-react 导入(最小 API 暴露原则)。

子入口 @arms/rum-browser-nextjs/app-router

| 导出 | 说明 | | ----------------------- | ----------------------------------------------------------------- | | ArmsAppRouter(): null | App Router view 检测组件,挂载于根 layout;返回 null 无渲染输出 |

子入口 @arms/rum-browser-nextjs/pages-router

| 导出 | 说明 | | ------------------------- | ------------------------------------------------------------------------------- | | ArmsPagesRouter(): null | Pages Router view 检测组件,挂载于 pages/_app.tsx;内置 router.isReady 守卫 |

computeViewNameFromParams(pathname, params)

两阶段反推算法(App Router 无客户端路由配置,只能由实际 pathname + 参数反推):

  1. catch-all(数组)参数优先:[...slug] 展开的连续段整体定位并替换回 [...slug](防止值重叠时被普通参数替换吞掉);
  2. 普通(字符串)参数贪婪左到右:参数值在段中首次出现的位置替换为 [paramName];多参数同值时按迭代顺序从左到右命名。

编码对称处理:usePathname() 与 useParams() 的编码形态并无稳定契约 —— Next 文档宣称 params 为 decoded,实测 next 15.5.23 两者均返回 URL 编码形态(如 /user/%E5%BC%A0%E4%B8%89 与 { id: '%E5%BC%A0%E4%B8%89' }),且历史版本间行为亦有摇摆 —— 直接以原段匹配参数值在「path 与 params 形态不一致」的场景下必然失配,中文等非 ASCII 动态段会退化为具体路径、模板不收敛。本包以「统一解码域匹配」覆盖全部形态组合(匹配双方均经 decodeURIComponent 归一,替换同步写入原段):中文动态段在任一形态组合下正常收敛,返回值中静态段保留原始编码形态(与 Next 自身 pathname 形态一致)。

| pathname | params | 返回值 | | -------------------------- | -------------------------------- | --------------------------------------- | | /blog/hello-world | {} | /blog/hello-world(原样) | | /user/42 | { id: '42' } | /user/[id] | | /user/%E5%BC%A0%E4%B8%89 | { id: '张三' } | /user/[id](编码对称) | | /docs/a/b/c | { slug: ['a', 'b', 'c'] } | /docs/[...slug] | | /shop/item/1/review/1 | { itemId: '1', reviewId: '1' } | /shop/item/[itemId]/review/[reviewId] | | /user/42/posts/42 | { id: '42' } | /user/[id]/posts/42(仅首次出现替换) |

反推是启发式算法,极端值重叠场景存在理论误差,实际业务影响可忽略。

版本兼容矩阵

| Next.js 版本 | 支持情况 | 说明 | | ------------ | ----------- | ----------------------------------------------------------------------------------------------------------- | | 15.3+ | ✅ 完整能力 | instrumentation-client.ts 初始化 + onRouterTransitionStart URL 预捕获(无滞后) | | 13 ~ 14 | ⚠️ 退化支持 | 初始化移入客户端组件 useEffect(见「Next.js 13 / 14 接入」);view.name 收敛不受影响,仅丢失 url 精确捕获 | | Turbopack | ✅ 支持 | exports 全条件带 .js 扩展名(Next 16 起 Turbopack 默认打包器) | | webpack 5 | ✅ 支持 | Next.js 内置构建链 |

本包仅经 bundler(npm 工程)消费,不出 UMD dist 产物;目标环境为 Next.js 工程(内置 webpack 5 / Turbopack),不涉及 webpack 4(未提供根级存根,区别于 @arms/rum-browser-react 的 v4 兼容存根)。

React 兼容性

| React 版本 | 支持情况 | 说明 | | ---------- | --------- | ----------------------------------------------------------------------------------------------------------------- | | 18.x | ✅ 已验证 | devDeps 锁定 ^18.3.1,单测与 E2E 全量通过 | | 19.x | ✅ 已验证 | peer 放开为 >=18;本地以 React 19.0.0 一次性运行全量单测(38/38 通过),view/error/ErrorBoundary 行为与 18 一致 |

产物压缩说明:es / lib 产物已经过 terser 压缩与混淆,且不附带 sourcemap。排查线上堆栈时请对照仓库源码与 CHANGELOG 版本定位对应发布版本。

已知限制

以下限制是当前设计所固有的,后续版本计划改进。

query-only 导航不触发新 view

/list?page=1 → /list?page=2 仅 query 变化:App Router 的 usePathname 与 Pages Router 的 asPath path 段检测均不视为导航 —— 不产生新 view(预期行为)。query 维度变化如需观测请通过自定义事件补齐。

parallel / interception routes 不触发新 view

并行路由与拦截路由不改变 primary pathname,渲染期比较无法感知(预期行为)。Next.js 16.3+ 的 fromRoutes(experimental)是官方给出的未来解法,列入演进路径。

CDN 场景指引

本包仅经 bundler 消费(无 UMD 产物),CDN(<script> 标签)接入场景不适用本插件 —— 请直接使用主包 @arms/rum-browser(自动 PV + 全局异常采集)。若以 CDN 方式加载主包,请在 App Router 下用 next/script 的 afterInteractive 策略加载;勿用 beforeInteractive(App Router 下不支持且阻塞首屏渲染)。

单实例约束

插件为模块级单例(导航状态持有 shell 引用,与 @arms/rum-browser-react 一致)。多 RUM 实例场景下最后一次初始化生效;微前端多子应用各自持有独立实例的场景暂不支持,列入后续演进。

source='react' 错误不受 collectors.jsError 采样管控

addNextjsError / ErrorBoundary 上报的错误携带 source: 'react'。browser SDK 的 SessionProcessor 采样键映射当前未包含 'react' source,这些错误会绕过 collectors.jsError 采样配置。如需禁用,请使用 collectors.exception(设置为 false)。后续版本可能会新增 'react' → 'js' 的 source 映射。

初始化时序

initNextjsPlugin 可在 armsRum.init() 前后任意时序同步调用 —— useCollectors 的双时序注册机制保证 collector 在 init 完成后统一 setup。初始化完成之前路由组件或 addNextjsError 触发的上报会安全降级(打印告警并跳过,不抛错)。

推荐写法是同步调用(initNextjsPlugin(armsRum) 紧跟 armsRum.init({...}) 之后),这也是本包示例工程的写法。若使用 remote-config 远端优先模式(init 晚于 hydration),早到的 view 会因 client 未就绪被丢弃并告警 —— 此场景下可改用 armsRum.init({...}).then(() => initNextjsPlugin(armsRum)) 确保首屏 view 就绪率。