Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
0ef10ab
chore: generate markdown docs from jsdocs
remix-run-bot Jan 6, 2026
02d430c
Bump @remix-run/node-fetch-server dep in dev package (#14704)
brophdawg11 Jan 7, 2026
690939f
chore: deduplicate `pnpm-lock.yaml`
remix-run-bot Jan 7, 2026
8dee4cd
Merge branch 'release-next' into dev
brophdawg11 Jan 7, 2026
c38d76c
chore: format
remix-run-bot Jan 7, 2026
dd08f8d
fix(react-router): add crossOrigin prop to Links component (#14687)
joseph0926 Jan 8, 2026
b248e39
chore: generate markdown docs from jsdocs
remix-run-bot Jan 8, 2026
9949c1d
Add data mode playground
brophdawg11 Jan 8, 2026
e630aea
chore: deduplicate `pnpm-lock.yaml`
remix-run-bot Jan 8, 2026
9987ba0
fix(fs-routes): resolve route file outside of app dir relative to app…
frodi-karlsson Jan 9, 2026
0bb972b
fix(react-router/dom/ssr): add `nonce` to inline critical css (#14691)
AnandShiva Jan 12, 2026
28605c8
chore: format
remix-run-bot Jan 12, 2026
f29c6c9
Add docs and loosen origins wildcard check (#14722)
brophdawg11 Jan 15, 2026
3a5b5ad
Fix double slash normalization for `useNavigate` paths with colons (#…
brophdawg11 Jan 16, 2026
3126264
Return 400 response on failed origin checks (#14737)
brophdawg11 Jan 20, 2026
a652b33
Merge branch 'main' into release-next
brophdawg11 Jan 20, 2026
0ee8f1a
Enter prerelease mode
brophdawg11 Jan 20, 2026
62c6e0e
chore: Update version for release (pre) (#14738)
github-actions[bot] Jan 20, 2026
d1f87c1
Draft release notes
brophdawg11 Jan 20, 2026
91188d2
Fix playground workspace dep
brophdawg11 Jan 20, 2026
1ce738d
Exit prerelease mode
brophdawg11 Jan 23, 2026
5557ba3
chore: Update version for release (#14749)
github-actions[bot] Jan 23, 2026
98ebb81
Merge branch 'release-next'
brophdawg11 Jan 23, 2026
8cb8264
chore: format
remix-run-bot Jan 23, 2026
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
300 changes: 161 additions & 139 deletions CHANGELOG.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions contributors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
- amitdahan
- AmRo045
- amsal
- AnandShiva
- Andarist
- andreasottosson-polestar
- andreiborza
Expand Down
8 changes: 7 additions & 1 deletion docs/api/components/Links.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default function Root() {
## Signature

```tsx
function Links({ nonce }: LinksProps): React.JSX.Element
function Links({ nonce, crossOrigin }: LinksProps): React.JSX.Element
```

## Props
Expand All @@ -56,3 +56,9 @@ A [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_a
attribute to render on the [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
element

### crossOrigin

A [`crossOrigin`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin)
attribute to render on the [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
element

45 changes: 44 additions & 1 deletion docs/api/framework-conventions/react-router.config.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,45 @@ export default {

## Options

### `allowedActionOrigins`

An array of allowed origin hosts for action submissions to UI routes (does not apply to resource routes). Supports micromatch glob patterns (`*` to match one segment, `**` to match multiple).

```tsx filename=react-router.config.ts
export default {
allowedActionOrigins: [
"example.com",
"*.example.com", // sub.example.com
"**.example.com", // sub.domain.example.com
],
} satisfies Config;
```

If you need to set this value at runtime, you can do in by setting the value on the server build in your custom server. For example, when using `express`:

```ts
import express from "express";
import { createRequestHandler } from "@react-router/express";
import type { ServerBuild } from "react-router";

export const app = express();

async function getBuild() {
let build: ServerBuild = await import(
"virtual:react-router/server-build"
);
return {
...build,
allowedActionOrigins:
process.env.NODE_ENV === "development"
? undefined
: ["staging.example.com", "www.example.com"],
};
}

app.use(createRequestHandler({ build: getBuild }));
```

### `appDirectory`

The path to the `app` directory, relative to the root directory. Defaults to `"app"`.
Expand Down Expand Up @@ -66,7 +105,11 @@ A function that is called after the full React Router build is complete.

```tsx filename=react-router.config.ts
export default {
buildEnd: async ({ buildManifest, reactRouterConfig, viteConfig }) => {
buildEnd: async ({
buildManifest,
reactRouterConfig,
viteConfig,
}) => {
// Custom build logic here
console.log("Build completed!");
},
Expand Down
6 changes: 6 additions & 0 deletions docs/api/rsc/matchRSCServerRequest.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ matchRSCServerRequest({

```tsx
async function matchRSCServerRequest({
allowedActionOrigins,
createTemporaryReferenceSet,
basename,
decodeReply,
Expand All @@ -82,6 +83,7 @@ async function matchRSCServerRequest({
routes,
generateResponse,
}: {
allowedActionOrigins?: string[];
createTemporaryReferenceSet: () => unknown;
basename?: string;
decodeReply?: DecodeReplyFunction;
Expand All @@ -107,6 +109,10 @@ async function matchRSCServerRequest({

## Params

### opts.allowedActionOrigins

Origin patterns that are allowed to execute actions.

### opts.basename

The basename to use when matching the request.
Expand Down
6 changes: 6 additions & 0 deletions packages/create-react-router/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
# `create-react-router`

## 7.13.0

_No changes_

## 7.12.0

_No changes_

## 7.11.0

_No changes_
Expand Down
2 changes: 1 addition & 1 deletion packages/create-react-router/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "create-react-router",
"version": "7.12.0",
"version": "7.13.0",
"description": "Create a new React Router app",
"homepage": "https://reactrouter.com",
"bugs": {
Expand Down
8 changes: 8 additions & 0 deletions packages/react-router-architect/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# `@react-router/architect`

## 7.13.0

### Patch Changes

- Updated dependencies:
- `react-router@7.13.0`
- `@react-router/node@7.13.0`

## 7.12.0

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/react-router-architect/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@react-router/architect",
"version": "7.12.0",
"version": "7.13.0",
"description": "Architect server request handler for React Router",
"bugs": {
"url": "https://github.com/remix-run/react-router/issues"
Expand Down
7 changes: 7 additions & 0 deletions packages/react-router-cloudflare/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# `@react-router/cloudflare`

## 7.13.0

### Patch Changes

- Updated dependencies:
- `react-router@7.13.0`

## 7.12.0

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/react-router-cloudflare/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@react-router/cloudflare",
"version": "7.12.0",
"version": "7.13.0",
"description": "Cloudflare platform abstractions for React Router",
"bugs": {
"url": "https://github.com/remix-run/react-router/issues"
Expand Down
10 changes: 10 additions & 0 deletions packages/react-router-dev/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# `@react-router/dev`

## 7.13.0

### Patch Changes

- Bump @remix-run/node-fetch-server dep ([#14704](https://github.com/remix-run/react-router/pull/14704))
- Updated dependencies:
- `react-router@7.13.0`
- `@react-router/node@7.13.0`
- `@react-router/serve@7.13.0`

## 7.12.0

### Minor Changes
Expand Down
40 changes: 38 additions & 2 deletions packages/react-router-dev/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,44 @@ export type ReactRouterConfig = {
ssr?: boolean;

/**
* The allowed origins for actions / mutations. Does not apply to routes
* without a component. micromatch glob patterns are supported.
* An array of allowed origin hosts for action submissions to UI routes (does not apply
* to resource routes). Supports micromatch glob patterns (`*` to match one segment,
* `**` to match multiple).
*
* ```tsx
* export default {
* allowedActionOrigins: [
* "example.com",
* "*.example.com", // sub.example.com
* "**.example.com", // sub.domain.example.com
* ],
* } satisfies Config;
* ```
*
* If you need to set this value at runtime, you can do in by setting the value
* on the server build in your custom server. For example, when using `express`:
*
* ```ts
* import express from "express";
* import { createRequestHandler } from "@react-router/express";
* import type { ServerBuild } from "react-router";
*
* export const app = express();
*
* async function getBuild() {
* let build: ServerBuild = await import(
* "virtual:react-router/server-build"
* );
* return {
* ...build,
* allowedActionOrigins:
* process.env.NODE_ENV === "development"
* ? undefined
* : ["staging.example.com", "www.example.com"],
* };
* }
*
* app.use(createRequestHandler({ build: getBuild }));
*/
allowedActionOrigins?: string[];
};
Expand Down
4 changes: 2 additions & 2 deletions packages/react-router-dev/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@react-router/dev",
"version": "7.12.0",
"version": "7.13.0",
"description": "Dev tools and CLI for React Router",
"homepage": "https://reactrouter.com",
"bugs": {
Expand Down Expand Up @@ -77,7 +77,7 @@
"@babel/traverse": "^7.27.7",
"@babel/types": "^7.27.7",
"@react-router/node": "workspace:*",
"@remix-run/node-fetch-server": "^0.9.0",
"@remix-run/node-fetch-server": "^0.13.0",
"arg": "^5.0.1",
"babel-dead-code-elimination": "^1.0.6",
"chokidar": "^4.0.0",
Expand Down
6 changes: 4 additions & 2 deletions packages/react-router-dev/vite/cloudflare-dev-proxy.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { sendResponse } from "@remix-run/node-fetch-server";
import { createRequestHandler } from "react-router";
import {
type AppLoadContext,
Expand Down Expand Up @@ -121,6 +120,9 @@ export const cloudflareDevProxyVitePlugin = <Env, Cf extends CfProperties>(
}
},
configureServer: async (viteDevServer) => {
// Async import here to allow ESM only module on Node 20.18.
// TODO(v8): Can move to a normal import when Node 20 support
const { sendResponse } = await import("@remix-run/node-fetch-server");
let context: Awaited<ReturnType<typeof getContext>>;
let getContext = async () => {
let { getPlatformProxy } = await importWrangler();
Expand All @@ -139,7 +141,7 @@ export const cloudflareDevProxyVitePlugin = <Env, Cf extends CfProperties>(
)) as ServerBuild;

let handler = createRequestHandler(build, "development");
let req = fromNodeRequest(nodeReq, nodeRes);
let req = await fromNodeRequest(nodeReq, nodeRes);
context ??= await getContext();
let loadContext = getLoadContext
? await getLoadContext({ request: req, context })
Expand Down
9 changes: 5 additions & 4 deletions packages/react-router-dev/vite/node-adapter.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import type { ServerResponse } from "node:http";

import { createRequest } from "@remix-run/node-fetch-server";
import type * as Vite from "vite";

import invariant from "../invariant";

export type NodeRequestHandler = (
req: Vite.Connect.IncomingMessage,
res: ServerResponse,
) => Promise<void>;

export function fromNodeRequest(
export async function fromNodeRequest(
nodeReq: Vite.Connect.IncomingMessage,
nodeRes: ServerResponse<Vite.Connect.IncomingMessage>,
): Request {
): Promise<Request> {
// Use `req.originalUrl` so React Router is aware of the full path
invariant(
nodeReq.originalUrl,
"Expected `nodeReq.originalUrl` to be defined",
);
nodeReq.url = nodeReq.originalUrl;

// Async import here to allow ESM only module on Node 20.18.
// TODO(v8): Can move to a normal import when Node 20 support
const { createRequest } = await import("@remix-run/node-fetch-server");
return createRequest(nodeReq, nodeRes);
}
16 changes: 13 additions & 3 deletions packages/react-router-dev/vite/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
import * as path from "node:path";
import * as url from "node:url";
import * as babel from "@babel/core";
import { sendResponse } from "@remix-run/node-fetch-server";
import {
unstable_setDevServerHooks as setDevServerHooks,
createRequestHandler,
Expand Down Expand Up @@ -1673,11 +1672,16 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = () => {
nodeReq,
nodeRes,
) => {
let req = fromNodeRequest(nodeReq, nodeRes);
let req = await fromNodeRequest(nodeReq, nodeRes);
let res = await handler(
req,
await reactRouterDevLoadContext(req),
);
// Async import here to allow ESM only module on Node 20.18.
// TODO(v8): Can move to a normal import when Node 20 support
const { sendResponse } = await import(
"@remix-run/node-fetch-server"
);
await sendResponse(nodeRes, res);
};
await nodeHandler(req, res);
Expand Down Expand Up @@ -1717,11 +1721,17 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = () => {
nodeReq,
nodeRes,
) => {
let req = fromNodeRequest(nodeReq, nodeRes);
let req = await fromNodeRequest(nodeReq, nodeRes);
let res = await handler(
req,
await reactRouterDevLoadContext(req),
);

// Async import here to allow ESM only module on Node 20.18.
// TODO(v8): Can move to a normal import when Node 20 support
const { sendResponse } = await import(
"@remix-run/node-fetch-server"
);
await sendResponse(nodeRes, res);
};
await nodeHandler(req, res);
Expand Down
7 changes: 7 additions & 0 deletions packages/react-router-dom/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# react-router-dom

## 7.13.0

### Patch Changes

- Updated dependencies:
- `react-router@7.13.0`

## 7.12.0

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/react-router-dom/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-router-dom",
"version": "7.12.0",
"version": "7.13.0",
"description": "Declarative routing for React web applications",
"keywords": [
"react",
Expand Down
8 changes: 8 additions & 0 deletions packages/react-router-express/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# `@react-router/express`

## 7.13.0

### Patch Changes

- Updated dependencies:
- `react-router@7.13.0`
- `@react-router/node@7.13.0`

## 7.12.0

### Patch Changes
Expand Down
Loading