diff --git a/.eslintignore b/.eslintignore
index 26b8a0ae29..49a812d5b4 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -3,9 +3,18 @@ node_modules/
pnpm-lock.yaml
/docs/api
examples/**/dist/
-/integration/helpers/vite-plugin-cloudflare-template/worker-configuration.d.ts
+worker-configuration.d.ts
/playground/
/playground-local/
+integration/helpers/**/dist/
+integration/helpers/**/build/
+playwright-report/
+test-results/
+build.utils.d.ts
+.wrangler/
+.tmp/
+.react-router/
+.react-router-parcel/
packages/**/dist/
packages/react-router-dom/server.d.ts
packages/react-router-dom/server.js
diff --git a/.eslintrc b/.eslintrc
index 61400497f8..2e0c041bd5 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -18,6 +18,78 @@
"env": {
"jest/globals": false
}
+ },
+ {
+ "files": ["packages/react-router-dev/config/default-rsc-entries/*"],
+ "rules": {
+ "@typescript-eslint/consistent-type-imports": "off"
+ }
+ },
+ {
+ // Only apply JSDoc lint rules to files we auto-generate docs for
+ "files": [
+ "packages/react-router/lib/components.tsx",
+ "packages/react-router/lib/hooks.tsx",
+ "packages/react-router/lib/dom/lib.tsx",
+ "packages/react-router/lib/dom/ssr/components.tsx",
+ "packages/react-router/lib/dom/ssr/server.tsx",
+ "packages/react-router/lib/dom-export/hydrated-router.tsx",
+ "packages/react-router/lib/dom/server.tsx",
+ "packages/react-router/lib/router/utils.ts",
+ "packages/react-router/lib/rsc/browser.tsx",
+ "packages/react-router/lib/rsc/server.rsc.ts",
+ "packages/react-router/lib/rsc/server.ssr.tsx",
+ "packages/react-router/lib/rsc/html-stream/browser.ts",
+ ],
+ "plugins": ["jsdoc"],
+ "rules": {
+ "jsdoc/check-access": "error",
+ "jsdoc/check-alignment": "error",
+ "jsdoc/check-param-names": "error",
+ "jsdoc/check-property-names": "error",
+ "jsdoc/check-tag-names": [
+ "error",
+ {
+ "definedTags": ["additionalExamples", "category", "mode"]
+ }
+ ],
+ "jsdoc/no-defaults": "error",
+ "jsdoc/no-multi-asterisks": ["error", { "allowWhitespace": true }],
+ "jsdoc/require-description": "error",
+ "jsdoc/require-param": ["error", { "enableRootFixer": false }],
+ "jsdoc/require-param-description": "error",
+ "jsdoc/require-param-name": "error",
+ "jsdoc/require-returns": "error",
+ "jsdoc/require-returns-check": "error",
+ "jsdoc/require-returns-description": "error",
+ "jsdoc/sort-tags": [
+ "error",
+ {
+ "tagSequence": [
+ {
+ "tags": ["description"]
+ },
+ {
+ "tags": ["example"]
+ },
+ {
+ "tags": ["additionalExamples"]
+ },
+ {
+ "tags": [
+ "name",
+ "public",
+ "private",
+ "category",
+ "mode",
+ "param",
+ "returns"
+ ]
+ }
+ ]
+ }
+ ]
+ }
}
],
"reportUnusedDisableDirectives": true
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 6b8e6fe5b9..4ef4cbaa8e 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -8,32 +8,31 @@ body:
value: |
Thank you for helping to improve React Router!
- ## Option 1: Submit a PR with a failing test
+ Please note that **all** bugs must have a **minimal** and **runnable** reproduction, meaning that it is not just pointing to a deployed site or a branch in your existing application, or showing a small code snippet. For more information, please refer to the [Bug/Issue Process Guidelines](https://github.com/remix-run/react-router/blob/main/GOVERNANCE.md#bugissue-process).
+
+ ## If you are using **Framework Mode**, you have 2 preferred options
+
+ ### Option 1 - Create a failing integration test
๐ The most helpful reproduction is to use our _bug report integration test_ template:
1. [Fork `remix-run/react-router`](https://github.com/remix-run/react-router/fork)
2. Open [`integration/bug-report-test.ts`](https://github.com/remix-run/react-router/blob/dev/integration/bug-report-test.ts) in your editor
- 3. Follow the instructions and submit a pull request with a failing bug report test!
-
- ## Option 2: Continue filling out this form
+ 3. Follow the instructions to create a failing bug report test
+ 4. Link to your forked branch with the failing test in this issue
- If you'd rather open a GitHub issue, here are other ways to share a reproduction (ordered from most helpful to least):
+ ### Option 2 - Create a **minimal** reproduction
- ๐ฅ Link to a [StackBlitz](https://reactrouter.com/new) environment
- - ๐ฅ Link to a GitHub repository
- - ๐ฅ Description of project including template, config files, `package.json` scripts, etc.
+ - ๐ฅ Link to a GitHub repository containing a minimal reproduction app
+
+ ## If you are using **Data** or **Declarative Mode**
+
+ Create a **minimal** reproduction
+
+ - ๐ฅ Link to a CodeSandbox repro: [TS](https://codesandbox.io/templates/react-vite-ts) | [JS](https://codesandbox.io/templates/react-vite)
+ - ๐ฅ Link to a GitHub repository containing a minimal reproduction app
- - type: dropdown
- id: mode
- attributes:
- label: I'm using React Router as a...
- description: See https://reactrouter.com/home for explanation
- options:
- - library
- - framework
- validations:
- required: true
- type: textarea
id: reproduction
attributes:
diff --git a/.github/workflows/close-feature-pr.yml b/.github/workflows/close-feature-pr.yml
new file mode 100644
index 0000000000..9dac3185c7
--- /dev/null
+++ b/.github/workflows/close-feature-pr.yml
@@ -0,0 +1,28 @@
+# Close a singular pull request that implements a feature that has not
+# gone through the Proposal process
+# Triggered by adding the `feature-request` label to an issue
+
+name: ๐ช Check Feature PR
+
+on:
+ pull_request:
+ types: [labeled]
+
+jobs:
+ close-feature-pr:
+ name: ๐ช Check Feature PR
+ if: github.repository == 'remix-run/react-router' && github.event.label.name == 'feature-request'
+ runs-on: ubuntu-latest
+ permissions:
+ pull-requests: write
+ steps:
+ - name: โฌ๏ธ Checkout repo
+ uses: actions/checkout@v5
+
+ - name: ๐ช Close PR
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ gh pr comment ${{ github.event.pull_request.number }} -F ./scripts/close-feature-pr.md
+ gh pr edit ${{ github.event.pull_request.number }} --remove-label ${{ github.event.label.name }}
+ gh pr close ${{ github.event.pull_request.number }}
diff --git a/.github/workflows/close-no-repro-issue.yml b/.github/workflows/close-no-repro-issue.yml
new file mode 100644
index 0000000000..c0e22f26ce
--- /dev/null
+++ b/.github/workflows/close-no-repro-issue.yml
@@ -0,0 +1,25 @@
+# Close a singular issue without a reproduction
+# Triggered by adding the `no-reproduction` label to an issue
+
+name: ๐ช Close issue without a reproduction
+
+on:
+ issues:
+ types: [labeled]
+
+jobs:
+ close-no-repro-issue:
+ name: ๐ช Close issue
+ if: github.repository == 'remix-run/react-router' && github.event.label.name == 'no-reproduction'
+ runs-on: ubuntu-latest
+ steps:
+ - name: โฌ๏ธ Checkout repo
+ uses: actions/checkout@v5
+
+ - name: ๐ช Close issue
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ gh issue comment ${{ github.event.issue.number }} -F ./scripts/close-no-repro-issue.md
+ gh issue edit ${{ github.event.issue.number }} --remove-label ${{ github.event.label.name }}
+ gh issue close ${{ github.event.issue.number }} -r "not planned";
diff --git a/.github/workflows/close-no-repro-issues.yml b/.github/workflows/close-no-repro-issues.yml
new file mode 100644
index 0000000000..ed616c6458
--- /dev/null
+++ b/.github/workflows/close-no-repro-issues.yml
@@ -0,0 +1,49 @@
+# This is a bulk-close script that was used initially to find and close issues
+# without a repro, but moving forward we'll likely use the singular version
+# (close-no-repro-issue.yml) on new issues which is driven by a label added to
+# the issue
+
+name: ๐ช Close issues without a reproduction
+
+on:
+ workflow_dispatch:
+ inputs:
+ dryRun:
+ type: boolean
+ description: "Dry Run? (no issues will be closed)"
+ default: false
+
+concurrency: ${{ github.workflow }}-${{ github.ref }}
+
+jobs:
+ close-no-repro-issues:
+ name: ๐ช Close issues
+ if: github.repository == 'remix-run/react-router'
+ runs-on: ubuntu-latest
+ env:
+ CI: "true"
+ GH_TOKEN: ${{ github.token }}
+ steps:
+ - name: โฌ๏ธ Checkout repo
+ uses: actions/checkout@v5
+
+ - name: ๐ฆ Setup pnpm
+ uses: pnpm/action-setup@v4.1.0
+
+ - name: โ Setup node
+ uses: actions/setup-node@v4
+ with:
+ # required for --experimental-strip-types
+ node-version: 22
+ cache: "pnpm"
+
+ - name: ๐ฅ Install deps
+ run: pnpm install --frozen-lockfile
+
+ - name: ๐ช Close Issues (Dry Run)
+ if: ${{ inputs.dryRun }}
+ run: node --experimental-strip-types ./scripts/close-no-repro-issues.ts --dryRun
+
+ - name: ๐ช Close Issues
+ if: ${{ ! inputs.dryRun }}
+ run: node --experimental-strip-types ./scripts/close-no-repro-issues.ts
diff --git a/.github/workflows/deduplicate-lock-file.yml b/.github/workflows/deduplicate-lock-file.yml
index 929df876e0..8398b6eada 100644
--- a/.github/workflows/deduplicate-lock-file.yml
+++ b/.github/workflows/deduplicate-lock-file.yml
@@ -5,20 +5,22 @@ on:
branches:
- dev
paths:
- - ./pnpm-lock.yaml
+ - pnpm-lock.yaml
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
- format:
+ dedupe:
if: github.repository == 'remix-run/react-router'
runs-on: ubuntu-latest
steps:
- name: โฌ๏ธ Checkout repo
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
+ with:
+ token: ${{ secrets.FORMAT_PAT }}
- name: ๐ฆ Setup pnpm
uses: pnpm/action-setup@v4
@@ -34,13 +36,14 @@ jobs:
- name: ๐ช Commit
run: |
- git config --local user.email "github-actions[bot]@users.noreply.github.com"
- git config --local user.name "github-actions[bot]"
+ git config --local user.email "hello@remix.run"
+ git config --local user.name "Remix Run Bot"
+
git add .
if [ -z "$(git status --porcelain)" ]; then
echo "๐ฟ no deduplication needed"
exit 0
fi
- git commit -m "chore: deduplicate `pnpm-lock.yaml`"
+ git commit -m "chore: deduplicate \`pnpm-lock.yaml\`"
git push
- echo "๐ฟ https://github.com/$GITHUB_REPOSITORY/commit/$(git rev-parse HEAD)"
+ echo "๐ฟ pushed dedupe changes https://github.com/$GITHUB_REPOSITORY/commit/$(git rev-parse HEAD)"
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 0000000000..6430bcfb06
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,67 @@
+name: ๐ Docs
+
+on:
+ push:
+ branches:
+ - main
+ - dev
+ paths:
+ - "packages/**/*.ts"
+ - "packages/**/*.tsx"
+ - "scripts/docs.ts"
+ workflow_dispatch:
+ inputs:
+ branch:
+ description: "Branch to generate docs on"
+ required: true
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ docs:
+ if: github.repository == 'remix-run/react-router'
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: โฌ๏ธ Checkout repo
+ uses: actions/checkout@v5
+ with:
+ token: ${{ secrets.FORMAT_PAT }}
+ ref: ${{ github.event.inputs.branch }}
+
+ - name: ๐ฆ Setup pnpm
+ uses: pnpm/action-setup@v4
+
+ - name: โ Setup node
+ uses: actions/setup-node@v4
+ with:
+ node-version-file: ".nvmrc"
+ cache: pnpm
+
+ - name: ๐ฅ Install deps
+ run: pnpm install --frozen-lockfile
+
+ - name: ๐ Build
+ run: pnpm build
+
+ - name: ๐ Generate Typedoc Docs
+ run: pnpm run docs:typedoc
+
+ - name: ๐ Generate Markdown Docs
+ run: pnpm run docs:jsdoc --write
+
+ - name: ๐ช Commit
+ run: |
+ git config --local user.email "hello@remix.run"
+ git config --local user.name "Remix Run Bot"
+
+ git add .
+ if [ -z "$(git status --porcelain)" ]; then
+ echo "๐ฟ no docs changed"
+ exit 0
+ fi
+ git commit -m "chore: generate markdown docs from jsdocs"
+ git push
+ echo "๐ฟ pushed docs changes https://github.com/$GITHUB_REPOSITORY/commit/$(git rev-parse HEAD)"
diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml
index cb7b558534..80094bc6a4 100644
--- a/.github/workflows/format.yml
+++ b/.github/workflows/format.yml
@@ -17,7 +17,7 @@ jobs:
steps:
- name: โฌ๏ธ Checkout repo
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
with:
token: ${{ secrets.FORMAT_PAT }}
diff --git a/.github/workflows/integration-full.yml b/.github/workflows/integration-full.yml
index fd2f79bfa9..cb1d064ee2 100644
--- a/.github/workflows/integration-full.yml
+++ b/.github/workflows/integration-full.yml
@@ -40,7 +40,7 @@ jobs:
uses: ./.github/workflows/shared-integration.yml
with:
os: "windows-latest"
- node_version: "[20, 22]"
+ node_version: "[22]"
browser: '["msedge"]'
integration-macos:
diff --git a/.github/workflows/integration-pr-windows-macos.yml b/.github/workflows/integration-pr-windows-macos.yml
index a36aef144d..f3d12c7681 100644
--- a/.github/workflows/integration-pr-windows-macos.yml
+++ b/.github/workflows/integration-pr-windows-macos.yml
@@ -32,7 +32,7 @@ jobs:
os: "windows-latest"
node_version: "[22]"
browser: '["msedge"]'
- timeout: 60
+ timeout: 120
integration-webkit:
name: "๐ Integration Test"
@@ -42,3 +42,4 @@ jobs:
os: "macos-latest"
node_version: "[22]"
browser: '["webkit"]'
+ timeout: 40
diff --git a/.github/workflows/release-comments.yml b/.github/workflows/release-comments.yml
index 4ba2af6e93..1b37a5771f 100644
--- a/.github/workflows/release-comments.yml
+++ b/.github/workflows/release-comments.yml
@@ -10,12 +10,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: โฌ๏ธ Checkout repo
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
with:
fetch-depth: 0
- name: ๐ Comment on related issues and pull requests
- uses: remix-run/release-comment-action@v0.4.1
+ uses: remix-run/release-comment-action@v0.4.2
with:
DIRECTORY_TO_CHECK: "./packages"
PACKAGE_NAME: "react-router"
diff --git a/.github/workflows/release-experimental.yml b/.github/workflows/release-experimental.yml
index 0a9437dc89..4ebdd0636a 100644
--- a/.github/workflows/release-experimental.yml
+++ b/.github/workflows/release-experimental.yml
@@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: โฌ๏ธ Checkout repo
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
with:
ref: ${{ github.event.inputs.branch }}
# checkout using a custom token so that we can push later on
diff --git a/.github/workflows/release-nightly.yml b/.github/workflows/release-nightly.yml
index 62d5db855b..64419f5a2f 100644
--- a/.github/workflows/release-nightly.yml
+++ b/.github/workflows/release-nightly.yml
@@ -29,7 +29,7 @@ jobs:
NEXT_VERSION: ${{ steps.version.outputs.NEXT_VERSION }}
steps:
- name: โฌ๏ธ Checkout repo
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
with:
ref: dev
# checkout using a custom token so that we can push later on
diff --git a/.github/workflows/release-stage-2-alpha.yml b/.github/workflows/release-stage-2-alpha.yml
new file mode 100644
index 0000000000..85e41296d4
--- /dev/null
+++ b/.github/workflows/release-stage-2-alpha.yml
@@ -0,0 +1,86 @@
+name: ๐งช Check Alpha Release
+
+on:
+ pull_request:
+ types: [labeled]
+
+concurrency: ${{ github.workflow }}-${{ github.ref }}
+
+env:
+ CI: true
+
+jobs:
+ alpha-release:
+ name: ๐งช Check Alpha Release
+ if: github.repository == 'remix-run/react-router' && github.event.label.name == 'alpha-release'
+ runs-on: ubuntu-latest
+ steps:
+ - name: ๐ Log Info
+ run: |
+ echo "Label: ${{ github.event.label.name }}"
+ echo "Branch: ${{ github.event.pull_request.head.ref }}"
+ echo "SHA: ${{ github.event.pull_request.head.sha }}"
+
+ - name: โฌ๏ธ Checkout repo
+ uses: actions/checkout@v5
+ with:
+ ref: ${{ github.event.pull_request.head.sha }}
+
+ - name: Log Git Info
+ run: |
+ git log -n 1
+ git status
+
+ - name: ๐ฆ Setup pnpm
+ uses: pnpm/action-setup@v4
+
+ - name: โ Setup node
+ uses: actions/setup-node@v4
+ with:
+ node-version-file: ".nvmrc"
+ cache: "pnpm"
+
+ - name: ๐ฅ Install deps
+ run: pnpm install --frozen-lockfile
+
+ - name: โคด๏ธ Update version
+ id: version
+ run: |
+ git config --local user.email "hello@remix.run"
+ git config --local user.name "Remix Run Bot"
+ SHORT_SHA=$(git rev-parse --short HEAD)
+ NEXT_VERSION=0.0.0-experimental-${SHORT_SHA}
+ git checkout -b experimental/${NEXT_VERSION}
+ pnpm run version ${NEXT_VERSION}
+ echo "version=${NEXT_VERSION}" >> "$GITHUB_OUTPUT"
+
+ - name: ๐ Build
+ run: pnpm build
+
+ - name: ๐ Setup npm auth
+ run: |
+ echo "registry=https://registry.npmjs.org" >> ~/.npmrc
+ echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" >> ~/.npmrc
+
+ - name: ๐ Publish
+ run: pnpm run publish
+
+ - name: ๐ฌ Comment
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ LATEST_RELEASE_SHA=$(gh release list --limit 1 --json tagName --jq ".[0].tagName")
+ BASE_SHA=$(echo ${{ github.event.pull_request.base.sha }} | cut -c1-7)
+ COMMAND="git log --pretty=oneline ${LATEST_RELEASE_SHA}..${BASE_SHA}"
+ echo -e \
+ "[Alpha release](https://github.com/remix-run/react-router/blob/main/GOVERNANCE.md#stage-2--alpha) \
+ created: \`${{ steps.version.outputs.version }}\`\n\n \
+ โ ๏ธ **Note:** This release was created from the \`HEAD\` of this branch so it \
+ may contain commits that have landed in \`dev\` but have not been released yet \
+ depending on when this branch was created. You can run the following command \
+ to see the commits that may not have been released yet:\n\n \
+ \`\`\`bash\n \
+ ${COMMAND}\n \
+ \`\`\`" \
+ | gh pr comment ${{ github.event.pull_request.number }} --body-file -
+ gh pr edit ${{ github.event.pull_request.number }} --remove-label ${{ github.event.label.name }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 6c65a6d930..d4284459ad 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -21,9 +21,13 @@ jobs:
outputs:
published_packages: ${{ steps.changesets.outputs.publishedPackages }}
published: ${{ steps.changesets.outputs.published }}
+ permissions:
+ contents: write # enable pushing changes to the origin
+ id-token: write # enable generation of an ID token for publishing
+ pull-requests: write # enable opening a PR for the release
steps:
- name: โฌ๏ธ Checkout repo
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
with:
fetch-depth: 0
@@ -51,7 +55,9 @@ jobs:
# publish to npm.
- name: ๐ PR / Publish
id: changesets
- uses: changesets/action@v1
+ # PLEASE KEEP THIS PINNED TO 1.4.10 to avoid a regression in 1.5.*
+ # See https://github.com/changesets/action/issues/465
+ uses: changesets/action@v1.4.10
with:
version: pnpm run changeset:version
commit: "chore: Update version for release"
@@ -60,6 +66,7 @@ jobs:
createGithubReleases: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ NPM_CONFIG_PROVENANCE: true
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
find_package_version:
@@ -71,7 +78,7 @@ jobs:
package_version: ${{ steps.find_package_version.outputs.package_version }}
steps:
- name: โฌ๏ธ Checkout repo
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
- name: ๐ฆ Setup pnpm
uses: pnpm/action-setup@v4
@@ -94,4 +101,7 @@ jobs:
name: ๐ Comment on related issues and pull requests
if: github.repository == 'remix-run/react-router' && github.ref_name != 'release-v6' && needs.find_package_version.outputs.package_version != ''
needs: [release, find_package_version]
+ permissions:
+ issues: write # enable commenting on released issues
+ pull-requests: write # enable commenting on released pull requests
uses: ./.github/workflows/release-comments.yml
diff --git a/.github/workflows/shared-build.yml b/.github/workflows/shared-build.yml
index 6de9e0c61f..49c9c41ca2 100644
--- a/.github/workflows/shared-build.yml
+++ b/.github/workflows/shared-build.yml
@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: โฌ๏ธ Checkout repo
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
- name: ๐ฆ Setup pnpm
uses: pnpm/action-setup@v4
diff --git a/.github/workflows/shared-integration.yml b/.github/workflows/shared-integration.yml
index e7adb0dd47..62da5c5a35 100644
--- a/.github/workflows/shared-integration.yml
+++ b/.github/workflows/shared-integration.yml
@@ -21,7 +21,7 @@ on:
timeout:
required: false
type: number
- default: 30
+ default: 40
env:
CI: true
@@ -38,7 +38,7 @@ jobs:
runs-on: ${{ inputs.os }}
steps:
- name: โฌ๏ธ Checkout repo
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
- name: ๐ฆ Setup pnpm
uses: pnpm/action-setup@v4
diff --git a/.github/workflows/support.yml b/.github/workflows/support.yml
index 8a4cf0c8c2..be506d9b36 100644
--- a/.github/workflows/support.yml
+++ b/.github/workflows/support.yml
@@ -11,7 +11,7 @@ jobs:
action:
runs-on: ubuntu-latest
steps:
- - uses: dessant/support-requests@v3
+ - uses: dessant/support-requests@v4
with:
issue-comment: >
:wave: @{issue-author}, we use the issue tracker exclusively for bug reports
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index af709fdaaa..12588257c9 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -33,7 +33,7 @@ jobs:
steps:
- name: โฌ๏ธ Checkout repo
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
- name: ๐ฆ Setup pnpm
uses: pnpm/action-setup@v4
diff --git a/.gitignore b/.gitignore
index 6e4cc73bf4..53e9a5baf1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,9 +27,11 @@ node_modules/
.wireit
.eslintcache
+.parcel-cache
.tmp
tsup.config.bundled_*.mjs
build.utils.d.ts
+worker-configuration.d.ts
/.env
/NOTES.md
diff --git a/.nvmrc b/.nvmrc
index 2edeafb09d..8fdd954df9 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-20
\ No newline at end of file
+22
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7e020d67eb..92acf7ff40 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,75 +13,106 @@ We manage release notes in this file instead of the paginated Github Releases Pa
Table of Contents
- [React Router Releases](#react-router-releases)
- - [v7.6.0](#v760)
- - [What's Changed](#whats-changed)
- - [`routeDiscovery` Config Option](#routediscovery-config-option)
- - [Automatic Types for Future Flags](#automatic-types-for-future-flags)
- - [Minor Changes](#minor-changes)
+ - [v7.8.2](#v782)
- [Patch Changes](#patch-changes)
- [Unstable Changes](#unstable-changes)
- - [Changes by Package](#changes-by-package)
- - [v7.5.3](#v753)
+ - [v7.8.1](#v781)
- [Patch Changes](#patch-changes-1)
- - [v7.5.2](#v752)
- - [Security Notice](#security-notice)
+ - [Unstable Changes](#unstable-changes-1)
+ - [v7.8.0](#v780)
+ - [What's Changed](#whats-changed)
+ - [Consistently named `loaderData` values](#consistently-named-loaderdata-values)
+ - [Improvements/fixes to the middleware APIs (unstable)](#improvementsfixes-to-the-middleware-apis-unstable)
+ - [Minor Changes](#minor-changes)
- [Patch Changes](#patch-changes-2)
- - [v7.5.1](#v751)
+ - [Unstable Changes](#unstable-changes-2)
+ - [Changes by Package](#changes-by-package)
+ - [v7.7.1](#v771)
- [Patch Changes](#patch-changes-3)
- - [Unstable Changes](#unstable-changes-1)
- - [v7.5.0](#v750)
+ - [Unstable Changes](#unstable-changes-3)
+ - [v7.7.0](#v770)
- [What's Changed](#whats-changed-1)
- - [`route.lazy` Object API](#routelazy-object-api)
+ - [Unstable RSC APIs](#unstable-rsc-apis)
- [Minor Changes](#minor-changes-1)
- [Patch Changes](#patch-changes-4)
- - [Unstable Changes](#unstable-changes-2)
+ - [Unstable Changes](#unstable-changes-4)
- [Changes by Package](#changes-by-package-1)
- - [v7.4.1](#v741)
- - [Security Notice](#security-notice-1)
+ - [v7.6.3](#v763)
- [Patch Changes](#patch-changes-5)
- - [Unstable Changes](#unstable-changes-3)
- - [v7.4.0](#v740)
- - [Minor Changes](#minor-changes-2)
+ - [v7.6.2](#v762)
- [Patch Changes](#patch-changes-6)
- - [Unstable Changes](#unstable-changes-4)
- - [Changes by Package](#changes-by-package-2)
- - [v7.3.0](#v730)
- - [Minor Changes](#minor-changes-3)
+ - [v7.6.1](#v761)
- [Patch Changes](#patch-changes-7)
- [Unstable Changes](#unstable-changes-5)
+ - [v7.6.0](#v760)
+ - [What's Changed](#whats-changed-2)
+ - [`routeDiscovery` Config Option](#routediscovery-config-option)
+ - [Automatic Types for Future Flags](#automatic-types-for-future-flags)
+ - [Minor Changes](#minor-changes-2)
+ - [Patch Changes](#patch-changes-8)
+ - [Unstable Changes](#unstable-changes-6)
+ - [Changes by Package](#changes-by-package-2)
+ - [v7.5.3](#v753)
+ - [Patch Changes](#patch-changes-9)
+ - [v7.5.2](#v752)
+ - [Security Notice](#security-notice)
+ - [Patch Changes](#patch-changes-10)
+ - [v7.5.1](#v751)
+ - [Patch Changes](#patch-changes-11)
+ - [Unstable Changes](#unstable-changes-7)
+ - [v7.5.0](#v750)
+ - [What's Changed](#whats-changed-3)
+ - [`route.lazy` Object API](#routelazy-object-api)
+ - [Minor Changes](#minor-changes-3)
+ - [Patch Changes](#patch-changes-12)
+ - [Unstable Changes](#unstable-changes-8)
+ - [Changes by Package](#changes-by-package-3)
+ - [v7.4.1](#v741)
+ - [Security Notice](#security-notice-1)
+ - [Patch Changes](#patch-changes-13)
+ - [Unstable Changes](#unstable-changes-9)
+ - [v7.4.0](#v740)
+ - [Minor Changes](#minor-changes-4)
+ - [Patch Changes](#patch-changes-14)
+ - [Unstable Changes](#unstable-changes-10)
+ - [Changes by Package](#changes-by-package-4)
+ - [v7.3.0](#v730)
+ - [Minor Changes](#minor-changes-5)
+ - [Patch Changes](#patch-changes-15)
+ - [Unstable Changes](#unstable-changes-11)
- [Client-side `context` (unstable)](#client-side-context-unstable)
- [Middleware (unstable)](#middleware-unstable)
- [Middleware `context` parameter](#middleware-context-parameter)
- [`unstable_SerializesTo`](#unstable_serializesto)
- - [Changes by Package](#changes-by-package-3)
+ - [Changes by Package](#changes-by-package-5)
- [v7.2.0](#v720)
- - [What's Changed](#whats-changed-2)
+ - [What's Changed](#whats-changed-4)
- [Type-safe `href` utility](#type-safe-href-utility)
- [Prerendering with a SPA Fallback](#prerendering-with-a-spa-fallback)
- [Allow a root `loader` in SPA Mode](#allow-a-root-loader-in-spa-mode)
- - [Minor Changes](#minor-changes-4)
- - [Patch Changes](#patch-changes-8)
- - [Unstable Changes](#unstable-changes-6)
+ - [Minor Changes](#minor-changes-6)
+ - [Patch Changes](#patch-changes-16)
+ - [Unstable Changes](#unstable-changes-12)
- [Split Route Modules (unstable)](#split-route-modules-unstable)
- - [Changes by Package](#changes-by-package-4)
+ - [Changes by Package](#changes-by-package-6)
- [v7.1.5](#v715)
- - [Patch Changes](#patch-changes-9)
+ - [Patch Changes](#patch-changes-17)
- [v7.1.4](#v714)
- - [Patch Changes](#patch-changes-10)
+ - [Patch Changes](#patch-changes-18)
- [v7.1.3](#v713)
- - [Patch Changes](#patch-changes-11)
+ - [Patch Changes](#patch-changes-19)
- [v7.1.2](#v712)
- - [Patch Changes](#patch-changes-12)
+ - [Patch Changes](#patch-changes-20)
- [v7.1.1](#v711)
- - [Patch Changes](#patch-changes-13)
+ - [Patch Changes](#patch-changes-21)
- [v7.1.0](#v710)
- - [Minor Changes](#minor-changes-5)
- - [Patch Changes](#patch-changes-14)
- - [Changes by Package](#changes-by-package-5)
+ - [Minor Changes](#minor-changes-7)
+ - [Patch Changes](#patch-changes-22)
+ - [Changes by Package](#changes-by-package-7)
- [v7.0.2](#v702)
- - [Patch Changes](#patch-changes-15)
+ - [Patch Changes](#patch-changes-23)
- [v7.0.1](#v701)
- - [Patch Changes](#patch-changes-16)
+ - [Patch Changes](#patch-changes-24)
- [v7.0.0](#v700)
- [Breaking Changes](#breaking-changes)
- [Package Restructuring](#package-restructuring)
@@ -93,204 +124,206 @@ We manage release notes in this file instead of the paginated Github Releases Pa
- [Exposed Router Promises](#exposed-router-promises)
- [Other Notable Changes](#other-notable-changes)
- [`routes.ts`](#routests)
- - [Typesafety improvements](#typesafety-improvements)
+ - [Type-safety improvements](#type-safety-improvements)
- [Prerendering](#prerendering)
- [Major Changes (`react-router`)](#major-changes-react-router)
- [Major Changes (`@react-router/*`)](#major-changes-react-router-1)
- - [Minor Changes](#minor-changes-6)
- - [Patch Changes](#patch-changes-17)
- - [Changes by Package](#changes-by-package-6)
+ - [Minor Changes](#minor-changes-8)
+ - [Patch Changes](#patch-changes-25)
+ - [Changes by Package](#changes-by-package-8)
- [React Router v6 Releases](#react-router-v6-releases)
+ - [v6.30.1](#v6301)
+ - [Patch Changes](#patch-changes-26)
- [v6.30.0](#v6300)
- - [Minor Changes](#minor-changes-7)
- - [Patch Changes](#patch-changes-18)
+ - [Minor Changes](#minor-changes-9)
+ - [Patch Changes](#patch-changes-27)
- [v6.29.0](#v6290)
- - [Minor Changes](#minor-changes-8)
- - [Patch Changes](#patch-changes-19)
+ - [Minor Changes](#minor-changes-10)
+ - [Patch Changes](#patch-changes-28)
- [v6.28.2](#v6282)
- - [Patch Changes](#patch-changes-20)
+ - [Patch Changes](#patch-changes-29)
- [v6.28.1](#v6281)
- - [Patch Changes](#patch-changes-21)
+ - [Patch Changes](#patch-changes-30)
- [v6.28.0](#v6280)
- - [What's Changed](#whats-changed-3)
- - [Minor Changes](#minor-changes-9)
- - [Patch Changes](#patch-changes-22)
+ - [What's Changed](#whats-changed-5)
+ - [Minor Changes](#minor-changes-11)
+ - [Patch Changes](#patch-changes-31)
- [v6.27.0](#v6270)
- - [What's Changed](#whats-changed-4)
+ - [What's Changed](#whats-changed-6)
- [Stabilized APIs](#stabilized-apis)
- - [Minor Changes](#minor-changes-10)
- - [Patch Changes](#patch-changes-23)
+ - [Minor Changes](#minor-changes-12)
+ - [Patch Changes](#patch-changes-32)
- [v6.26.2](#v6262)
- - [Patch Changes](#patch-changes-24)
+ - [Patch Changes](#patch-changes-33)
- [v6.26.1](#v6261)
- - [Patch Changes](#patch-changes-25)
+ - [Patch Changes](#patch-changes-34)
- [v6.26.0](#v6260)
- - [Minor Changes](#minor-changes-11)
- - [Patch Changes](#patch-changes-26)
+ - [Minor Changes](#minor-changes-13)
+ - [Patch Changes](#patch-changes-35)
- [v6.25.1](#v6251)
- - [Patch Changes](#patch-changes-27)
+ - [Patch Changes](#patch-changes-36)
- [v6.25.0](#v6250)
- - [What's Changed](#whats-changed-5)
+ - [What's Changed](#whats-changed-7)
- [Stabilized `v7_skipActionErrorRevalidation`](#stabilized-v7_skipactionerrorrevalidation)
- - [Minor Changes](#minor-changes-12)
- - [Patch Changes](#patch-changes-28)
+ - [Minor Changes](#minor-changes-14)
+ - [Patch Changes](#patch-changes-37)
- [v6.24.1](#v6241)
- - [Patch Changes](#patch-changes-29)
+ - [Patch Changes](#patch-changes-38)
- [v6.24.0](#v6240)
- - [What's Changed](#whats-changed-6)
+ - [What's Changed](#whats-changed-8)
- [Lazy Route Discovery (a.k.a. "Fog of War")](#lazy-route-discovery-aka-fog-of-war)
- - [Minor Changes](#minor-changes-13)
- - [Patch Changes](#patch-changes-30)
+ - [Minor Changes](#minor-changes-15)
+ - [Patch Changes](#patch-changes-39)
- [v6.23.1](#v6231)
- - [Patch Changes](#patch-changes-31)
+ - [Patch Changes](#patch-changes-40)
- [v6.23.0](#v6230)
- - [What's Changed](#whats-changed-7)
+ - [What's Changed](#whats-changed-9)
- [Data Strategy (unstable)](#data-strategy-unstable)
- [Skip Action Error Revalidation (unstable)](#skip-action-error-revalidation-unstable)
- - [Minor Changes](#minor-changes-14)
+ - [Minor Changes](#minor-changes-16)
- [v6.22.3](#v6223)
- - [Patch Changes](#patch-changes-32)
+ - [Patch Changes](#patch-changes-41)
- [v6.22.2](#v6222)
- - [Patch Changes](#patch-changes-33)
+ - [Patch Changes](#patch-changes-42)
- [v6.22.1](#v6221)
- - [Patch Changes](#patch-changes-34)
+ - [Patch Changes](#patch-changes-43)
- [v6.22.0](#v6220)
- - [What's Changed](#whats-changed-8)
+ - [What's Changed](#whats-changed-10)
- [Core Web Vitals Technology Report Flag](#core-web-vitals-technology-report-flag)
- - [Minor Changes](#minor-changes-15)
- - [Patch Changes](#patch-changes-35)
+ - [Minor Changes](#minor-changes-17)
+ - [Patch Changes](#patch-changes-44)
- [v6.21.3](#v6213)
- - [Patch Changes](#patch-changes-36)
+ - [Patch Changes](#patch-changes-45)
- [v6.21.2](#v6212)
- - [Patch Changes](#patch-changes-37)
+ - [Patch Changes](#patch-changes-46)
- [v6.21.1](#v6211)
- - [Patch Changes](#patch-changes-38)
+ - [Patch Changes](#patch-changes-47)
- [v6.21.0](#v6210)
- - [What's Changed](#whats-changed-9)
+ - [What's Changed](#whats-changed-11)
- [`future.v7_relativeSplatPath`](#futurev7_relativesplatpath)
- [Partial Hydration](#partial-hydration)
- - [Minor Changes](#minor-changes-16)
- - [Patch Changes](#patch-changes-39)
+ - [Minor Changes](#minor-changes-18)
+ - [Patch Changes](#patch-changes-48)
- [v6.20.1](#v6201)
- - [Patch Changes](#patch-changes-40)
+ - [Patch Changes](#patch-changes-49)
- [v6.20.0](#v6200)
- - [Minor Changes](#minor-changes-17)
- - [Patch Changes](#patch-changes-41)
+ - [Minor Changes](#minor-changes-19)
+ - [Patch Changes](#patch-changes-50)
- [v6.19.0](#v6190)
- - [What's Changed](#whats-changed-10)
+ - [What's Changed](#whats-changed-12)
- [`unstable_flushSync` API](#unstable_flushsync-api)
- - [Minor Changes](#minor-changes-18)
- - [Patch Changes](#patch-changes-42)
+ - [Minor Changes](#minor-changes-20)
+ - [Patch Changes](#patch-changes-51)
- [v6.18.0](#v6180)
- - [What's Changed](#whats-changed-11)
+ - [What's Changed](#whats-changed-13)
- [New Fetcher APIs](#new-fetcher-apis)
- [Persistence Future Flag (`future.v7_fetcherPersist`)](#persistence-future-flag-futurev7_fetcherpersist)
- - [Minor Changes](#minor-changes-19)
- - [Patch Changes](#patch-changes-43)
+ - [Minor Changes](#minor-changes-21)
+ - [Patch Changes](#patch-changes-52)
- [v6.17.0](#v6170)
- - [What's Changed](#whats-changed-12)
+ - [What's Changed](#whats-changed-14)
- [View Transitions ๐](#view-transitions-)
- - [Minor Changes](#minor-changes-20)
- - [Patch Changes](#patch-changes-44)
+ - [Minor Changes](#minor-changes-22)
+ - [Patch Changes](#patch-changes-53)
- [v6.16.0](#v6160)
- - [Minor Changes](#minor-changes-21)
- - [Patch Changes](#patch-changes-45)
+ - [Minor Changes](#minor-changes-23)
+ - [Patch Changes](#patch-changes-54)
- [v6.15.0](#v6150)
- - [Minor Changes](#minor-changes-22)
- - [Patch Changes](#patch-changes-46)
+ - [Minor Changes](#minor-changes-24)
+ - [Patch Changes](#patch-changes-55)
- [v6.14.2](#v6142)
- - [Patch Changes](#patch-changes-47)
+ - [Patch Changes](#patch-changes-56)
- [v6.14.1](#v6141)
- - [Patch Changes](#patch-changes-48)
+ - [Patch Changes](#patch-changes-57)
- [v6.14.0](#v6140)
- - [What's Changed](#whats-changed-13)
+ - [What's Changed](#whats-changed-15)
- [JSON/Text Submissions](#jsontext-submissions)
- - [Minor Changes](#minor-changes-23)
- - [Patch Changes](#patch-changes-49)
+ - [Minor Changes](#minor-changes-25)
+ - [Patch Changes](#patch-changes-58)
- [v6.13.0](#v6130)
- - [What's Changed](#whats-changed-14)
+ - [What's Changed](#whats-changed-16)
- [`future.v7_startTransition`](#futurev7_starttransition)
- - [Minor Changes](#minor-changes-24)
- - [Patch Changes](#patch-changes-50)
+ - [Minor Changes](#minor-changes-26)
+ - [Patch Changes](#patch-changes-59)
- [v6.12.1](#v6121)
- - [Patch Changes](#patch-changes-51)
+ - [Patch Changes](#patch-changes-60)
- [v6.12.0](#v6120)
- - [What's Changed](#whats-changed-15)
+ - [What's Changed](#whats-changed-17)
- [`React.startTransition` support](#reactstarttransition-support)
- - [Minor Changes](#minor-changes-25)
- - [Patch Changes](#patch-changes-52)
+ - [Minor Changes](#minor-changes-27)
+ - [Patch Changes](#patch-changes-61)
- [v6.11.2](#v6112)
- - [Patch Changes](#patch-changes-53)
+ - [Patch Changes](#patch-changes-62)
- [v6.11.1](#v6111)
- - [Patch Changes](#patch-changes-54)
+ - [Patch Changes](#patch-changes-63)
- [v6.11.0](#v6110)
- - [Minor Changes](#minor-changes-26)
- - [Patch Changes](#patch-changes-55)
+ - [Minor Changes](#minor-changes-28)
+ - [Patch Changes](#patch-changes-64)
- [v6.10.0](#v6100)
- - [What's Changed](#whats-changed-16)
- - [Minor Changes](#minor-changes-27)
+ - [What's Changed](#whats-changed-18)
+ - [Minor Changes](#minor-changes-29)
- [`future.v7_normalizeFormMethod`](#futurev7_normalizeformmethod)
- - [Patch Changes](#patch-changes-56)
+ - [Patch Changes](#patch-changes-65)
- [v6.9.0](#v690)
- - [What's Changed](#whats-changed-17)
+ - [What's Changed](#whats-changed-19)
- [`Component`/`ErrorBoundary` route properties](#componenterrorboundary-route-properties)
- [Introducing Lazy Route Modules](#introducing-lazy-route-modules)
- - [Minor Changes](#minor-changes-28)
- - [Patch Changes](#patch-changes-57)
+ - [Minor Changes](#minor-changes-30)
+ - [Patch Changes](#patch-changes-66)
- [v6.8.2](#v682)
- - [Patch Changes](#patch-changes-58)
+ - [Patch Changes](#patch-changes-67)
- [v6.8.1](#v681)
- - [Patch Changes](#patch-changes-59)
+ - [Patch Changes](#patch-changes-68)
- [v6.8.0](#v680)
- - [Minor Changes](#minor-changes-29)
- - [Patch Changes](#patch-changes-60)
+ - [Minor Changes](#minor-changes-31)
+ - [Patch Changes](#patch-changes-69)
- [v6.7.0](#v670)
- - [Minor Changes](#minor-changes-30)
- - [Patch Changes](#patch-changes-61)
+ - [Minor Changes](#minor-changes-32)
+ - [Patch Changes](#patch-changes-70)
- [v6.6.2](#v662)
- - [Patch Changes](#patch-changes-62)
+ - [Patch Changes](#patch-changes-71)
- [v6.6.1](#v661)
- - [Patch Changes](#patch-changes-63)
+ - [Patch Changes](#patch-changes-72)
- [v6.6.0](#v660)
- - [What's Changed](#whats-changed-18)
- - [Minor Changes](#minor-changes-31)
- - [Patch Changes](#patch-changes-64)
+ - [What's Changed](#whats-changed-20)
+ - [Minor Changes](#minor-changes-33)
+ - [Patch Changes](#patch-changes-73)
- [v6.5.0](#v650)
- - [What's Changed](#whats-changed-19)
- - [Minor Changes](#minor-changes-32)
- - [Patch Changes](#patch-changes-65)
+ - [What's Changed](#whats-changed-21)
+ - [Minor Changes](#minor-changes-34)
+ - [Patch Changes](#patch-changes-74)
- [v6.4.5](#v645)
- - [Patch Changes](#patch-changes-66)
+ - [Patch Changes](#patch-changes-75)
- [v6.4.4](#v644)
- - [Patch Changes](#patch-changes-67)
+ - [Patch Changes](#patch-changes-76)
- [v6.4.3](#v643)
- - [Patch Changes](#patch-changes-68)
+ - [Patch Changes](#patch-changes-77)
- [v6.4.2](#v642)
- - [Patch Changes](#patch-changes-69)
+ - [Patch Changes](#patch-changes-78)
- [v6.4.1](#v641)
- - [Patch Changes](#patch-changes-70)
+ - [Patch Changes](#patch-changes-79)
- [v6.4.0](#v640)
- - [What's Changed](#whats-changed-20)
+ - [What's Changed](#whats-changed-22)
- [Remix Data APIs](#remix-data-apis)
- - [Patch Changes](#patch-changes-71)
+ - [Patch Changes](#patch-changes-80)
- [v6.3.0](#v630)
- - [Minor Changes](#minor-changes-33)
+ - [Minor Changes](#minor-changes-35)
- [v6.2.2](#v622)
- - [Patch Changes](#patch-changes-72)
+ - [Patch Changes](#patch-changes-81)
- [v6.2.1](#v621)
- - [Patch Changes](#patch-changes-73)
+ - [Patch Changes](#patch-changes-82)
- [v6.2.0](#v620)
- - [Minor Changes](#minor-changes-34)
- - [Patch Changes](#patch-changes-74)
+ - [Minor Changes](#minor-changes-36)
+ - [Patch Changes](#patch-changes-83)
- [v6.1.1](#v611)
- - [Patch Changes](#patch-changes-75)
+ - [Patch Changes](#patch-changes-84)
- [v6.1.0](#v610)
- - [Minor Changes](#minor-changes-35)
- - [Patch Changes](#patch-changes-76)
+ - [Minor Changes](#minor-changes-37)
+ - [Patch Changes](#patch-changes-85)
- [v6.0.2](#v602)
- - [Patch Changes](#patch-changes-77)
+ - [Patch Changes](#patch-changes-86)
- [v6.0.1](#v601)
- - [Patch Changes](#patch-changes-78)
+ - [Patch Changes](#patch-changes-87)
- [v6.0.0](#v600)
@@ -315,7 +348,6 @@ Date: YYYY-MM-DD
โ ๏ธ _[Unstable features](https://reactrouter.com/community/api-development-strategy#unstable-flags) are not recommended for production use_
-
### Changes by Package
- [`create-react-router`](https://github.com/remix-run/react-router/blob/react-router%407.X.Y/packages/create-react-router/CHANGELOG.md#7XY)
@@ -332,6 +364,501 @@ Date: YYYY-MM-DD
**Full Changelog**: [`v7.X.Y...v7.X.Y`](https://github.com/remix-run/react-router/compare/react-router@7.X.Y...react-router@7.X.Y)
-->
+## v7.8.2
+
+Date: 2025-08-22
+
+### Patch Changes
+
+- `react-router` - Maintain `ReadonlyMap` and `ReadonlySet` types in server response data. ([#13092](https://github.com/remix-run/react-router/pull/13092))
+- `react-router` - Fix `basename` usage without a leading slash in data routers ([#11671](https://github.com/remix-run/react-router/pull/11671))
+- `react-router` - Fix `TypeError` if you throw from `patchRoutesOnNavigation` when no partial matches exist ([#14198](https://github.com/remix-run/react-router/pull/14198))
+- `react-router` - Properly escape interpolated param values in `generatePath()` ([#13530](https://github.com/remix-run/react-router/pull/13530))
+- `@react-router/dev` - Fix potential memory leak in default `entry.server` ([#14200](https://github.com/remix-run/react-router/pull/14200))
+
+### Unstable Changes
+
+โ ๏ธ _[Unstable features](https://reactrouter.com/community/api-development-strategy#unstable-flags) are not recommended for production use_
+
+**Client-side `onError`**
+
+- `react-router` - Add ``/`` prop for client side error reporting ([#14162](https://github.com/remix-run/react-router/pull/14162))
+
+**Middleware**
+
+- `react-router` - Delay serialization of `.data` redirects to 202 responses until after middleware chain ([#14205](https://github.com/remix-run/react-router/pull/14205))
+- `react-router` - Update client middleware so it returns the `dataStrategy` results up the chain allowing for more advanced post-processing middleware ([#14151](https://github.com/remix-run/react-router/pull/14151), [#14212](https://github.com/remix-run/react-router/pull/14212))
+- `react-router` - Remove Data Mode `future.unstable_middleware` flag from `createBrowserRouter` ([#14213](https://github.com/remix-run/react-router/pull/14213))
+ - This is only needed as a Framework Mode flag because of the route modules and the `getLoadContext` type behavior change
+ - In Data Mode, it's an opt-in feature because it's just a new property on a route object, so there's no behavior changes that necessitate a flag
+
+**RSC**
+
+- `react-router` - Allow opting out of revalidation on server actions with hidden `$SKIP_REVALIDATION` input ([#14154](https://github.com/remix-run/react-router/pull/14154))
+
+**Full Changelog**: [`v7.8.1...v7.8.2`](https://github.com/remix-run/react-router/compare/react-router@7.8.1...react-router@7.8.2)
+
+## v7.8.1
+
+Date: 2025-08-15
+
+### Patch Changes
+
+- `react-router` - Fix usage of optional path segments in nested routes defined using absolute paths ([#14135](https://github.com/remix-run/react-router/pull/14135))
+- `react-router` - Fix optional static segment matching in `matchPath` ([#11813](https://github.com/remix-run/react-router/pull/11813))
+- `react-router` - Fix pre-rendering when a `basename` is set with `ssr:false` ([#13791](https://github.com/remix-run/react-router/pull/13791))
+- `react-router` - Properly convert returned/thrown `data()` values to `Response` instances via `Response.json()` in resource routes and middleware ([#14159](https://github.com/remix-run/react-router/pull/14159), [#14181](https://github.com/remix-run/react-router/pull/14181))
+- `@react-router/dev` - Update generated `Route.MetaArgs` type so `loaderData` is only potentially undefined when an `ErrorBoundary` export is present ([#14173](https://github.com/remix-run/react-router/pull/14173))
+
+### Unstable Changes
+
+โ ๏ธ _[Unstable features](https://reactrouter.com/community/api-development-strategy#unstable-flags) are not recommended for production use_
+
+**Middleware**
+
+- `react-router` - Bubble client pre-`next` middleware errors to the shallowest ancestor that needs to load, not strictly the shallowest ancestor with a loader ([#14150](https://github.com/remix-run/react-router/pull/14150))
+- `react-router` - Propagate non-redirect `Response` values thrown from middleware to the error boundary on document/data requests ([#14182](https://github.com/remix-run/react-router/pull/14182))
+
+**RSC**
+
+- `react-router` - Provide `isRouteErrorResponse` utility in `react-server` environments ([#14166](https://github.com/remix-run/react-router/pull/14166))
+- `react-router` - Handle `meta` and `links` Route Exports in RSC Data Mode ([#14136](https://github.com/remix-run/react-router/pull/14136))
+
+**Full Changelog**: [`v7.8.0...v7.8.1`](https://github.com/remix-run/react-router/compare/react-router@7.8.0...react-router@7.8.1)
+
+## v7.8.0
+
+Date: 2025-08-07
+
+### What's Changed
+
+#### Consistently named `loaderData` values
+
+Ever noticed the discrepancies in loader data values handed to you by the framework? Like, we call it `loaderData` in your component props, but then `match.data` in your matches? Yeah, us too - as well as some keen-eyed React Router users who raised this in a proposal. We've added new `loaderData` fields alongside existing `data` fields in a few lingering spots to align with the `loaderData` naming used in the new `Route.*` APIs.
+
+#### Improvements/fixes to the middleware APIs (unstable)
+
+The biggest set of changes in `7.8.0` are to the `unstable_middleware` API's as we move closer to stabilizing them. If you've adopted the middleware APIs for early testing, please read the middleware changes below carefully. We hope to stabilize these soon so please let us know of any feedback you have on the API's in their current state!
+
+### Minor Changes
+
+- `react-router` - Add `nonce` prop to `Links` & `PrefetchPageLinks` ([#14048](https://github.com/remix-run/react-router/pull/14048))
+- `react-router` - Add `loaderData` arguments/properties alongside existing `data` arguments/properties to provide consistency and clarity between `loaderData` and `actionData` across the board ([#14047](https://github.com/remix-run/react-router/pull/14047))
+ - Updated types: `Route.MetaArgs`, `Route.MetaMatch`, `MetaArgs`, `MetaMatch`, `Route.ComponentProps.matches`, `UIMatch`
+ - `@deprecated` warnings have been added to the existing `data` properties to point users to new `loaderData` properties, in preparation for removing the `data` properties in a future major release
+
+### Patch Changes
+
+- `react-router` - Prevent _"Did not find corresponding fetcher result"_ console error when navigating during a `fetcher.submit` revalidation ([#14114](https://github.com/remix-run/react-router/pull/14114))
+- `react-router` - Switch Lazy Route Discovery manifest URL generation to use a standalone `URLSearchParams` instance instead of `URL.searchParams` to avoid a major performance bottleneck in Chrome ([#14084](https://github.com/remix-run/react-router/pull/14084))
+- `react-router` - Adjust internal RSC usage of `React.use` to avoid Webpack compilation errors when using React 18 ([#14113](https://github.com/remix-run/react-router/pull/14113))
+- `react-router` - Remove dependency on `@types/node` in TypeScript declaration files ([#14059](https://github.com/remix-run/react-router/pull/14059))
+- `react-router` - Fix types for `UIMatch` to reflect that the `loaderData`/`data` properties may be `undefined` ([#12206](https://github.com/remix-run/react-router/pull/12206))
+ - When an `ErrorBoundary` is being rendered, not all active matches will have loader data available, since it may have been their `loader` that threw to trigger the boundary
+ - The `UIMatch.data` type was not correctly handing this and would always reflect the presence of data, leading to the unexpected runtime errors when an `ErrorBoundary` was rendered
+ - โ ๏ธ This may cause some type errors to show up in your code for unguarded `match.data` accesses - you should properly guard for `undefined` values in those scenarios.
+
+ ```tsx
+ // app/root.tsx
+ export function loader() {
+ someFunctionThatThrows(); // โ Throws an Error
+ return { title: "My Title" };
+ }
+
+ export function Layout({ children }: { children: React.ReactNode }) {
+ let matches = useMatches();
+ let rootMatch = matches[0] as UIMatch>>;
+ // ^ rootMatch.data is currently incorrectly typed here, so TypeScript does
+ // not complain if you do the following which throws an error at runtime:
+ let { title } = rootMatch.data; // ๐ฅ
+
+ return ...;
+ }
+ ```
+
+- `@react-router/dev` - Fix rename without mkdir in Vite plugin ([#14105](https://github.com/remix-run/react-router/pull/14105))
+
+### Unstable Changes
+
+โ ๏ธ _[Unstable features](https://reactrouter.com/community/api-development-strategy#unstable-flags) are not recommended for production use_
+
+**RSC**
+
+- `react-router` - Fix Data Mode issue where routes that return `false` from `shouldRevalidate` would be replaced by an `` ([#14071](https://github.com/remix-run/react-router/pull/14071))
+- `react-router` - Proxy server action side-effect redirects from actions for document and `callServer` requests ([#14131](https://github.com/remix-run/react-router/pull/14131))
+
+**Middleware**
+
+- `react-router` - Change the `unstable_getContext` signature on `RouterProvider`, `HydratedRouter`, and `unstable_RSCHydratedRouter` so that it returns an `unstable_RouterContextProvider` instance instead of a `Map` used to construct the instance internally ([#14097](https://github.com/remix-run/react-router/pull/14097))
+ - See the [docs](https://reactrouter.com/api/data-routers/createBrowserRouter#optsunstable_getcontext) for more information
+ - โ ๏ธ This is a breaking change if you have adopted the `unstable_getContext` prop
+- `react-router` - Run client middleware on client navigations even if no loaders exist ([#14106](https://github.com/remix-run/react-router/pull/14106))
+- `react-router` - Convert internal middleware implementations to use the new `unstable_generateMiddlewareResponse` API ([#14103](https://github.com/remix-run/react-router/pull/14103))
+- `react-router` - Ensure resource route errors go through `handleError` w/middleware enabled ([#14078](https://github.com/remix-run/react-router/pull/14078))
+- `react-router` - Propagate returned `Response` from server middleware if `next` wasn't called ([#14093](https://github.com/remix-run/react-router/pull/14093))
+- `react-router` - Allow server middlewares to return `data()` values which will be converted into a `Response` ([#14093](https://github.com/remix-run/react-router/pull/14093), [#14128](https://github.com/remix-run/react-router/pull/14128))
+- `react-router` - Update middleware error handling so that the `next` function never throws and instead handles any middleware errors at the proper `ErrorBoundary` and returns the `Response` up through the ancestor `next` function ([#14118](https://github.com/remix-run/react-router/pull/14118))
+ - See the [error handling docs](https://reactrouter.com/how-to/middleware#next-and-error-handling) for more information
+ - โ ๏ธ This changes existing functionality so if you are currently wrapping `next` calls in `try`/`catch` you should be able to remove those
+- `react-router` - Bubble client-side middleware errors prior to `next` to the appropriate ancestor error boundary ([#14138](https://github.com/remix-run/react-router/pull/14138))
+- `react-router` - When middleware is enabled, make the `context` parameter read-only (`Readonly`) so that TypeScript will not allow you to write arbitrary fields to it in loaders, actions, or middleware. ([#14097](https://github.com/remix-run/react-router/pull/14097))
+- `react-router` - Rename and alter the signature/functionality of the `unstable_respond` API in `staticHandler.query`/`staticHandler.queryRoute` ([#14103](https://github.com/remix-run/react-router/pull/14103))
+ - This only impacts users using `createStaticHandler()` for manual data loading during non-Framework Mode SSR
+ - The API has been renamed to `unstable_generateMiddlewareResponse` for clarity
+ - The main functional change is that instead of running the loaders/actions before calling `unstable_respond` and handing you the result, we now pass a `query`/`queryRoute` function as a parameter and you execute the loaders/actions inside your callback, giving you full access to pre-processing and error handling
+ - The `query` version of the API now has a signature of `(query: (r: Request) => Promise) => Promise`
+ - The `queryRoute` version of the API now has a signature of `(queryRoute: (r: Request) => Promise) => Promise`
+ - This allows for more advanced usages such as running logic before/after calling `query` and direct error handling of errors thrown from query
+ - โ ๏ธ This is a breaking change if you've adopted the `staticHandler` `unstable_respond` API
+
+ ```tsx
+ let response = await staticHandler.query(request, {
+ requestContext: new unstable_RouterContextProvider(),
+ async unstable_generateMiddlewareResponse(query) {
+ try {
+ // At this point we've run middleware top-down so we need to call the
+ // handlers and generate the Response to bubble back up the middleware
+ let result = await query(request);
+ if (isResponse(result)) {
+ return result; // Redirects, etc.
+ }
+ return await generateHtmlResponse(result);
+ } catch (error: unknown) {
+ return generateErrorResponse(error);
+ }
+ },
+ });
+ ```
+
+- `@react-router/{architect,cloudflare,express,node}` - Change the `getLoadContext` signature (`type GetLoadContextFunction`) when `future.unstable_middleware` is enabled so that it returns an `unstable_RouterContextProvider` instance instead of a `Map` used to construct the instance internally ([#14097](https://github.com/remix-run/react-router/pull/14097))
+ - This also removes the `type unstable_InitialContext` export
+ - See the [middleware `getLoadContext` docs](https://reactrouter.com/how-to/middleware#changes-to-getloadcontextapploadcontext) for more information
+ - โ ๏ธ This is a breaking change if you have adopted middleware and are using a custom server with a `getLoadContext` function
+
+### Changes by Package
+
+- [`create-react-router`](https://github.com/remix-run/react-router/blob/react-router%407.8.0/packages/create-react-router/CHANGELOG.md#780)
+- [`react-router`](https://github.com/remix-run/react-router/blob/react-router%407.8.0/packages/react-router/CHANGELOG.md#780)
+- [`@react-router/architect`](https://github.com/remix-run/react-router/blob/react-router%407.8.0/packages/react-router-architect/CHANGELOG.md#780)
+- [`@react-router/cloudflare`](https://github.com/remix-run/react-router/blob/react-router%407.8.0/packages/react-router-cloudflare/CHANGELOG.md#780)
+- [`@react-router/dev`](https://github.com/remix-run/react-router/blob/react-router%407.8.0/packages/react-router-dev/CHANGELOG.md#780)
+- [`@react-router/express`](https://github.com/remix-run/react-router/blob/react-router%407.8.0/packages/react-router-express/CHANGELOG.md#780)
+- [`@react-router/fs-routes`](https://github.com/remix-run/react-router/blob/react-router%407.8.0/packages/react-router-fs-routes/CHANGELOG.md#780)
+- [`@react-router/node`](https://github.com/remix-run/react-router/blob/react-router%407.8.0/packages/react-router-node/CHANGELOG.md#780)
+- [`@react-router/remix-config-routes-adapter`](https://github.com/remix-run/react-router/blob/react-router%407.8.0/packages/react-router-remix-config-routes-adapter/CHANGELOG.md#780)
+- [`@react-router/serve`](https://github.com/remix-run/react-router/blob/react-router%407.8.0/packages/react-router-serve/CHANGELOG.md#780)
+
+**Full Changelog**: [`v7.7.1...v7.8.0`](https://github.com/remix-run/react-router/compare/react-router@7.7.1...react-router@7.8.0)
+
+## v7.7.1
+
+Date: 2025-07-24
+
+### Patch Changes
+
+- `@react-router/dev` - Update to Prettier v3 for formatting when running `react-router reveal --no-typescript` ([#14049](https://github.com/remix-run/react-router/pull/14049))
+
+### Unstable Changes
+
+โ ๏ธ _[Unstable features](https://reactrouter.com/community/api-development-strategy#unstable-flags) are not recommended for production use_
+
+- `react-router` - RSC Data Mode: fix bug where routes with errors weren't forced to revalidate when `shouldRevalidate` returned `false` ([#14026](https://github.com/remix-run/react-router/pull/14026))
+- `react-router` - RSC Data Mode: fix `Matched leaf route at location "/..." does not have an element or Component` warnings when error boundaries are rendered ([#14021](https://github.com/remix-run/react-router/pull/14021))
+
+**Full Changelog**: [`v7.7.0...v7.7.1`](https://github.com/remix-run/react-router/compare/react-router@7.7.0...react-router@7.7.1)
+
+## v7.7.0
+
+Date: 2025-07-16
+
+### What's Changed
+
+#### Unstable RSC APIs
+
+We're excited to introduce experimental support for RSC in Data Mode via the following new APIs:
+
+- [`unstable_RSCHydratedRouter`](https://reactrouter.com/api/rsc/RSCHydratedRouter)
+- [`unstable_RSCStaticRouter`](https://reactrouter.com/api/rsc/RSCStaticRouter)
+- [`unstable_createCallServer`](https://reactrouter.com/api/rsc/createCallServer)
+- [`unstable_getRSCStream`](https://reactrouter.com/api/rsc/getRSCStream)
+- [`unstable_matchRSCServerRequest`](https://reactrouter.com/api/rsc/matchRSCServerRequest)
+- [`unstable_routeRSCServerRequest`](https://reactrouter.com/api/rsc/routeRSCServerRequest)
+
+For more information, check out the [blog post](https://remix.run/blog/react-router-and-react-server-components) and the [RSC Docs](https://reactrouter.com/how-to/react-server-components).
+
+### Minor Changes
+
+- `create-react-router` - Add Deno as a supported and detectable package manager. Note that this detection will only work with Deno versions 2.0.5 and above. If you are using an older version version of Deno then you must specify the --package-manager CLI flag set to `deno`. ([#12327](https://github.com/remix-run/react-router/pull/12327))
+- `@react-router/remix-config-routes-adapter` - Export `DefineRouteFunction` type alongside `DefineRoutesFunction` ([#13945](https://github.com/remix-run/react-router/pull/13945))
+
+### Patch Changes
+
+- `react-router` - Handle `InvalidCharacterError` when validating cookie signature ([#13847](https://github.com/remix-run/react-router/pull/13847))
+- `react-router` - Pass a copy of `searchParams` to the `setSearchParams` callback function to avoid mutations of the internal `searchParams` instance ([#12784](https://github.com/remix-run/react-router/pull/12784))
+ - This causes bugs if you mutate the current stateful `searchParams` when a navigation is blocked because the internal instance gets out of sync with `useLocation().search`
+- `react-router` - Support invalid `Date` in `turbo-stream` v2 fork ([#13684](https://github.com/remix-run/react-router/pull/13684))
+- `react-router` - In Framework Mode, clear critical CSS in development after initial render ([#13872](https://github.com/remix-run/react-router/pull/13872), [#13995](https://github.com/remix-run/react-router/pull/13995))
+- `react-router` - Strip search parameters from `patchRoutesOnNavigation` `path` param for fetcher calls ([#13911](https://github.com/remix-run/react-router/pull/13911))
+- `react-router` - Skip scroll restoration on `useRevalidator()` calls because they're not new locations ([#13671](https://github.com/remix-run/react-router/pull/13671))
+- `react-router` - Support unencoded UTF-8 routes in prerender config with `ssr` set to `false` ([#13699](https://github.com/remix-run/react-router/pull/13699))
+- `react-router` - Do not throw if the url hash is not a valid URI component ([#13247](https://github.com/remix-run/react-router/pull/13247))
+- `react-router` - Remove `Content-Length` header from Single Fetch responses ([#13902](https://github.com/remix-run/react-router/pull/13902))
+- `react-router` - Fix a regression in `createRoutesStub` introduced with the middleware feature ([#13946](https://github.com/remix-run/react-router/pull/13946))
+ - As part of that work we altered the signature to align with the new middleware APIs without making it backwards compatible with the prior `AppLoadContext` API
+ - This permitted `createRoutesStub` to work if you were opting into middleware and the updated `context` typings, but broke `createRoutesStub` for users not yet opting into middleware
+ - We've reverted this change and re-implemented it in such a way that both sets of users can leverage it
+ - โ ๏ธ This may be a breaking bug for if you have adopted the unstable Middleware feature and are using `createRoutesStub` with the updated API.
+
+ ```tsx
+ // If you have not opted into middleware, the old API should work again
+ let context: AppLoadContext = {
+ /*...*/
+ };
+ let Stub = createRoutesStub(routes, context);
+
+ // If you have opted into middleware, you should now pass an instantiated
+ // `unstable_routerContextProvider` instead of a `getContext` factory function.
+ let context = new unstable_RouterContextProvider();
+ context.set(SomeContext, someValue);
+ let Stub = createRoutesStub(routes, context);
+ ```
+
+- `@react-router/dev` - Update `vite-node` to `^3.2.2` to support Vite 7 ([#13781](https://github.com/remix-run/react-router/pull/13781))
+- `@react-router/dev` - Properly handle `https` protocol in dev mode ([#13746](https://github.com/remix-run/react-router/pull/13746))
+- `@react-router/dev` - Fix missing styles when Vite's `build.cssCodeSplit` option is disabled ([#13943](https://github.com/remix-run/react-router/pull/13943))
+- `@react-router/dev` - Allow `.mts` and `.mjs` extensions for route config file ([#13931](https://github.com/remix-run/react-router/pull/13931))
+- `@react-router/dev` - Fix prerender file locations when `cwd` differs from project root ([#13824](https://github.com/remix-run/react-router/pull/13824))
+- `@react-router/dev` - Improve chunk error logging when a chunk cannot be found during the build ([#13799](https://github.com/remix-run/react-router/pull/13799))
+- `@react-router/dev` - Fix incorrectly configured `externalConditions` which had enabled `module` condition for externals and broke builds with certain packages (like Emotion) ([#13871](https://github.com/remix-run/react-router/pull/13871))
+
+### Unstable Changes
+
+โ ๏ธ _[Unstable features](https://reactrouter.com/community/api-development-strategy#unstable-flags) are not recommended for production use_
+
+- Add unstable RSC support for Data Mode ([#13700](https://github.com/remix-run/react-router/pull/13700))
+ - For more information, see the [RSC documentation](https://reactrouter.com/how-to/react-server-components)
+
+### Changes by Package
+
+- [`create-react-router`](https://github.com/remix-run/react-router/blob/react-router%407.7.0/packages/create-react-router/CHANGELOG.md#770)
+- [`react-router`](https://github.com/remix-run/react-router/blob/react-router%407.7.0/packages/react-router/CHANGELOG.md#770)
+- [`@react-router/architect`](https://github.com/remix-run/react-router/blob/react-router%407.7.0/packages/react-router-architect/CHANGELOG.md#770)
+- [`@react-router/cloudflare`](https://github.com/remix-run/react-router/blob/react-router%407.7.0/packages/react-router-cloudflare/CHANGELOG.md#770)
+- [`@react-router/dev`](https://github.com/remix-run/react-router/blob/react-router%407.7.0/packages/react-router-dev/CHANGELOG.md#770)
+- [`@react-router/express`](https://github.com/remix-run/react-router/blob/react-router%407.7.0/packages/react-router-express/CHANGELOG.md#770)
+- [`@react-router/fs-routes`](https://github.com/remix-run/react-router/blob/react-router%407.7.0/packages/react-router-fs-routes/CHANGELOG.md#770)
+- [`@react-router/node`](https://github.com/remix-run/react-router/blob/react-router%407.7.0/packages/react-router-node/CHANGELOG.md#770)
+- [`@react-router/remix-config-routes-adapter`](https://github.com/remix-run/react-router/blob/react-router%407.7.0/packages/react-router-remix-config-routes-adapter/CHANGELOG.md#770)
+- [`@react-router/serve`](https://github.com/remix-run/react-router/blob/react-router%407.7.0/packages/react-router-serve/CHANGELOG.md#770)
+
+**Full Changelog**: [`v7.6.3...v7.7.0`](https://github.com/remix-run/react-router/compare/react-router@7.6.3...react-router@7.7.0)
+
+## v7.6.3
+
+Date: 2025-06-27
+
+### Patch Changes
+
+- `react-router` - Do not serialize types for `useRouteLoaderData` ([#13752](https://github.com/remix-run/react-router/pull/13752))
+ - For types to distinguish a `clientLoader` from a `serverLoader`, you MUST annotate `clientLoader` args:
+
+ ```ts
+ // ๐ annotation required to skip serializing types
+ export function clientLoader({}: Route.ClientLoaderArgs) {
+ return { fn: () => "earth" };
+ }
+
+ function SomeComponent() {
+ const data = useRouteLoaderData("routes/this-route");
+ const planet = data?.fn() ?? "world";
+ return
Hello, {planet}!
;
+ }
+ ```
+
+- `@react-router/cloudflare` - Remove `tsup` from `peerDependencies` ([#13757](https://github.com/remix-run/react-router/pull/13757))
+- `@react-router/dev` - Add Vite 7 support ([#13748](https://github.com/remix-run/react-router/pull/13748))
+- `@react-router/dev` - Skip `package.json` resolution checks when a custom `entry.server.(j|t)sx` file is provided ([#13744](https://github.com/remix-run/react-router/pull/13744))
+- `@react-router/dev` - Add validation for a route's id not being 'root' ([#13792](https://github.com/remix-run/react-router/pull/13792))
+- `@react-router/fs-routes` `@react-router/remix-config-routes-adapter` - Use `replaceAll` for normalizing windows file system slashes ([#13738](https://github.com/remix-run/react-router/pull/13738))
+- `@react-router/node` - Remove old "install" package exports ([#13762](https://github.com/remix-run/react-router/pull/13762))
+
+**Full Changelog**: [`v7.6.2...v7.6.3`](https://github.com/remix-run/react-router/compare/react-router@7.6.2...react-router@7.6.3)
+
+## v7.6.2
+
+Date: 2025-06-03
+
+### Patch Changes
+
+- `create-react-router` - Update `tar-fs` ([#13675](https://github.com/remix-run/react-router/pull/13675))
+- `react-router` - (INTERNAL) Slight refactor of internal `headers()` function processing for use with RSC ([#13639](https://github.com/remix-run/react-router/pull/13639))
+- `react-router` `@react-router/dev` - Avoid additional `with-props` chunk in Framework Mode by moving route module component prop logic from the Vite plugin to `react-router` ([#13650](https://github.com/remix-run/react-router/pull/13650))
+- `@react-router/dev` - When `future.unstable_viteEnvironmentApi` is enabled and an absolute Vite `base` has been configured, ensure critical CSS is handled correctly during development ([#13598](https://github.com/remix-run/react-router/pull/13598))
+- `@react-router/dev` - Update `vite-node` ([#13673](https://github.com/remix-run/react-router/pull/13673))
+- `@react-router/dev` - Fix typegen for non-{.js,.jsx,.ts,.tsx} routes like .mdx ([#12453](https://github.com/remix-run/react-router/pull/12453))
+- `@react-router/dev` - Fix href types for optional dynamic params ([#13725](https://github.com/remix-run/react-router/pull/13725))
+
+ 7.6.1 introduced fixes for `href` when using optional static segments,
+ but those fixes caused regressions with how optional dynamic params worked in 7.6.0:
+
+ ```ts
+ // 7.6.0
+ href("/users/:id?"); // โ
+ href("/users/:id?", { id: 1 }); // โ
+
+ // 7.6.1
+ href("/users/:id?"); // โ
+ href("/users/:id?", { id: 1 }); // โ
+ ```
+
+ Now, optional static segments are expanded into different paths for `href`, but optional dynamic params are not.
+ This way `href` can unambiguously refer to an exact URL path, all while keeping the number of path options to a minimum.
+
+ ```ts
+ // 7.6.2
+
+ // path: /users/:id?/edit?
+ href("
+ // ^ suggestions when cursor is here:
+ //
+ // /users/:id?
+ // /users/:id?/edit
+ ```
+
+ Additionally, you can pass `params` from component props without needing to narrow them manually:
+
+ ```ts
+ declare const params: { id?: number };
+
+ // 7.6.0
+ href("/users/:id?", params);
+
+ // 7.6.1
+ href("/users/:id?", params); // โ
+ "id" in params ? href("/users/:id", params) : href("/users"); // works... but is annoying
+
+ // 7.6.2
+ href("/users/:id?", params); // restores behavior of 7.6.0
+ ```
+
+**Full Changelog**: [`v7.6.1...v7.6.2`](https://github.com/remix-run/react-router/compare/react-router@7.6.1...react-router@7.6.2)
+
+## v7.6.1
+
+Date: 2025-05-25
+
+### Patch Changes
+
+- `react-router` - Partially revert optimization added in `7.1.4` to reduce calls to `matchRoutes` because it surfaced other issues ([#13562](https://github.com/remix-run/react-router/pull/13562))
+- `react-router` - Update `Route.MetaArgs` to reflect that `data` can be potentially `undefined` ([#13563](https://github.com/remix-run/react-router/pull/13563))
+ - This is primarily for cases where a route `loader` threw an error to it's own `ErrorBoundary`, but it also arises in the case of a 404 which renders the root `ErrorBoundary`/`meta` but the root `loader` did not run because not routes matched
+- `react-router` - Avoid initial fetcher execution 404 error when Lazy Route Discovery is interrupted by a navigation ([#13564](https://github.com/remix-run/react-router/pull/13564))
+- `react-router` - Properly `href` replaces splats `*` ([#13593](https://github.com/remix-run/react-router/pull/13593))
+ - `href("/products/*", { "*": "/1/edit" }); // -> /products/1/edit`
+- `@react-router/architect` - Update `@architect/functions` from `^5.2.0` to `^7.0.0` ([#13556](https://github.com/remix-run/react-router/pull/13556))
+- `@react-router/dev` - Prevent typegen with route files that are outside the `app/` directory ([#12996](https://github.com/remix-run/react-router/pull/12996))
+- `@react-router/dev` - Add additional logging to `build` command output when cleaning assets from server build ([#13547](https://github.com/remix-run/react-router/pull/13547))
+- `@react-router/dev` - Don't clean assets from server build when `build.ssrEmitAssets` has been enabled in Vite config ([#13547](https://github.com/remix-run/react-router/pull/13547))
+- `@react-router/dev` - Fix typegen when same route is used at multiple paths ([#13574](https://github.com/remix-run/react-router/pull/13574))
+ - For example, `routes/route.tsx` is used at 4 different paths here:
+
+ ```ts
+ import { type RouteConfig, route } from "@react-router/dev/routes";
+ export default [
+ route("base/:base", "routes/base.tsx", [
+ route("home/:home", "routes/route.tsx", { id: "home" }),
+ route("changelog/:changelog", "routes/route.tsx", { id: "changelog" }),
+ route("splat/*", "routes/route.tsx", { id: "splat" }),
+ ]),
+ route("other/:other", "routes/route.tsx", { id: "other" }),
+ ] satisfies RouteConfig;
+ ```
+
+ - Previously, typegen would arbitrarily pick one of these paths to be the "winner" and generate types for the route module based on that path
+ - Now, typegen creates unions as necessary for alternate paths for the same route file
+
+- `@react-router/dev` - Better types for `params` ([#13543](https://github.com/remix-run/react-router/pull/13543))
+ - For example:
+
+ ```ts
+ // routes.ts
+ import { type RouteConfig, route } from "@react-router/dev/routes";
+
+ export default [
+ route("parent/:p", "routes/parent.tsx", [
+ route("route/:r", "routes/route.tsx", [
+ route("child1/:c1a/:c1b", "routes/child1.tsx"),
+ route("child2/:c2a/:c2b", "routes/child2.tsx"),
+ ]),
+ ]),
+ ] satisfies RouteConfig;
+ ```
+
+ - Previously, `params` for `routes/route` were calculated as `{ p: string, r: string }`.
+ - This incorrectly ignores params that could come from child routes
+ - If visiting `/parent/1/route/2/child1/3/4`, the actual params passed to `routes/route` will have a type of `{ p: string, r: string, c1a: string, c1b: string }`
+ - Now, `params` are aware of child routes and autocompletion will include child params as optionals:
+
+ ```ts
+ params.|
+ // ^ cursor is here and you ask for autocompletion
+ // p: string
+ // r: string
+ // c1a?: string
+ // c1b?: string
+ // c2a?: string
+ // c2b?: string
+ ```
+
+ - You can also narrow the types for `params` as it is implemented as a normalized union of params for each page that includes `routes/route`:
+
+ ```ts
+ if (typeof params.c1a === 'string') {
+ params.|
+ // ^ cursor is here and you ask for autocompletion
+ // p: string
+ // r: string
+ // c1a: string
+ // c1b: string
+ }
+ ```
+
+- `@react-router/dev` - Fix `href` for optional segments ([#13595](https://github.com/remix-run/react-router/pull/13595))
+ - Type generation now expands paths with optionals into their corresponding non-optional paths
+ - For example, the path `/user/:id?` gets expanded into `/user` and `/user/:id` to more closely model visitable URLs
+ - `href` then uses these expanded (non-optional) paths to construct type-safe paths for your app:
+
+ ```ts
+ // original: /user/:id?
+ // expanded: /user & /user/:id
+ href("/user"); // โ
+ href("/user/:id", { id: 1 }); // โ
+ ```
+
+ - This becomes even more important for static optional paths where there wasn't a good way to indicate whether the optional should be included in the resulting path:
+
+ ```ts
+ // original: /products/:id/detail?
+
+ // before
+ href("/products/:id/detail?"); // โ How can we tell `href` to include or omit `detail?` segment with a complex API?
+
+ // now
+ // expanded: /products/:id & /products/:id/detail
+ href("/product/:id"); // โ
+ href("/product/:id/detail"); // โ
+ ```
+
+### Unstable Changes
+
+โ ๏ธ _[Unstable features](https://reactrouter.com/community/api-development-strategy#unstable-flags) are not recommended for production use_
+
+- `@react-router/dev` - Renamed internal `react-router/route-module` export to `react-router/internal` ([#13543](https://github.com/remix-run/react-router/pull/13543))
+- `@react-router/dev` - Removed `Info` export from generated `+types/*` files ([#13543](https://github.com/remix-run/react-router/pull/13543))
+- `@react-router/dev` - Normalize dirent entry path across node versions when generating SRI manifest ([#13591](https://github.com/remix-run/react-router/pull/13591))
+
+**Full Changelog**: [`v7.6.0...v7.6.1`](https://github.com/remix-run/react-router/compare/react-router@7.6.0...react-router@7.6.1)
+
## v7.6.0
Date: 2025-05-08
@@ -404,7 +931,6 @@ Behind the scenes, React Router will generate the corresponding `declare module`
- `react-router` - Added a new `routeDiscovery` option in `react-router.config.ts` to configure Lazy Route Discovery behavior ([#13451](https://github.com/remix-run/react-router/pull/13451))
- `react-router` - Add support for route component props in `createRoutesStub` ([#13528](https://github.com/remix-run/react-router/pull/13528))
-
- This allows you to unit test your route components using the props instead of the hooks:
```tsx
@@ -523,7 +1049,6 @@ Date: 2025-04-17
### Patch Changes
- `react-router` - When using the object-based `route.lazy` API, the `HydrateFallback` and `hydrateFallbackElement` properties are now skipped when lazy loading routes after hydration ([#13376](https://github.com/remix-run/react-router/pull/13376))
-
- If you move the code for these properties into a separate file, since the hydrate properties were unused already (if the route wasn't present during hydration), you can avoid downloading them at all. For example:
```ts
@@ -828,7 +1353,7 @@ Here's a simple example of a client-side logging middleware that can be placed o
```tsx
const clientLogger: Route.unstable_ClientMiddlewareFunction = async (
{ request },
- next
+ next,
) => {
let start = performance.now();
@@ -847,7 +1372,7 @@ For a server-side middleware, the `next` function will return the HTTP `Response
```tsx
const serverLogger: Route.unstable_MiddlewareFunction = async (
{ request, params, context },
- next
+ next,
) => {
let start = performance.now();
@@ -868,7 +1393,7 @@ You can throw a `redirect` from a middleware to short circuit any remaining proc
import { sessionContext } from "../context";
const serverAuth: Route.unstable_MiddlewareFunction = (
{ request, params, context },
- next
+ next,
) => {
let session = context.get(sessionContext);
let user = session.get("user");
@@ -1121,7 +1646,7 @@ import { MassiveComponent } from "~/components";
export async function clientLoader() {
return await fetch("https://example.com/api").then((response) =>
- response.json()
+ response.json(),
);
}
@@ -1156,7 +1681,7 @@ To achieve this optimization, React Router will split the route module into mult
```tsx filename=routes/example.tsx?route-chunk=clientLoader
export async function clientLoader() {
return await fetch("https://example.com/api").then((response) =>
- response.json()
+ response.json(),
);
}
```
@@ -1202,7 +1727,7 @@ const shared = () => console.log("hello");
export async function clientLoader() {
shared();
return await fetch("https://example.com/api").then((response) =>
- response.json()
+ response.json(),
);
}
@@ -1229,7 +1754,7 @@ import { shared } from "./shared";
export async function clientLoader() {
shared();
return await fetch("https://example.com/api").then((response) =>
- response.json()
+ response.json(),
);
}
@@ -1247,7 +1772,7 @@ import { shared } from "./shared";
export async function clientLoader() {
shared();
return await fetch("https://example.com/api").then((response) =>
- response.json()
+ response.json(),
);
}
```
@@ -1641,7 +2166,7 @@ Also note that, if you were using Remix's `routes` option to define config-based
+];
```
-#### Typesafety improvements
+#### Type-safety improvements
React Router now generates types for each of your route modules and passes typed props to route module component exports ([#11961](https://github.com/remix-run/react-router/pull/11961), [#12019](https://github.com/remix-run/react-router/pull/12019)). You can access those types by importing them from `./+types/`.
@@ -1846,6 +2371,17 @@ async function fakeGetSlugsFromCms() {
# React Router v6 Releases
+## v6.30.1
+
+Date: 2025-05-20
+
+### Patch Changes
+
+- Partially revert optimization added in `6.29.0` to reduce calls to `matchRoutes` because it surfaced other issues ([#13623](https://github.com/remix-run/react-router/pull/13623))
+- Stop logging invalid warning when `v7_relativeSplatPath` is set to `false` ([#13502](https://github.com/remix-run/react-router/pull/13502))
+
+**Full Changelog**: [`v6.30.0...v6.30.1`](https://github.com/remix-run/react-router/compare/react-router@6.30.0...react-router@6.30.1)
+
## v6.30.0
Date: 2025-02-27
@@ -2102,7 +2638,7 @@ const router = createBrowserRouter(
patch("root", [route]);
}
},
- }
+ },
);
```
@@ -2882,7 +3418,7 @@ let routes = createRoutesFromElements(
} />
import("./a")} />
import("./b")} />
-
+ ,
);
```
diff --git a/GOVERNANCE.md b/GOVERNANCE.md
new file mode 100644
index 0000000000..cfaf61031f
--- /dev/null
+++ b/GOVERNANCE.md
@@ -0,0 +1,150 @@
+# React Router Open Governance Model
+
+- [Overview](#overview)
+- [Design Goals](#design-goals)
+- [Steering Committee](#steering-committee)
+- [Bug/Issue Process](#bugissue-process)
+- [New Feature Process](#new-feature-process)
+- [New Feature Stages](#new-feature-stages)
+ - [Stage 0 โ Proposal](#stage-0--proposal)
+ - [Stage 1 โ Consideration](#stage-1--consideration)
+ - [Stage 2 โ Alpha](#stage-2--alpha)
+ - [Stage 3 โ Beta](#stage-3--beta)
+ - [Stage 4 โ Stabilization](#stage-4--stabilization)
+ - [Stage 5 โ Stable](#stage-5--stable)
+
+## Overview
+
+React Router has been around since 2014 largely under the development and oversight of [Michael Jackson](https://x.com/mjackson) and [Ryan Florence](https://x.com/ryanflorence). After the launch of [Remix](https://remix.run/) in 2021, the subsequent creation of the Remix team, and the merging of Remix v2 into React Router v7[^1][^2], the project shifted from a [Founder-Leader](https://www.redhat.com/en/blog/understanding-open-source-governance-models) model to a "Steering Committee" (SC) model that operates on a Request for Comments (RFC) process.
+
+[^1]: https://remix.run/blog/merging-remix-and-react-router
+
+[^2]: https://remix.run/blog/incremental-path-to-react-19
+
+This document will outline the process in which React Router will continue to evolve and how new features will make their way into the codebase. This is an evergreen document and will be updated as needed to reflect future changes in the process.
+
+## Design Goals
+
+The following design goals should be considered when considering RFCs for acceptance:
+
+- **Less is More**. React Router has gained a _lot_ of functionality in the past years, but with that comes a bunch of new API surface. It's time to hone in on the core functionality and aim to reduce API surface _without sacrificing capabilities_. This may come in multiple forms, such as condensing a few existing APIs into a singular API, or deprecating current APIs in favor of a new React API.
+- **Routing and Data Focused.** Focus on core router-integrated/router-centric APIs and avoid adding first-class APIs that can be implemented in user-land
+- **Simple Migration Paths.** Major version upgrades don't have to stink. Breaking changes should be implemented behind future flags. Deprecations should be properly marked ahead of time in code and in documentation. Console warnings should be added prior to major releases to nudge developers towards the changes they can begin to make to prep for the upgrade.
+- **Lowest Common Mode.** Features are added at the lowest mode possible (`declarative -> data -> framework`) and then leveraged by the higher-level modes. This ensures that the largest number of React Router applications can leverage them.
+- **Regular Release Cadence**. Aim for major SemVer releases on a ~yearly basis so application developers can prepare in advance.
+
+## Steering Committee
+
+The Steering Committee will be in charge of accepting RFC's for consideration, approving PRs to land features in an "unstable" state, and approving stabilization PRs to land PRs that stabilize features into React Router.
+
+The SC will initially consist of the Remix team developers:
+
+- Matt Brophy ([`@brophdawg11`](https://github.com/brophdawg11))
+- Pedro Cattori ([`@pcattori`](https://github.com/pcattori))
+- Mark Dalgleish ([`@markdalgleish`](https://github.com/markdalgleish))
+- Jacob Ebey ([`@jacob-ebey`](https://github.com/jacob-ebey))
+- Brooks Lybrand ([`@brookslybrand`](https://github.com/brookslybrand))
+
+In the future, we may add a limited number of heavily involved community members to the SC as well.
+
+To reduce friction, the SC will primarily operate asynchronously via GitHub, but private and/or public meetings may be scheduled as needed.
+
+## Bug/Issue Process
+
+Due to the large number of React Router applications out there, we have to be a bit strict on the process for filing issues to avoid an overload in GitHub.
+
+- **All** bugs must have a **minimal** and **runnable** reproduction [^3]
+ - _Minimal_ means that it is not just pointing to a deployed site or a branch in your existing application
+ - _Runnable_ means that it is a working application where we can see the issue, not just a few snippets of code that need to be manually reassembled into a running application
+ - The preferred methods for reproductions are:
+ - **Framework Mode**: [StackBlitz](https://reactrouter.com/new) or a GitHub fork with a failing integration test based on [`bug-report-test.ts`](integration/bug-report-test.ts)
+ - **Data/Declarative Modes**: [CodeSandbox (TS)](https://codesandbox.io/templates/react-vite-ts) or [CodeSandbox (JS)](https://codesandbox.io/templates/react-vite)
+ - If StackBlitz/CodeSandbox is not an option, a GitHub repo based on a fresh `npx create-react-router` app is acceptable
+ - Only in extraordinary circumstances will code snippets or maximal reproductions be accepted
+- Issue Review
+ - Issues not meeting the above criteria will be closed and pointed to this document
+ - Non-issues (feature requests, usage questions) will also be closed with a link to this document
+ - The SC will triage issues regularly
+- Fixing Issues
+ - The SC will mark good community issues with an `Accepting PRs` label
+ - These issues will generally be ones that are likely to have a small surface area fix
+ - However, anyone can work on any issue, but there's no guarantee the PR will be accepted if the surface area is too large for expedient review by a core team member
+
+[^3]: https://antfu.me/posts/why-reproductions-are-required
+
+## New Feature Process
+
+The process for new features being added to React Router will follow a series of stages loosely based on the [TC39 Process](https://tc39.es/process-document/). It is important to note that entrance into any given stage does not imply that an RFC will proceed any further. The stages will act as a funnel with fewer RFCs making it into later stages such that only the strongest RFCs make it into a React Router release in a stable fashion.
+
+> [!NOTE]
+> Most new community-driven features for React Router will go through all stages. Some features, if trivial or obvious enough, may skip stages and be implemented directly as a stable feature.
+
+This table gives a high-level overview of the stages, but please see the individual stage sections below for more detailed information on the stages and the process for moving an FC through them. Once a feature reaches Stage 2, it will be added to the [Roadmap](https://github.com/orgs/remix-run/projects/5) where it can be tracked as it moves through the stages.
+
+| Stage | Name | Entrance Criteria | Purpose |
+| ----- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 0 | Proposal | Proposal discussion opened on GitHub | We start with a GitHub Proposal to provide the lowest barrier to RFC submission. Anyone can submit an RFC and community members can review, comment, up-vote without any initial involvement of the SC. |
+| 1 | Consideration | Proposal acceptance from 2 SC members | The consideration phase is the first "funnel" for incoming RFCs where the SC can officially express interest in the more popular RFCs. We only require 2 SC members to express interest to move an RFC into the **Consideration** phase to allow for low-friction experimentation of features in the **Alpha** stage. |
+| 2 | Alpha | Pull request (PR) opened to implement the feature in an "unstable" state | The **Alpha** stage is the next funnel for RFCs. Once interest has been expressed by the SC in the **Consideration** phase we open the RFC up for a sample PR implementation and a mechanism for community members to alpha test the feature without requiring that anything be shipped in a React Router SemVer release. This stage allows evaluation of the RFC in running applications and consideration of what a practical implementation of the RFC looks like in the code. |
+| 3 | Beta | PR approval from 2 SC members indicating their acceptance of the PR for an unstable API | A RFC enters the **Beta** stage once enough members of the SC feel comfortable not only with the code for the beta feature, but have also seen positive feedback from alpha testers that the feature is working as expected. Once an **Alpha** stage PR has enough SC approvals, it will be merged and be included in the next React Router release. |
+| 4 | Stabilization | At least 1 month in the Beta stage and PR opened to stabilize the APIs. This PR should also include documentation for the new feature. | The **Stabilization** phase exists to ensure that unstable features are available for enough time for applications to update their React Router version and opt-into beta testing. We don't want to rush features through beta testing so that we have maximal feedback prior to stabilizing a feature. |
+| 5 | Stable | PR approval from at least 50% of the SC members indicating their acceptance of the PR for a stable API | A RFC is completed and enters the **Stable** stage once enough members of the SC feel comfortable not only with the code for the stable feature, but have also seen positive feedback from beta testers that the feature is working as expected. Once an **Beta** stage PR has enough SC approvals and has spent the required amount of time in the **Beta** stage, it can be merged and included in the next React Router release. |
+
+## New Feature Stages
+
+### Stage 0 โ Proposal
+
+- All new features begin at **Stage 0 โ Proposal** when a Request For Comments (RFC) is written up in a GitHub Proposal Discussion
+- Anyone can write an RFC, including core team members and community members
+- The RFC should outline the use-case for the new feature, why current APIs are insufficient for the use-case, and provide potential API surfaces for the feature
+- The proposal should be clear, concise, and provide enough context for the Steering Committee (SC) and community to evaluate its merit
+- Community upvotes on the proposal are used as a signal of interest and demand for the SC โ higher upvoted issues are more likely to be considered by the SC members
+- At this stage, community members may feel free to work on sample implementations in a fork of the repo and provide links in the RFC, but a pull request **should not** be opened until it reaches Stage 1
+
+### Stage 1 โ Consideration
+
+- A proposal enters **Stage 1 โ Consideration** when 2 SC members indicate interest/support for the idea as a valuable addition to React Router
+- Upon entering this stage, a GitHub Issue will be created for the feature and added to the roadmap
+- These initial supporting SC members will be the champions for the feature and will be loosely responsible for shepherding the feature through the stages of the RFC process
+- At this stage, the proposal is eligible for a sample PR implementation from a core team or community member
+- The SC will indicate at this stage if this is a feature open to a community PR or something the core team would prefer to tackle
+- All PRs at this stage should implement the feature in an "unstable" fashion (usually using an `unstable_` prefix on the future flag or API)
+
+### Stage 2 โ Alpha
+
+- A proposal enters **Stage 2 โ Alpha** once a PR has been opened implementing the feature in an `unstable_` state
+- At this stage, we should open an Issue for the Proposal and add it to the [Roadmap](https://github.com/orgs/remix-run/projects/5)
+- At this stage, we are looking for early community testing _before_ merging any work to the React Router repo โ so these PRs should provide a mechanism for community members to opt into to alpha testing
+ - Maintainers can trigger an alpha release from the PR branch by adding the `alpha-release` label, which will kick off an experimental release and comment it back on the PR
+ - Because the alpha release may contain other work committed to `dev` but not yet released in a stable version, it may not be ideal for testing in all cases
+ - In these cases, PR authors may also add the contents for a `.patch` file in a comment that folks can use via [patch-package](https://www.npmjs.com/package/patch-package) or [pnpm patch](https://pnpm.io/cli/patch)
+- Feedback from alpha testers is considered essential for further progress
+- The PR should also contain a changeset documenting the new API for the release notes
+- SC members will review and approve the PR via GitHub reviews
+- Approval at this stage communicates:
+ - The feature is valuable for React Router
+ - The API/code is sufficient for unstable/beta testing, though further iteration may be needed
+ - Code is not required to be in a final state yet, but it must be coded in such a way so as not to introduce regressions to other areas of the API
+ - We have seen enough positive feedback from Alpha testers to move the feature along
+
+### Stage 3 โ Beta
+
+- A proposal enters **Stage 3 โ Beta** once it receives **Stage 2 โ Alpha** PR approvals from 2 SC members and is merged to `dev`
+ - An SC member authoring the `unstable_` PR counts as an implicit approval, so in those cases explicit approval is required from 1 additional SC member
+- This will include the feature in `nightly` releases and the next normal SemVer release for broader beta testing under the `unstable_` flag
+
+### Stage 4 โ Stabilization
+
+- A proposal enters **Stage 4 โ Stabilization** after a minimum of 1 month in **Stage 3 โ Beta** and a PR has been opened to remove the `unstable_` prefixes and stabilize the feature
+- Stabilization PRs should add proper documentation for the feature
+- SC members will review and approve the PR via GitHub reviews
+- Approval at this stage communicates:
+ - Sufficient community feedback has been received from beta testers to trust the API's design and implementation
+ - The code is production-quality and well-tested, with no related regressions
+ - The PR includes documentation for the stable feature
+
+### Stage 5 โ Stable
+
+- A proposal enters **Stage 5 โ Stable** once it receives **Stage 4 โ Stabilization** PR approvals from at least 50% of the SC members and is merged to `dev`
+ - An SC member authoring the stabilization PR counts as an implicit approval
+- This will include the stable feature in `nightly` releases and the next normal SemVer release
diff --git a/contributors.yml b/contributors.yml
index e0df33a8b1..913ae49346 100644
--- a/contributors.yml
+++ b/contributors.yml
@@ -1,4 +1,6 @@
- 0xEddie
+- 3fuyang
+- 43081j
- aarbi
- abdallah-nour
- abeadam
@@ -16,6 +18,7 @@
- akamfoad
- alany411
- alberto
+- AlemTuzlak
- Aleuck
- alexandernanberg
- alexanderson1993
@@ -24,6 +27,7 @@
- amitdahan
- AmRo045
- amsal
+- Andarist
- andreasottosson-polestar
- andreiduca
- antonmontrezor
@@ -48,11 +52,14 @@
- BDomzalski
- bhbs
- bilalk711
+- bjohn465
+- bmsuseluda
- bobziroll
- bravo-kernel
- Brendonovich
- briankb
- BrianT1414
+- Bricklou
- brockross
- brookslybrand
- brophdawg11
@@ -60,15 +67,19 @@
- bvangraafeiland
- camthompson
- CanRau
+- caprolactam
- cassidoo
- chaance
- chasinhues
- chensokheng
+- chr33s
+- chrille0313
- chrisngobanh
- christopherchudzicki
- ChristophP
- christowiz
- clavery
+- clonemycode
- Cmoen11
- codeape2
- coryhouse
@@ -84,10 +95,12 @@
- david-crespo
- davidbielik
- dcblair
+- dchenk
- decadentsavant
- developit
- dgrijuela
- DigitalNaut
+- DimaAmega
- dmitrytarassov
- dokeet
- doytch
@@ -106,6 +119,7 @@
- FilipJirsak
- focusotter
- foxscotch
+- frodi-karlsson
- frontsideair
- fucancode
- fyzhu
@@ -114,6 +128,7 @@
- gatzjames
- gavriguy
- Geist5000
+- GeoffKarnov
- gesposito
- gianlucca
- gijo-varghese
@@ -126,10 +141,14 @@
- haivuw
- hampelm
- harshmangalam
+- HelpMe-Pls
+- HenriqueLimas
- hernanif1
+- hi-ogawa
- HK-SHAO
- holynewbie
- hongji00
+- hoosierhuy
- hsbtr
- hyesungoh
- iamnishanth
@@ -138,6 +157,7 @@
- igniscyan
- imjordanxd
- infoxicator
+- ioNihal
- IsaiStormBlesed
- Isammoc
- iskanderbroere
@@ -184,6 +204,8 @@
- ken0x0a
- kentcdodds
- kettanaito
+- kigawas
+- kilavvy
- kiliman
- kkirsche
- kno-raziel
@@ -193,6 +215,7 @@
- KubasuIvanSakwa
- KutnerUri
- kylegirard
+- LadyTsukiko
- landisdesign
- latin-1
- lazybean
@@ -218,17 +241,21 @@
- marc2332
- markdalgleish
- markivancho
+- markmals
+- Marlon-Buckley
- maruffahmed
- marvinruder
- mathpaquette
- matmilbury
- matt-harro
+- matteogauthier
- matthewlynch
- maximevtush
- maxpou
- mcansh
- MeatSim
- MenouerBetty
+- Methuselah96
- mfijas
- MichaelDeBoey
- michal-antczak
@@ -240,6 +267,7 @@
- mjackson
- mlewando
- mm-jpoole
+- mobregozo
- modex98
- morleytatro
- ms10596
@@ -247,16 +275,20 @@
- mtendekuyokwa19
- mtliendo
- namoscato
+- nanianlisao
- ned-park
- nenene3
+- ngbrown
- nichtsam
- nikeee
- nilubisan
- Nismit
- nnhjs
- noisypigeon
+- nowells
- Nurai1
- Obi-Dann
+- okalil
- OlegDev1
- omahs
- omar-moquete
@@ -267,6 +299,7 @@
- parveen232
- paulsmithkc
- pavsoldatov
+- pawelblaszczyk5
- pcattori
- penx
- petersendidit
@@ -279,9 +312,12 @@
- pruszel
- pwdcd
- pyitphyoaung
+- redabacha
- refusado
+- remorses
- renyu-io
- reyronald
+- richardscarrott
- rifaidev
- rimian
- robbtraister
@@ -291,16 +327,20 @@
- rtmann
- rtzll
- rubeonline
+- ruidi-huang
+- rururux
- ryanflorence
- ryanhiebert
- saengmotmi
- samimsu
- sanjai451
- sanketshah19
+- sapphi-red
- saul-atomrigs
- sbolel
- scarf005
- sealer3
+- seasick
- senseibarni
- sergiodxa
- serranoarevalo
@@ -312,14 +352,17 @@
- shivamsinghchahar
- silvenon
- SimenB
+- SirDaev
- SkayuX
- skratchdot
+- skrhlm
- smff
- smithki
- soartec-lab
- sorokya
- sorrycc
- souzasmatheus
+- SovietGhost
- soxtoby
- srmagura
- SsongQ-92
@@ -341,6 +384,7 @@
- ThornWu
- tiborbarsi
- timdorr
+- timfisher
- TkDodo
- tkindy
- tlinhart
@@ -354,6 +398,7 @@
- tosinamuda
- triangularcube
- trungpv1601
+- tryonelove
- TrySound
- ttys026
- Tumas2
@@ -363,6 +408,8 @@
- valerii15298
- ValiantCat
- vdusart
+- vesan
+- vezaynk
- VictorElHajj
- vijaypushkin
- vikingviolinist
@@ -372,17 +419,24 @@
- vonagam
- WalkAlone0325
- whxhlgy
+- wilcoxmd
- willemarcel
- williamsdyyz
- willsawyerrrr
- willsmithte
+- Willvillegas
- wkovacs64
+- woodywoodsta
- xavier-lc
- xcsnowcity
- xdaxer
- yionr
- yracnet
- ytori
+- yuhwan-park
- yuleicul
+- yuri-poliantsev
+- zeevick10
- zeromask1337
- zheng-chuang
+- zxTomw
diff --git a/decisions/0003-data-strategy.md b/decisions/0003-data-strategy.md
index dafb3b4fef..cb8884363e 100644
--- a/decisions/0003-data-strategy.md
+++ b/decisions/0003-data-strategy.md
@@ -113,7 +113,7 @@ Therefore, we're introducing the concept of a `DataStrategyMatch` which is just
```js
function dataStrategy({ matches, defaultStrategy }) {
return Promise.all(
- matches.map((m) => match.route.then((route) => route.loader(/* ... */)))
+ matches.map((m) => match.route.then((route) => route.loader(/* ... */))),
);
}
```
@@ -141,7 +141,7 @@ We considered how to handle `shouldRevalidate` behavior. There's sort of 2 basic
I _think_ (1) is preferred to keep the API at a minimum and avoid leaking into _other_ ways to opt-out of revalidation. We already have an API for that so let's lean into it.
-Additionally, another big con of (2) is that if we want to let them make revalidation decisions inside `dataStrategy` - we need to expose all of the informaiton required for that (`currentUrl`, `currentParams`, `nextUrl`, `nextParams`, `submission` info, `actionResult`, etc.) - the API becomes a mess.
+Additionally, another big con of (2) is that if we want to let them make revalidation decisions inside `dataStrategy` - we need to expose all of the information required for that (`currentUrl`, `currentParams`, `nextUrl`, `nextParams`, `submission` info, `actionResult`, etc.) - the API becomes a mess.
Therefore we are aiming to stick with one and let `shouldRevalidate` be the only way to opt-out of revalidation.
diff --git a/decisions/0005-remixing-react-router.md b/decisions/0005-remixing-react-router.md
index 9b24896a86..e5b6e7389d 100644
--- a/decisions/0005-remixing-react-router.md
+++ b/decisions/0005-remixing-react-router.md
@@ -204,7 +204,7 @@ function NewErrorBoundary() {
const error = useRouteError();
if (error instanceof Response) {
- return ;
+ return ;
} else {
return ;
}
@@ -283,7 +283,7 @@ If folks still prefer the JSX notation, they can leverage `createRoutesFromEleme
const routes = createRoutesFromElements(
}>
} />
-
+ ,
);
const router = createBrowserRouter(routes);
diff --git a/decisions/0010-splitting-up-client-and-server-code-in-vite.md b/decisions/0010-splitting-up-client-and-server-code-in-vite.md
index 70755110d7..144ce7d57c 100644
--- a/decisions/0010-splitting-up-client-and-server-code-in-vite.md
+++ b/decisions/0010-splitting-up-client-and-server-code-in-vite.md
@@ -96,6 +96,7 @@ Checking for `.server` modules only requires checking the module's path and does
`vite-env-only` does require AST parsing and transformations so it will always be slower than `.server` modules.
[^1]: Vite provides a lower-level module graph API, but the module graph is not guaranteed to be complete as it is only populated as modules are requested.
+
[^2]: When a file changes on disk, Vite invalidates the corresponding module in its cache to power features like HMR.
[decision-0009]: ./0009-do-not-rely-on-treeshaking-for-correctness.md
diff --git a/decisions/0014-context-middleware.md b/decisions/0014-context-middleware.md
index a5be46d17f..10dd5db633 100644
--- a/decisions/0014-context-middleware.md
+++ b/decisions/0014-context-middleware.md
@@ -105,7 +105,7 @@ The middleware API we landed on to ship looks as follows:
```ts
const myMiddleware: Route.unstable_MiddlewareFunction = async (
{ request, context },
- next
+ next,
) => {
// Do stuff before the handlers are called
context.user = await getUser(request);
@@ -125,7 +125,7 @@ export const middleware = [myMiddleware];
// `clientLoader`/`clientAction`
const myClientMiddleware: Route.unstable_ClientMiddlewareFunction = (
{ context },
- next
+ next,
) => {
//...
};
diff --git a/docs/api/components/Await.md b/docs/api/components/Await.md
index feaf291683..259f55d1d4 100644
--- a/docs/api/components/Await.md
+++ b/docs/api/components/Await.md
@@ -4,6 +4,18 @@ title: Await
# Await
+
+
[MODES: framework, data]
## Summary
@@ -12,16 +24,16 @@ title: Await
Used to render promise values with automatic error handling.
+**Note:** `` expects to be rendered inside a [``](https://react.dev/reference/react/Suspense)
+
```tsx
import { Await, useLoaderData } from "react-router";
-export function loader() {
+export async function loader() {
// not awaited
const reviews = getReviews();
// awaited (blocks the transition)
- const book = await fetch("/api/book").then((res) =>
- res.json()
- );
+ const book = await fetch("/api/book").then((res) => res.json());
return { book, reviews };
}
@@ -47,14 +59,20 @@ function Book() {
}
```
-`` expects to be rendered inside of a ``
+## Signature
+
+```tsx
+function Await({
+ children,
+ errorElement,
+ resolve,
+}: AwaitProps)
+```
## Props
### children
-[modes: framework, data]
-
When using a function, the resolved value is provided as the parameter.
```tsx [2]
@@ -63,13 +81,13 @@ When using a function, the resolved value is provided as the parameter.
```
-When using React elements, [useAsyncValue](../hooks/useAsyncValue) will provide the
+When using React elements, [`useAsyncValue`](../hooks/useAsyncValue) will provide the
resolved value:
```tsx [2]
-;
+
function Reviews() {
const resolvedReviews = useAsyncValue();
@@ -79,9 +97,8 @@ function Reviews() {
### errorElement
-[modes: framework, data]
-
-The error element renders instead of the children when the promise rejects.
+The error element renders instead of the `children` when the [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
+rejects.
```tsx
```
-To provide a more contextual error, you can use the [useAsyncError](../hooks/useAsyncError) in a
+To provide a more contextual error, you can use the [`useAsyncError`](../hooks/useAsyncError) in a
child component
```tsx
@@ -101,7 +118,7 @@ child component
resolve={reviewsPromise}
>
-;
+
function ReviewsError() {
const error = useAsyncError();
@@ -109,18 +126,18 @@ function ReviewsError() {
}
```
-If you do not provide an errorElement, the rejected value will bubble up to
-the nearest route-level ErrorBoundary and be accessible
-via [useRouteError](../hooks/useRouteError) hook.
+If you do not provide an `errorElement`, the rejected value will bubble up
+to the nearest route-level [`ErrorBoundary`](../../start/framework/route-module#errorboundary)
+and be accessible via the [`useRouteError`](../hooks/useRouteError) hook.
### resolve
-[modes: framework, data]
+Takes a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
+returned from a [`loader`](../../start/framework/route-module#loader) to be
+resolved and rendered.
-Takes a promise returned from a [LoaderFunction](../Other/LoaderFunction) value to be resolved and rendered.
-
-```jsx
-import { useLoaderData, Await } from "react-router";
+```tsx
+import { Await, useLoaderData } from "react-router";
export async function loader() {
let reviews = getReviews(); // not awaited
@@ -153,3 +170,4 @@ export default function Book() {
);
}
```
+
diff --git a/docs/api/components/Form.md b/docs/api/components/Form.md
index a393a37e4f..a63c679707 100644
--- a/docs/api/components/Form.md
+++ b/docs/api/components/Form.md
@@ -4,17 +4,41 @@ title: Form
# Form
+
+
[MODES: framework, data]
## Summary
-[Reference Documentation โ](https://api.reactrouter.com/v7/functions/react_router.Form.html)
+[Reference Documentation โ](https://api.reactrouter.com/v7/variables/react_router.Form.html)
-A progressively enhanced HTML [`