Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/create-runtime-tanstack-tailwind.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@modern-js/runtime': minor
'@modern-js/plugin-tanstack': minor
---

feat(runtime): move TanStack Router integration to `@modern-js/plugin-tanstack`

- add `@modern-js/plugin-tanstack` runtime/cli package surface
- add generic runtime router CLI and SSR hooks so router integrations can be implemented by plugins
- keep TanStack route generation/runtime ownership in the standalone plugin instead of `@modern-js/runtime`
3 changes: 3 additions & 0 deletions packages/document/docs/en/apis/app/runtime/router/router.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ sidebar_position: 1
:::info
The router solution based on [react-router v7](https://reactrouter.com/).

This page documents the React Router runtime export (`@modern-js/runtime/router`).
If your app uses TanStack Router through `@modern-js/plugin-tanstack`, use `@modern-js/plugin-tanstack/runtime` and refer to TanStack Router API docs.

:::

## hooks
Expand Down
17 changes: 14 additions & 3 deletions packages/document/docs/en/guides/basic-features/routes/routes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ The routing mentioned in this section all refers to conventional routing.

:::

:::tip
This page uses React Router import paths in examples (`@modern-js/runtime/router`).
If your project uses `@modern-js/plugin-tanstack`, use `@modern-js/plugin-tanstack/runtime` instead.
:::

## What is Nested Routing

Nested routing is a pattern that couples URL segments with the component hierarchy and data. Typically, URL segments determine:
Expand Down Expand Up @@ -493,13 +498,19 @@ import Motivation from '@site-docs-en/components/convention-routing-motivation';

## FAQ

1. Why is there `@modern-js/runtime/router` to re-export React Router API?
1. Why should I import from `@modern-js/runtime/router` or `@modern-js/plugin-tanstack/runtime`?

Notice that all the code examples in the documentation uses APIs exported from the `@modern-js/runtime/router` package instead of directly using the API exported from the React Router package. So, what is the difference?

The API exported from `@modern-js/runtime/router` is the same as the API from the React Router package. If you encounter issues while using an API, check the React Router documentation and issues first.
- `@modern-js/runtime/router` for React Router mode.
- `@modern-js/plugin-tanstack/runtime` for TanStack Router mode.

Use the Modern.js runtime export that matches your selected router framework, instead of importing directly from the upstream router package. This avoids dependency duplication and ensures compatibility with Modern.js route generation/runtime behavior.

If you encounter API usage issues, check the corresponding upstream docs:

Additionally, when using conventional routing, make sure to use the API from `@modern-js/runtime/router` instead of directly using the React Router API. Modern.js internally installs React Router, and using the React Router API directly in your application may result in two versions of React Router being present, causing unexpected behavior.
- React Router docs for `@modern-js/runtime/router`.
- TanStack Router docs for `@modern-js/plugin-tanstack/runtime`.

:::note
If you must directly use the React Router package's API (e.g., route behavior wrapped in a unified npm package), you can set [`source.alias`](/configure/app/source/alias) to point `react-router` and `react-router-dom` to the project's dependencies, avoiding the issue of two versions of React Router.
Expand Down
7 changes: 6 additions & 1 deletion packages/document/docs/en/guides/get-started/tech-stack.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ Rsbuild supports building Vue applications. If you need to use Vue, you can refe

## Routing

Modern.js uses [React Router v7](https://reactrouter.com/en/main) for routing.
Modern.js provides two first-party routing frameworks:

- [React Router v7](https://reactrouter.com/en/main) (default), via `@modern-js/runtime/router`.
- [TanStack Router](https://tanstack.com/router), via `@modern-js/plugin-tanstack/runtime`.

TanStack Router support is provided by `@modern-js/plugin-tanstack`, so the TanStack packages do not need to be bundled into `@modern-js/runtime`.

Modern.js supports conventional routing, self-controlled routing, or other routing schemes. Please refer to ["Routing"](/guides/basic-features/routes/routes) to make your choice.

Expand Down
3 changes: 3 additions & 0 deletions packages/document/docs/zh/apis/app/runtime/router/router.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ sidebar_position: 1
:::info 补充信息
基于 [react-router](https://reactrouter.com/) 的路由解决方案。

本页文档对应 React Router 运行时导出(`@modern-js/runtime/router`)。
如果应用通过 `@modern-js/plugin-tanstack` 使用 TanStack Router,请使用 `@modern-js/plugin-tanstack/runtime`,并参考 TanStack Router 官方 API 文档。

:::

## hooks
Expand Down
17 changes: 14 additions & 3 deletions packages/document/docs/zh/guides/basic-features/routes/routes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ Modern.js 的路由基于 [React Router 7](https://reactrouter.com/en/main),

:::

:::tip
本页示例默认使用 React Router 的导出路径(`@modern-js/runtime/router`)。
如果你的项目使用 `@modern-js/plugin-tanstack`,请改用 `@modern-js/plugin-tanstack/runtime`。
:::

## 什么是嵌套路由

嵌套路由是一种将 URL 分段与组件层次结构和数据耦合起来的路由模式。通常,URL 段会决定:
Expand Down Expand Up @@ -495,13 +500,19 @@ import Motivation from '@site-docs/components/convention-routing-motivation';

## 常见问题

1. 为什么要提供 `@modern-js/runtime/router` 来导出 React Router API ?
1. 为什么要通过 `@modern-js/runtime/router` 或 `@modern-js/plugin-tanstack/runtime` 引入 API

可以发现,在文档中所有的代码用例都是使用 `@modern-js/runtime/router` 包导出的 API,而不是直接使用 React Router 包导出的 API。那两者有什么区别呢?

首先,在 `@modern-js/runtime/router` 中导出的 API 和 React Router 包的 API 是完全一致的,如果某个 API 使用出现问题,请先检查 React Router 的文档和 Issues。
- React Router 模式使用 `@modern-js/runtime/router`。
- TanStack Router 模式使用 `@modern-js/plugin-tanstack/runtime`。

建议优先使用与当前路由方案对应的 Modern.js 运行时导出,而不是直接从上游路由包中引入。这样可以避免依赖重复,并确保与 Modern.js 的路由生成和运行时行为保持一致。

如果遇到 API 使用问题,可以分别参考上游文档:

在使用约定式路由的情况下,务必使用 `@modern-js/runtime/router` 中的 API,不直接使用 React Router 的 API。因为 Modern.js 内部会安装 React Router,如果应用中使用了 React Router 的 API,可能会导致两个版本的 React Router 同时存在,出现不符合预期的行为。
- `@modern-js/runtime/router` 对应 React Router 文档。
- `@modern-js/plugin-tanstack/runtime` 对应 TanStack Router 文档。

:::note

Expand Down
7 changes: 6 additions & 1 deletion packages/document/docs/zh/guides/get-started/tech-stack.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ Modern.js 底层的 Rsbuild 支持构建 Vue 应用,如果你需要使用 Vue

## 路由

Modern.js 的路由基于 [React Router 7](https://reactrouter.com/en/main)。
Modern.js 提供两套一方路由方案:

- 默认使用 [React Router 7](https://reactrouter.com/en/main),通过 `@modern-js/runtime/router` 导出 API。
- 支持 [TanStack Router](https://tanstack.com/router),通过 `@modern-js/plugin-tanstack/runtime` 导出 API。

TanStack Router 支持由 `@modern-js/plugin-tanstack` 提供,因此不需要把 TanStack 相关包直接内置到 `@modern-js/runtime` 中。

Modern.js 支持约定式路由、自控式路由或其他路由方案,请参考 [页面入口](/guides/concept/entries) 进行选择。

Expand Down
24 changes: 21 additions & 3 deletions packages/runtime/plugin-runtime/src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,34 @@ import {
isReact18 as checkIsReact18,
cleanRequireCache,
} from '@modern-js/utils';
import { documentPlugin } from '../document/cli';
import { routerPlugin } from '../router/cli';
import {
documentPlugin,
} from '../document/cli';
import {
getEntrypointRoutesDir,
handleFileChange,
handleGeneratorEntryCode,
handleModifyEntrypoints,
isRouteEntry,
routerPlugin,
} from '../router/cli';
import { builderPluginAlias } from './alias';
import { generateCode } from './code';
import { ENTRY_BOOTSTRAP_FILE_NAME, ENTRY_POINT_FILE_NAME } from './constants';
import { isRuntimeEntry } from './entry';
import { ssrPlugin } from './ssr';

export { isRuntimeEntry } from './entry';
export { ssrPlugin, routerPlugin, documentPlugin };
export {
documentPlugin,
getEntrypointRoutesDir,
handleFileChange,
handleGeneratorEntryCode,
handleModifyEntrypoints,
isRouteEntry,
routerPlugin,
ssrPlugin,
};
export const runtimePlugin = (params?: {
plugins?: CliPlugin<AppTools>[];
}): CliPlugin<AppTools> => ({
Expand Down
19 changes: 16 additions & 3 deletions packages/runtime/plugin-runtime/src/core/context/runtime.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import type { RouteObject } from '@modern-js/runtime-utils/router';
import type { StaticHandlerContext } from '@modern-js/runtime-utils/router';
import type {
RouteObject,
StaticHandlerContext,
} from '@modern-js/runtime-utils/router';
import type { BaseSSRServerContext } from '@modern-js/types';
import { ROUTE_MANIFEST } from '@modern-js/utils/universal/constants';
import { createContext, useContext } from 'react';
import type { RouteManifest } from '../../router/runtime/types';
import type {
InternalRouterRuntimeState,
InternalRouterServerSnapshot,
RouteManifest,
RouterFramework,
} from '../../router/runtime/types';
import type { RequestContext, SSRServerContext } from '../types';

export interface TRuntimeContext {
initialData?: Record<string, unknown>;
isBrowser: boolean;
routes?: RouteObject[];
routerFramework?: RouterFramework;
requestContext: RequestContext;
/**
* @deprecated Use `requestContext` instead
Expand All @@ -23,6 +31,11 @@ export interface TRuntimeContext {
*/
export interface TInternalRuntimeContext extends TRuntimeContext {
routeManifest?: RouteManifest;
routerRuntime?: InternalRouterRuntimeState;
routerInstance?: unknown;
routerHydrationScript?: string;
routerMatchedRouteIds?: string[];
routerServerSnapshot?: InternalRouterServerSnapshot;
routerContext?: StaticHandlerContext;
unstable_getBlockNavState?: () => boolean;
ssrContext?: SSRServerContext;
Expand Down
7 changes: 7 additions & 0 deletions packages/runtime/plugin-runtime/src/core/react/wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,14 @@ export function wrapRuntimeContextProvider(
isBrowser,
initialData,
routes,
routerFramework,
context,
routeManifest,
routerRuntime,
routerInstance,
routerHydrationScript,
routerMatchedRouteIds,
routerServerSnapshot,
routerContext,
unstable_getBlockNavState,
ssrContext,
Expand All @@ -28,6 +34,7 @@ export function wrapRuntimeContextProvider(
isBrowser,
initialData,
routes,
routerFramework,
context,
...rest,
};
Expand Down
Loading