react-router 7.16.0 → 8.0.0-pre.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +0 -1
- package/dist/development/dom-export.d.ts +6 -172
- package/dist/development/dom-export.js +12 -1007
- package/dist/development/index-react-server-client.d.ts +7 -4
- package/dist/development/index-react-server-client.js +8 -52
- package/dist/development/index-react-server.d.ts +1645 -1635
- package/dist/development/index-react-server.js +2880 -3642
- package/dist/development/index.d.ts +43 -1475
- package/dist/development/index.js +37 -2621
- package/dist/development/lib/actions.js +62 -0
- package/dist/development/lib/components.d.ts +1022 -0
- package/dist/development/lib/components.js +835 -0
- package/dist/development/lib/context.d.ts +83 -0
- package/dist/development/lib/context.js +41 -0
- package/dist/development/lib/dom/dom.d.ts +119 -0
- package/dist/development/lib/dom/dom.js +143 -0
- package/dist/development/lib/dom/lib.d.ts +2042 -0
- package/dist/development/lib/dom/lib.js +1259 -0
- package/dist/development/lib/dom/server.d.ts +138 -0
- package/dist/development/lib/dom/server.js +301 -0
- package/dist/development/lib/dom/ssr/components.d.ts +196 -0
- package/dist/development/lib/dom/ssr/components.js +579 -0
- package/dist/development/lib/dom/ssr/data.js +29 -0
- package/dist/development/lib/dom/ssr/entry.d.ts +59 -0
- package/dist/development/lib/dom/ssr/errorBoundaries.d.ts +27 -0
- package/dist/development/lib/dom/ssr/errorBoundaries.js +83 -0
- package/dist/development/lib/dom/ssr/errors.d.ts +7 -0
- package/dist/development/lib/dom/ssr/errors.js +36 -0
- package/dist/development/lib/dom/ssr/fallback.js +28 -0
- package/dist/development/lib/dom/ssr/fog-of-war.d.ts +12 -0
- package/dist/development/lib/dom/ssr/fog-of-war.js +170 -0
- package/dist/development/lib/dom/ssr/hydration.d.ts +32 -0
- package/dist/development/lib/dom/ssr/hydration.js +29 -0
- package/dist/development/lib/dom/ssr/invariant.js +16 -0
- package/dist/development/lib/dom/ssr/links.js +170 -0
- package/dist/development/lib/dom/ssr/markup.js +24 -0
- package/dist/development/lib/dom/ssr/routeModules.d.ts +206 -0
- package/dist/development/lib/dom/ssr/routeModules.js +31 -0
- package/dist/development/lib/dom/ssr/routes-test-stub.d.ts +62 -0
- package/dist/development/lib/dom/ssr/routes-test-stub.js +108 -0
- package/dist/development/lib/dom/ssr/routes.d.ts +33 -0
- package/dist/development/lib/dom/ssr/routes.js +303 -0
- package/dist/development/lib/dom/ssr/server.d.ts +45 -0
- package/dist/development/lib/dom/ssr/server.js +68 -0
- package/dist/development/lib/dom/ssr/single-fetch.d.ts +14 -0
- package/dist/development/lib/dom/ssr/single-fetch.js +346 -0
- package/dist/development/lib/dom-export/dom-router-provider.d.ts +9 -0
- package/dist/development/lib/dom-export/dom-router-provider.js +22 -0
- package/dist/development/lib/dom-export/hydrated-router.d.ts +125 -0
- package/dist/development/lib/dom-export/hydrated-router.js +153 -0
- package/dist/development/lib/errors.js +29 -0
- package/dist/development/lib/hooks.d.ts +947 -0
- package/dist/development/lib/hooks.js +1386 -0
- package/dist/development/lib/href.d.ts +20 -0
- package/dist/development/lib/href.js +50 -0
- package/dist/development/lib/router/history.d.ts +258 -0
- package/dist/development/lib/router/history.js +371 -0
- package/dist/development/lib/router/instrumentation.d.ts +86 -0
- package/dist/development/lib/router/instrumentation.js +213 -0
- package/dist/development/lib/router/links.d.ts +113 -0
- package/dist/development/lib/router/router.d.ts +663 -0
- package/dist/development/lib/router/router.js +2981 -0
- package/dist/development/lib/router/utils.d.ts +942 -0
- package/dist/development/lib/router/utils.js +791 -0
- package/dist/development/lib/rsc/browser.d.ts +137 -0
- package/dist/development/lib/rsc/browser.js +599 -0
- package/dist/development/lib/rsc/errorBoundaries.d.ts +11 -0
- package/dist/development/lib/rsc/errorBoundaries.js +90 -0
- package/dist/development/lib/rsc/html-stream/browser.d.ts +48 -0
- package/dist/development/lib/rsc/html-stream/browser.js +74 -0
- package/dist/development/lib/rsc/html-stream/server.js +78 -0
- package/dist/development/lib/rsc/route-modules.js +27 -0
- package/dist/development/lib/rsc/server.rsc.d.ts +219 -0
- package/dist/development/lib/rsc/server.ssr.d.ts +129 -0
- package/dist/development/lib/rsc/server.ssr.js +388 -0
- package/dist/development/lib/server-runtime/build.d.ts +66 -0
- package/dist/development/lib/server-runtime/cookies.d.ts +66 -0
- package/dist/development/lib/server-runtime/cookies.js +139 -0
- package/dist/development/lib/server-runtime/crypto.js +43 -0
- package/dist/development/lib/server-runtime/data.d.ts +13 -0
- package/dist/development/lib/server-runtime/data.js +25 -0
- package/dist/development/lib/server-runtime/dev.d.ts +9 -0
- package/dist/development/lib/server-runtime/dev.js +26 -0
- package/dist/development/lib/server-runtime/entry.js +20 -0
- package/dist/development/lib/server-runtime/errors.js +95 -0
- package/dist/development/lib/server-runtime/headers.js +73 -0
- package/dist/development/lib/server-runtime/invariant.js +19 -0
- package/dist/development/lib/server-runtime/mode.d.ts +12 -0
- package/dist/development/lib/server-runtime/mode.js +25 -0
- package/dist/development/lib/server-runtime/routeMatching.js +28 -0
- package/dist/development/lib/server-runtime/routes.d.ts +13 -0
- package/dist/development/lib/server-runtime/routes.js +74 -0
- package/dist/development/lib/server-runtime/server.d.ts +10 -0
- package/dist/development/lib/server-runtime/server.js +351 -0
- package/dist/development/lib/server-runtime/serverHandoff.js +17 -0
- package/dist/development/lib/server-runtime/sessions/cookieStorage.d.ts +25 -0
- package/dist/development/lib/server-runtime/sessions/cookieStorage.js +45 -0
- package/dist/development/lib/server-runtime/sessions/memoryStorage.d.ts +23 -0
- package/dist/development/lib/server-runtime/sessions/memoryStorage.js +52 -0
- package/dist/development/lib/server-runtime/sessions.d.ts +145 -0
- package/dist/development/lib/server-runtime/sessions.js +98 -0
- package/dist/development/lib/server-runtime/single-fetch.d.ts +7 -0
- package/dist/development/lib/server-runtime/single-fetch.js +215 -0
- package/dist/development/lib/server-runtime/urls.js +31 -0
- package/dist/development/lib/server-runtime/warnings.js +20 -0
- package/dist/development/lib/types/future.d.ts +9 -0
- package/dist/development/lib/types/internal.d.ts +26 -177
- package/dist/development/lib/types/internal.js +3 -2
- package/dist/{production/register-CNAx3TXj.d.ts → development/lib/types/register.d.ts} +9 -15
- package/dist/development/lib/types/route-data.d.ts +113 -0
- package/dist/development/lib/types/route-module-annotations.d.ts +149 -0
- package/dist/development/lib/types/route-module.d.ts +19 -0
- package/dist/development/lib/types/serializes-to.d.ts +13 -0
- package/dist/development/lib/types/utils.d.ts +11 -0
- package/dist/development/vendor/turbo-stream-v2/flatten.js +159 -0
- package/dist/development/vendor/turbo-stream-v2/turbo-stream.js +178 -0
- package/dist/development/vendor/turbo-stream-v2/unflatten.js +198 -0
- package/dist/development/vendor/turbo-stream-v2/utils.js +47 -0
- package/dist/production/dom-export.d.ts +6 -172
- package/dist/production/dom-export.js +12 -1007
- package/dist/production/index-react-server-client.d.ts +7 -4
- package/dist/production/index-react-server-client.js +8 -52
- package/dist/production/index-react-server.d.ts +1645 -1635
- package/dist/production/index-react-server.js +2871 -3642
- package/dist/production/index.d.ts +43 -1475
- package/dist/production/index.js +37 -2621
- package/dist/production/lib/actions.js +62 -0
- package/dist/production/lib/components.d.ts +1022 -0
- package/dist/production/lib/components.js +835 -0
- package/dist/production/lib/context.d.ts +83 -0
- package/dist/production/lib/context.js +41 -0
- package/dist/production/lib/dom/dom.d.ts +119 -0
- package/dist/production/lib/dom/dom.js +143 -0
- package/dist/production/lib/dom/lib.d.ts +2042 -0
- package/dist/production/lib/dom/lib.js +1259 -0
- package/dist/production/lib/dom/server.d.ts +138 -0
- package/dist/production/lib/dom/server.js +301 -0
- package/dist/production/lib/dom/ssr/components.d.ts +196 -0
- package/dist/production/lib/dom/ssr/components.js +579 -0
- package/dist/production/lib/dom/ssr/data.js +29 -0
- package/dist/production/lib/dom/ssr/entry.d.ts +59 -0
- package/dist/production/lib/dom/ssr/errorBoundaries.d.ts +27 -0
- package/dist/production/lib/dom/ssr/errorBoundaries.js +83 -0
- package/dist/production/lib/dom/ssr/errors.d.ts +7 -0
- package/dist/production/lib/dom/ssr/errors.js +36 -0
- package/dist/production/lib/dom/ssr/fallback.js +21 -0
- package/dist/production/lib/dom/ssr/fog-of-war.d.ts +12 -0
- package/dist/production/lib/dom/ssr/fog-of-war.js +170 -0
- package/dist/production/lib/dom/ssr/hydration.d.ts +32 -0
- package/dist/production/lib/dom/ssr/hydration.js +29 -0
- package/dist/production/lib/dom/ssr/invariant.js +16 -0
- package/dist/production/lib/dom/ssr/links.js +170 -0
- package/dist/production/lib/dom/ssr/markup.js +24 -0
- package/dist/production/lib/dom/ssr/routeModules.d.ts +206 -0
- package/dist/production/lib/dom/ssr/routeModules.js +31 -0
- package/dist/production/lib/dom/ssr/routes-test-stub.d.ts +62 -0
- package/dist/production/lib/dom/ssr/routes-test-stub.js +108 -0
- package/dist/production/lib/dom/ssr/routes.d.ts +33 -0
- package/dist/production/lib/dom/ssr/routes.js +303 -0
- package/dist/production/lib/dom/ssr/server.d.ts +45 -0
- package/dist/production/lib/dom/ssr/server.js +68 -0
- package/dist/production/lib/dom/ssr/single-fetch.d.ts +14 -0
- package/dist/production/lib/dom/ssr/single-fetch.js +346 -0
- package/dist/production/lib/dom-export/dom-router-provider.d.ts +9 -0
- package/dist/production/lib/dom-export/dom-router-provider.js +22 -0
- package/dist/production/lib/dom-export/hydrated-router.d.ts +125 -0
- package/dist/production/lib/dom-export/hydrated-router.js +153 -0
- package/dist/production/lib/errors.js +29 -0
- package/dist/production/lib/hooks.d.ts +947 -0
- package/dist/production/lib/hooks.js +1371 -0
- package/dist/production/lib/href.d.ts +20 -0
- package/dist/production/lib/href.js +50 -0
- package/dist/production/lib/router/history.d.ts +258 -0
- package/dist/production/lib/router/history.js +371 -0
- package/dist/production/lib/router/instrumentation.d.ts +86 -0
- package/dist/production/lib/router/instrumentation.js +213 -0
- package/dist/production/lib/router/links.d.ts +113 -0
- package/dist/production/lib/router/router.d.ts +663 -0
- package/dist/production/lib/router/router.js +2981 -0
- package/dist/production/lib/router/utils.d.ts +942 -0
- package/dist/production/lib/router/utils.js +782 -0
- package/dist/production/lib/rsc/browser.d.ts +137 -0
- package/dist/production/lib/rsc/browser.js +599 -0
- package/dist/production/lib/rsc/errorBoundaries.d.ts +11 -0
- package/dist/production/lib/rsc/errorBoundaries.js +90 -0
- package/dist/production/lib/rsc/html-stream/browser.d.ts +48 -0
- package/dist/production/lib/rsc/html-stream/browser.js +74 -0
- package/dist/production/lib/rsc/html-stream/server.js +78 -0
- package/dist/production/lib/rsc/route-modules.js +27 -0
- package/dist/production/lib/rsc/server.rsc.d.ts +219 -0
- package/dist/production/lib/rsc/server.ssr.d.ts +129 -0
- package/dist/production/lib/rsc/server.ssr.js +388 -0
- package/dist/production/lib/server-runtime/build.d.ts +66 -0
- package/dist/production/lib/server-runtime/cookies.d.ts +66 -0
- package/dist/production/lib/server-runtime/cookies.js +139 -0
- package/dist/production/lib/server-runtime/crypto.js +43 -0
- package/dist/production/lib/server-runtime/data.d.ts +13 -0
- package/dist/production/lib/server-runtime/data.js +25 -0
- package/dist/production/lib/server-runtime/dev.d.ts +9 -0
- package/dist/production/lib/server-runtime/dev.js +26 -0
- package/dist/production/lib/server-runtime/entry.js +20 -0
- package/dist/production/lib/server-runtime/errors.js +95 -0
- package/dist/production/lib/server-runtime/headers.js +73 -0
- package/dist/production/lib/server-runtime/invariant.js +19 -0
- package/dist/production/lib/server-runtime/mode.d.ts +12 -0
- package/dist/production/lib/server-runtime/mode.js +25 -0
- package/dist/production/lib/server-runtime/routeMatching.js +28 -0
- package/dist/production/lib/server-runtime/routes.d.ts +13 -0
- package/dist/production/lib/server-runtime/routes.js +74 -0
- package/dist/production/lib/server-runtime/server.d.ts +10 -0
- package/dist/production/lib/server-runtime/server.js +351 -0
- package/dist/production/lib/server-runtime/serverHandoff.js +17 -0
- package/dist/production/lib/server-runtime/sessions/cookieStorage.d.ts +25 -0
- package/dist/production/lib/server-runtime/sessions/cookieStorage.js +45 -0
- package/dist/production/lib/server-runtime/sessions/memoryStorage.d.ts +23 -0
- package/dist/production/lib/server-runtime/sessions/memoryStorage.js +52 -0
- package/dist/production/lib/server-runtime/sessions.d.ts +145 -0
- package/dist/production/lib/server-runtime/sessions.js +98 -0
- package/dist/production/lib/server-runtime/single-fetch.d.ts +7 -0
- package/dist/production/lib/server-runtime/single-fetch.js +215 -0
- package/dist/production/lib/server-runtime/urls.js +31 -0
- package/dist/production/lib/server-runtime/warnings.js +20 -0
- package/dist/production/lib/types/future.d.ts +9 -0
- package/dist/production/lib/types/internal.d.ts +26 -177
- package/dist/production/lib/types/internal.js +3 -2
- package/dist/{development/register-CNAx3TXj.d.ts → production/lib/types/register.d.ts} +9 -15
- package/dist/production/lib/types/route-data.d.ts +113 -0
- package/dist/production/lib/types/route-module-annotations.d.ts +149 -0
- package/dist/production/lib/types/route-module.d.ts +19 -0
- package/dist/production/lib/types/serializes-to.d.ts +13 -0
- package/dist/production/lib/types/utils.d.ts +11 -0
- package/dist/production/vendor/turbo-stream-v2/flatten.js +159 -0
- package/dist/production/vendor/turbo-stream-v2/turbo-stream.js +178 -0
- package/dist/production/vendor/turbo-stream-v2/unflatten.js +198 -0
- package/dist/production/vendor/turbo-stream-v2/utils.js +47 -0
- package/docs/explanation/backend-for-frontend.md +50 -0
- package/docs/explanation/code-splitting.md +77 -0
- package/docs/explanation/concurrency.md +135 -0
- package/docs/explanation/form-vs-fetcher.md +292 -0
- package/docs/explanation/hot-module-replacement.md +137 -0
- package/docs/explanation/hydration.md +14 -0
- package/docs/explanation/index-query-param.md +86 -0
- package/docs/explanation/index.md +4 -0
- package/docs/explanation/lazy-route-discovery.md +78 -0
- package/docs/explanation/location.md +6 -0
- package/docs/explanation/progressive-enhancement.md +150 -0
- package/docs/explanation/race-conditions.md +88 -0
- package/docs/explanation/react-transitions.md +160 -0
- package/docs/explanation/route-matching.md +7 -0
- package/docs/explanation/server-client-execution.md +4 -0
- package/docs/explanation/sessions-and-cookies.md +465 -0
- package/docs/explanation/special-files.md +16 -0
- package/docs/explanation/state-management.md +524 -0
- package/docs/explanation/styling.md +87 -0
- package/docs/explanation/type-safety.md +82 -0
- package/docs/how-to/accessibility.md +44 -0
- package/docs/how-to/client-data.md +199 -0
- package/docs/how-to/data-strategy.md +317 -0
- package/docs/how-to/error-boundary.md +231 -0
- package/docs/how-to/error-reporting.md +134 -0
- package/docs/how-to/fetchers.md +307 -0
- package/docs/how-to/file-route-conventions.md +410 -0
- package/docs/how-to/file-uploads.md +217 -0
- package/docs/how-to/form-validation.md +120 -0
- package/docs/how-to/headers.md +164 -0
- package/docs/how-to/index.md +4 -0
- package/docs/how-to/instrumentation.md +556 -0
- package/docs/how-to/meta.md +40 -0
- package/docs/how-to/middleware.md +728 -0
- package/docs/how-to/navigation-blocking.md +233 -0
- package/docs/how-to/optimize-revalidation.md +12 -0
- package/docs/how-to/pre-rendering.md +225 -0
- package/docs/how-to/presets.md +103 -0
- package/docs/how-to/react-server-components.md +899 -0
- package/docs/how-to/resource-routes.md +126 -0
- package/docs/how-to/route-module-type-safety.md +100 -0
- package/docs/how-to/search-params.md +4 -0
- package/docs/how-to/security.md +30 -0
- package/docs/how-to/server-bundles.md +66 -0
- package/docs/how-to/spa.md +120 -0
- package/docs/how-to/status.md +63 -0
- package/docs/how-to/suspense.md +132 -0
- package/docs/how-to/using-handle.md +117 -0
- package/docs/how-to/view-transitions.md +237 -0
- package/docs/how-to/webhook.md +50 -0
- package/docs/index.md +39 -0
- package/docs/start/data/actions.md +138 -0
- package/docs/start/data/custom.md +198 -0
- package/docs/start/data/data-loading.md +44 -0
- package/docs/start/data/index.md +4 -0
- package/docs/start/data/installation.md +52 -0
- package/docs/start/data/navigating.md +12 -0
- package/docs/start/data/pending-ui.md +12 -0
- package/docs/start/data/route-object.md +248 -0
- package/docs/start/data/routing.md +281 -0
- package/docs/start/data/testing.md +8 -0
- package/docs/start/declarative/index.md +4 -0
- package/docs/start/declarative/installation.md +43 -0
- package/docs/start/declarative/navigating.md +133 -0
- package/docs/start/declarative/routing.md +237 -0
- package/docs/start/declarative/url-values.md +65 -0
- package/docs/start/framework/actions.md +175 -0
- package/docs/start/framework/data-loading.md +201 -0
- package/docs/start/framework/deploying.md +96 -0
- package/docs/start/framework/index.md +4 -0
- package/docs/start/framework/installation.md +42 -0
- package/docs/start/framework/navigating.md +182 -0
- package/docs/start/framework/pending-ui.md +142 -0
- package/docs/start/framework/rendering.md +59 -0
- package/docs/start/framework/route-module.md +527 -0
- package/docs/start/framework/routing.md +362 -0
- package/docs/start/framework/testing.md +133 -0
- package/docs/start/index.md +4 -0
- package/docs/start/modes.md +201 -0
- package/docs/upgrading/component-routes.md +363 -0
- package/docs/upgrading/future.md +31 -0
- package/docs/upgrading/index.md +4 -0
- package/docs/upgrading/remix.md +403 -0
- package/docs/upgrading/router-provider.md +442 -0
- package/docs/upgrading/v6.md +379 -0
- package/package.json +44 -87
- package/dist/development/browser-D3uq9sI1.d.ts +0 -318
- package/dist/development/browser-nIQ4Nsyi.d.mts +0 -318
- package/dist/development/chunk-IBI7OMNB.js +0 -1363
- package/dist/development/chunk-QUQL4437.mjs +0 -11529
- package/dist/development/chunk-S54KXAEJ.mjs +0 -2585
- package/dist/development/chunk-SRID2YZ2.js +0 -10229
- package/dist/development/chunk-XEJDWL2B.js +0 -188
- package/dist/development/context-m8rizgnE.d.mts +0 -1771
- package/dist/development/data-D4xhSy90.d.ts +0 -1732
- package/dist/development/data-U8FS-wNn.d.mts +0 -1732
- package/dist/development/dom-export.d.mts +0 -172
- package/dist/development/dom-export.mjs +0 -1008
- package/dist/development/index-react-server-client-BLiUx67a.d.ts +0 -3655
- package/dist/development/index-react-server-client-CdKROblb.d.mts +0 -2600
- package/dist/development/index-react-server-client.d.mts +0 -4
- package/dist/development/index-react-server-client.mjs +0 -59
- package/dist/development/index-react-server.d.mts +0 -2703
- package/dist/development/index-react-server.mjs +0 -3780
- package/dist/development/index.d.mts +0 -1478
- package/dist/development/index.mjs +0 -277
- package/dist/development/instrumentation-1q4YhLGP.d.ts +0 -715
- package/dist/development/lib/types/internal.d.mts +0 -184
- package/dist/development/lib/types/internal.mjs +0 -10
- package/dist/development/register-CqK96Zfk.d.mts +0 -30
- package/dist/production/browser-D3uq9sI1.d.ts +0 -318
- package/dist/production/browser-nIQ4Nsyi.d.mts +0 -318
- package/dist/production/chunk-EAQNHM3N.js +0 -188
- package/dist/production/chunk-NALGHHKE.mjs +0 -2585
- package/dist/production/chunk-Q65P7S7Y.mjs +0 -11529
- package/dist/production/chunk-SKEDDLRM.js +0 -1363
- package/dist/production/chunk-Y7DNFQZP.js +0 -10229
- package/dist/production/context-m8rizgnE.d.mts +0 -1771
- package/dist/production/data-D4xhSy90.d.ts +0 -1732
- package/dist/production/data-U8FS-wNn.d.mts +0 -1732
- package/dist/production/dom-export.d.mts +0 -172
- package/dist/production/dom-export.mjs +0 -1008
- package/dist/production/index-react-server-client-BLiUx67a.d.ts +0 -3655
- package/dist/production/index-react-server-client-CdKROblb.d.mts +0 -2600
- package/dist/production/index-react-server-client.d.mts +0 -4
- package/dist/production/index-react-server-client.mjs +0 -59
- package/dist/production/index-react-server.d.mts +0 -2703
- package/dist/production/index-react-server.mjs +0 -3780
- package/dist/production/index.d.mts +0 -1478
- package/dist/production/index.mjs +0 -277
- package/dist/production/instrumentation-1q4YhLGP.d.ts +0 -715
- package/dist/production/lib/types/internal.d.mts +0 -184
- package/dist/production/lib/types/internal.mjs +0 -10
- package/dist/production/register-CqK96Zfk.d.mts +0 -30
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Resource Routes
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Resource Routes
|
|
6
|
+
|
|
7
|
+
[MODES: framework, data]
|
|
8
|
+
|
|
9
|
+
<br/>
|
|
10
|
+
<br/>
|
|
11
|
+
|
|
12
|
+
When server rendering, routes can serve "resources" instead of rendering components, like images, PDFs, JSON payloads, webhooks, etc.
|
|
13
|
+
|
|
14
|
+
## Defining a Resource Route
|
|
15
|
+
|
|
16
|
+
A route becomes a resource route by convention when its module exports a loader or action but does not export a default component.
|
|
17
|
+
|
|
18
|
+
Consider a route that serves a PDF instead of UI:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
route("/reports/pdf/:id", "pdf-report.ts");
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```tsx filename=pdf-report.ts
|
|
25
|
+
import type { Route } from "./+types/pdf-report";
|
|
26
|
+
|
|
27
|
+
export async function loader({ params }: Route.LoaderArgs) {
|
|
28
|
+
const report = await getReport(params.id);
|
|
29
|
+
const pdf = await generateReportPDF(report);
|
|
30
|
+
return new Response(pdf, {
|
|
31
|
+
status: 200,
|
|
32
|
+
headers: {
|
|
33
|
+
"Content-Type": "application/pdf",
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Note there is no default export. That makes this route a resource route.
|
|
40
|
+
|
|
41
|
+
## Linking to Resource Routes
|
|
42
|
+
|
|
43
|
+
When linking to resource routes, use `<a>` or `<Link reloadDocument>`, otherwise React Router will attempt to use client side routing and fetching the payload (you'll get a helpful error message if you make this mistake).
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
<Link reloadDocument to="/reports/pdf/123">
|
|
47
|
+
View as PDF
|
|
48
|
+
</Link>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Handling different request methods
|
|
52
|
+
|
|
53
|
+
GET requests are handled by the `loader`, while POST, PUT, PATCH, and DELETE are handled by the `action`:
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import type { Route } from "./+types/resource";
|
|
57
|
+
|
|
58
|
+
export function loader(_: Route.LoaderArgs) {
|
|
59
|
+
return Response.json({ message: "I handle GET" });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function action(_: Route.ActionArgs) {
|
|
63
|
+
return Response.json({
|
|
64
|
+
message: "I handle everything else",
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Return Types
|
|
70
|
+
|
|
71
|
+
Resource Routes are flexible when it comes to the return type - you can return [`Response`][Response] instances or [`data()`][data] objects. A good general rule of thumb when deciding which type to use is:
|
|
72
|
+
|
|
73
|
+
- If you're using resource routes intended for external consumption, return `Response` instances
|
|
74
|
+
- Keeps the resulting response encoding explicit in your code rather than having to wonder how React Router might convert `data() -> Response` under the hood
|
|
75
|
+
- If you're accessing resource routes from [fetchers][fetcher] or [`<Form>`][form] submissions, return `data()`
|
|
76
|
+
- Keeps things consistent with the loaders/actions in your UI routes
|
|
77
|
+
- Allows you to stream promises down to your UI through `data()`/[`Await`][await]
|
|
78
|
+
|
|
79
|
+
## Error Handling
|
|
80
|
+
|
|
81
|
+
Throwing an `Error` from Resource route (or anything other than a `Response`/`data()`) will trigger [`handleError`][handleError] and result in a 500 HTTP Response:
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
export function action() {
|
|
85
|
+
let db = await getDb();
|
|
86
|
+
if (!db) {
|
|
87
|
+
// Fatal error - return a 500 response and trigger `handleError`
|
|
88
|
+
throw new Error("Could not connect to DB");
|
|
89
|
+
}
|
|
90
|
+
// ...
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
If a resource route generates a `Response` (via `new Response()` or `data()`), it is considered a successful execution and will not trigger `handleError` because the API has successfully produced a Response for the HTTP request. This applies to thrown responses as well as returned responses with a 4xx/5xx status code. This behavior aligns with `fetch()` which does not return a rejected promise on 4xx/5xx Responses.
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
export function action() {
|
|
98
|
+
// Non-fatal error - don't trigger `handleError`:
|
|
99
|
+
throw new Response(
|
|
100
|
+
{ error: "Unauthorized" },
|
|
101
|
+
{ status: 401 },
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
// These 3 are equivalent to the above
|
|
105
|
+
return new Response(
|
|
106
|
+
{ error: "Unauthorized" },
|
|
107
|
+
{ status: 401 },
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
throw data({ error: "Unauthorized" }, { status: 401 });
|
|
111
|
+
|
|
112
|
+
return data({ error: "Unauthorized" }, { status: 401 });
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Error Boundaries
|
|
117
|
+
|
|
118
|
+
[Error Boundaries][error-boundary] are only applicable when a resource route is accessed from a UI, such as from a [`fetcher`][fetcher] call or a [`<Form>`][form] submission. If you `throw` from your resource route in these cases, it will bubble to the nearest `ErrorBoundary` in the UI.
|
|
119
|
+
|
|
120
|
+
[handleError]: ../api/framework-conventions/entry.server.tsx#handleerror
|
|
121
|
+
[data]: ../api/utils/data
|
|
122
|
+
[Response]: https://developer.mozilla.org/en-US/docs/Web/API/Response
|
|
123
|
+
[fetcher]: ../api/hooks/useFetcher
|
|
124
|
+
[form]: ../api/components/Form
|
|
125
|
+
[await]: ../api/components/Await
|
|
126
|
+
[error-boundary]: ../start/framework/route-module#errorboundary
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Route Module Type Safety
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Route Module Type Safety
|
|
6
|
+
|
|
7
|
+
[MODES: framework]
|
|
8
|
+
|
|
9
|
+
<br/>
|
|
10
|
+
<br/>
|
|
11
|
+
|
|
12
|
+
React Router generates route-specific types to power type inference for URL params, loader data, and more.
|
|
13
|
+
This guide will help you set it up if you didn't start with a template.
|
|
14
|
+
|
|
15
|
+
To learn more about how type safety works in React Router, check out [Type Safety Explanation](../explanation/type-safety).
|
|
16
|
+
|
|
17
|
+
## 1. Add `.react-router/` to `.gitignore`
|
|
18
|
+
|
|
19
|
+
React Router generates types into a `.react-router/` directory at the root of your app. This directory is fully managed by React Router and should be gitignore'd.
|
|
20
|
+
|
|
21
|
+
```txt
|
|
22
|
+
.react-router/
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## 2. Include the generated types in tsconfig
|
|
26
|
+
|
|
27
|
+
Edit your tsconfig to get TypeScript to use the generated types. Additionally, `rootDirs` needs to be configured so the types can be imported as relative siblings to route modules.
|
|
28
|
+
|
|
29
|
+
```json filename=tsconfig.json
|
|
30
|
+
{
|
|
31
|
+
"include": [".react-router/types/**/*"],
|
|
32
|
+
"compilerOptions": {
|
|
33
|
+
"rootDirs": [".", "./.react-router/types"]
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
If you are using multiple `tsconfig` files for your app, you'll need to make these changes in whichever one `include`s your app directory.
|
|
39
|
+
For example, the [`node-custom-server` template](https://github.com/remix-run/react-router-templates/tree/390fcec476dd336c810280479688fe893da38713/node-custom-server) contains `tsconfig.json`, `tsconfig.node.json`, and `tsconfig.vite.json`. Since `tsconfig.vite.json` is the one that [includes the app directory](https://github.com/remix-run/react-router-templates/blob/390fcec476dd336c810280479688fe893da38713/node-custom-server/tsconfig.vite.json#L4-L6), that's the one that sets up `.react-router/types` for route module type safety.
|
|
40
|
+
|
|
41
|
+
## 3. Generate types before type checking
|
|
42
|
+
|
|
43
|
+
If you want to run type checking as its own command — for example, as part of your Continuous Integration pipeline — you'll need to make sure to generate types _before_ running typechecking:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"scripts": {
|
|
48
|
+
"typecheck": "react-router typegen && tsc"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 4. Typing `AppLoadContext`
|
|
54
|
+
|
|
55
|
+
## Extending app `Context` types
|
|
56
|
+
|
|
57
|
+
To define your app's `context` type, add the following in a `.ts` or `.d.ts` file within your project:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import "react-router";
|
|
61
|
+
declare module "react-router" {
|
|
62
|
+
interface AppLoadContext {
|
|
63
|
+
// add context properties here
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## 5. Type-only auto-imports (optional)
|
|
69
|
+
|
|
70
|
+
When auto-importing the `Route` type helper, TypeScript will generate:
|
|
71
|
+
|
|
72
|
+
```ts filename=app/routes/my-route.tsx
|
|
73
|
+
import { Route } from "./+types/my-route";
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
But if you enable [verbatimModuleSyntax](https://www.typescriptlang.org/tsconfig/#verbatimModuleSyntax):
|
|
77
|
+
|
|
78
|
+
```json filename=tsconfig.json
|
|
79
|
+
{
|
|
80
|
+
"compilerOptions": {
|
|
81
|
+
"verbatimModuleSyntax": true
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Then, you will get the `type` modifier for the import automatically as well:
|
|
87
|
+
|
|
88
|
+
```ts filename=app/routes/my-route.tsx
|
|
89
|
+
import type { Route } from "./+types/my-route";
|
|
90
|
+
// ^^^^
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
This helps tools like bundlers to detect type-only module that can be safely excluded from the bundle.
|
|
94
|
+
|
|
95
|
+
## Conclusion
|
|
96
|
+
|
|
97
|
+
React Router's Vite plugin should be automatically generating types into `.react-router/types/` anytime you edit your route config (`routes.ts`).
|
|
98
|
+
That means all you need to do is run `react-router dev` (or your custom dev server) to get to up-to-date types in your routes.
|
|
99
|
+
|
|
100
|
+
Check out our [Type Safety Explanation](../explanation/type-safety) for an example of how to pull in those types into your routes.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Security
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Security
|
|
6
|
+
|
|
7
|
+
[MODES: framework]
|
|
8
|
+
|
|
9
|
+
<br/>
|
|
10
|
+
<br/>
|
|
11
|
+
|
|
12
|
+
This is by no means a comprehensive guide, but React Router provides features to help address a few aspects under the _very large_ umbrella that is _Security_.
|
|
13
|
+
|
|
14
|
+
## `Content-Security-Policy`
|
|
15
|
+
|
|
16
|
+
If you are implementing a [Content-Security-Policy (CSP)][csp] in your application, specifically one using the `unsafe-inline` directive, you will need to specify a [`nonce`][nonce] attribute on the inline `<script>` elements rendered in your HTML. This must be specified on any API that generates inline scripts, including:
|
|
17
|
+
|
|
18
|
+
- [`<Scripts nonce>`][scripts] (`root.tsx`)
|
|
19
|
+
- [`<ScrollRestoration nonce>`][scrollrestoration] (`root.tsx`)
|
|
20
|
+
- [`<ServerRouter nonce>`][serverrouter] (`entry.server.tsx`)
|
|
21
|
+
- [`renderToPipeableStream(..., { nonce })`][renderToPipeableStream] (`entry.server.tsx`)
|
|
22
|
+
- [`renderToReadableStream(..., { nonce })`][renderToReadableStream] (`entry.server.tsx`)
|
|
23
|
+
|
|
24
|
+
[csp]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP
|
|
25
|
+
[nonce]: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce
|
|
26
|
+
[renderToPipeableStream]: https://react.dev/reference/react-dom/server/renderToPipeableStream
|
|
27
|
+
[renderToReadableStream]: https://react.dev/reference/react-dom/server/renderToReadableStream
|
|
28
|
+
[scripts]: ../api/components/Scripts
|
|
29
|
+
[scrollrestoration]: ../api/components/ScrollRestoration
|
|
30
|
+
[serverrouter]: ../api/components/ServerRouter
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Server Bundles
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Server Bundles
|
|
6
|
+
|
|
7
|
+
[MODES: framework]
|
|
8
|
+
|
|
9
|
+
<br/>
|
|
10
|
+
<br/>
|
|
11
|
+
|
|
12
|
+
<docs-warning>This is an advanced feature designed for hosting provider integrations. When compiling your app into multiple server bundles, there will need to be a custom routing layer in front of your app directing requests to the correct bundle.</docs-warning>
|
|
13
|
+
|
|
14
|
+
React Router typically builds your server code into a single bundle that exports a request handler function. However, there are scenarios where you might want to split your route tree into multiple server bundles, each exposing a request handler function for a subset of routes. To provide this flexibility, [`react-router.config.ts`][react-router-config] supports a `serverBundles` option, which is a function for assigning routes to different server bundles.
|
|
15
|
+
|
|
16
|
+
The [`serverBundles` function][server-bundles-function] is called for each route in the tree (except for routes that aren't addressable, e.g., pathless layout routes) and returns a server bundle ID that you'd like to assign that route to. These bundle IDs will be used as directory names in your server build directory.
|
|
17
|
+
|
|
18
|
+
For each route, this function receives an array of routes leading to and including that route, referred to as the route `branch`. This allows you to create server bundles for different portions of the route tree. For example, you could use this to create a separate server bundle containing all routes within a particular layout route:
|
|
19
|
+
|
|
20
|
+
```ts filename=react-router.config.ts lines=[5-13]
|
|
21
|
+
import type { Config } from "@react-router/dev/config";
|
|
22
|
+
|
|
23
|
+
export default {
|
|
24
|
+
// ...
|
|
25
|
+
serverBundles: ({ branch }) => {
|
|
26
|
+
const isAuthenticatedRoute = branch.some((route) =>
|
|
27
|
+
route.id.split("/").includes("_authenticated"),
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
return isAuthenticatedRoute
|
|
31
|
+
? "authenticated"
|
|
32
|
+
: "unauthenticated";
|
|
33
|
+
},
|
|
34
|
+
} satisfies Config;
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Each `route` in the `branch` array contains the following properties:
|
|
38
|
+
|
|
39
|
+
- `id` — The unique ID for this route, named like its `file` but relative to the app directory and without the extension, e.g., `app/routes/gists.$username.tsx` will have an `id` of `routes/gists.$username`
|
|
40
|
+
- `path` — The path this route uses to match the URL pathname
|
|
41
|
+
- `file` — The absolute path to the entry point for this route
|
|
42
|
+
- `index` — Whether this route is an index route
|
|
43
|
+
|
|
44
|
+
## Build manifest
|
|
45
|
+
|
|
46
|
+
When the build is complete, React Router will call the `buildEnd` hook, passing a `buildManifest` object. This is useful if you need to inspect the build manifest to determine how to route requests to the correct server bundle.
|
|
47
|
+
|
|
48
|
+
```ts filename=react-router.config.ts lines=[5-7]
|
|
49
|
+
import type { Config } from "@react-router/dev/config";
|
|
50
|
+
|
|
51
|
+
export default {
|
|
52
|
+
// ...
|
|
53
|
+
buildEnd: async ({ buildManifest }) => {
|
|
54
|
+
// ...
|
|
55
|
+
},
|
|
56
|
+
} satisfies Config;
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
When using server bundles, the build manifest contains the following properties:
|
|
60
|
+
|
|
61
|
+
- `serverBundles` — An object that maps bundle IDs to the bundle's `id` and `file`
|
|
62
|
+
- `routeIdToServerBundleId` — An object that maps route IDs to their server bundle ID
|
|
63
|
+
- `routes` — A route manifest that maps route IDs to route metadata. This can be used to drive a custom routing layer in front of your React Router request handlers
|
|
64
|
+
|
|
65
|
+
[react-router-config]: https://api.reactrouter.com/v7/types/_react-router_dev.config.Config.html
|
|
66
|
+
[server-bundles-function]: https://api.reactrouter.com/v7/types/_react-router_dev.config.ServerBundlesFunction.html
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Single Page App (SPA)
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Single Page App (SPA)
|
|
6
|
+
|
|
7
|
+
[MODES: framework]
|
|
8
|
+
|
|
9
|
+
<br/>
|
|
10
|
+
<br/>
|
|
11
|
+
|
|
12
|
+
<docs-info>This guide focuses on how to build Single Page Apps with React Router Framework mode. If you're using React Router in declarative or data mode, you can design your own SPA architecture.</docs-info>
|
|
13
|
+
|
|
14
|
+
When using React Router as a framework, you can enable "SPA Mode" by setting `ssr:false` in your `react-router.config.ts` file. This will disable runtime server rendering and generate an `index.html` at build time that you can serve and hydrate as a SPA.
|
|
15
|
+
|
|
16
|
+
Typical Single Page apps send a mostly blank `index.html` template with little more than an empty `<div id="root"></div>`. In contrast, `react-router build` (in SPA Mode) pre-renders your root route at build time into an `index.html` file. This means you can:
|
|
17
|
+
|
|
18
|
+
- Send more than an empty `<div>`
|
|
19
|
+
- Use a root `loader` to load data for your application shell
|
|
20
|
+
- Use React components to generate the initial page users see (root `HydrateFallback`)
|
|
21
|
+
- Re-enable server rendering later without changing anything about your UI
|
|
22
|
+
|
|
23
|
+
<docs-info>SPA Mode is a special form of "Pre-Rendering" that allows you to serve all paths in your application from the same HTML file. Please refer to the [Pre-Rendering](./pre-rendering) guide if you want to do more extensive pre-rendering.</docs-info>
|
|
24
|
+
|
|
25
|
+
## 1. Disable Runtime Server Rendering
|
|
26
|
+
|
|
27
|
+
Server rendering is enabled by default. Set the `ssr` flag to `false` in `react-router.config.ts` to disable it.
|
|
28
|
+
|
|
29
|
+
```ts filename=react-router.config.ts lines=[4]
|
|
30
|
+
import { type Config } from "@react-router/dev/config";
|
|
31
|
+
|
|
32
|
+
export default {
|
|
33
|
+
ssr: false,
|
|
34
|
+
} satisfies Config;
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
With this set to false, the server build will no longer be generated.
|
|
38
|
+
|
|
39
|
+
<docs-info>It's important to note that setting `ssr:false` only disables _runtime server rendering_. React Router will still server render your root route at _build time_ to generate the `index.html` file. This is why your project still needs a dependency on `@react-router/node` and your routes need to be SSR-safe. That means you can't call `window` or other browser-only APIs during the initial render, even when server rendering is disabled.</docs-info>
|
|
40
|
+
|
|
41
|
+
## 2. Add a `HydrateFallback` and optional `loader` to your root route
|
|
42
|
+
|
|
43
|
+
SPA Mode will generate an `index.html` file at build-time that you can serve as the entry point for your SPA. This will only render the root route so that it is capable of hydrating at runtime for any path in your application.
|
|
44
|
+
|
|
45
|
+
To provide a better loading UI than an empty `<div>`, you can add a `HydrateFallback` component to your root route to render your loading UI into the `index.html` at build time. This way, it will be shown to users immediately while the SPA is loading/hydrating.
|
|
46
|
+
|
|
47
|
+
```tsx filename=root.tsx lines=[7-9]
|
|
48
|
+
import LoadingScreen from "./components/loading-screen";
|
|
49
|
+
|
|
50
|
+
export function Layout() {
|
|
51
|
+
return <html>{/*...*/}</html>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function HydrateFallback() {
|
|
55
|
+
return <LoadingScreen />;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export default function App() {
|
|
59
|
+
return <Outlet />;
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Because the root route is server-rendered at build time, you can also use a `loader` in your root route if you choose. This `loader` will be called at build time and the data will be available via the optional `HydrateFallback` `loaderData` prop.
|
|
64
|
+
|
|
65
|
+
```tsx filename=root.tsx lines=[5,10,14]
|
|
66
|
+
import { Route } from "./+types/root";
|
|
67
|
+
|
|
68
|
+
export async function loader() {
|
|
69
|
+
return {
|
|
70
|
+
version: await getVersion(),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function HydrateFallback({
|
|
75
|
+
loaderData,
|
|
76
|
+
}: Route.ComponentProps) {
|
|
77
|
+
return (
|
|
78
|
+
<div>
|
|
79
|
+
<h1>Loading version {loaderData.version}...</h1>
|
|
80
|
+
<AwesomeSpinner />
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
You cannot include a `loader` in any other routes in your app when using SPA Mode unless you are [pre-rendering those pages](./pre-rendering).
|
|
87
|
+
|
|
88
|
+
## 3. Use client loaders and client actions
|
|
89
|
+
|
|
90
|
+
With server rendering disabled, you can still use `clientLoader` and `clientAction` to manage route data and mutations.
|
|
91
|
+
|
|
92
|
+
```tsx filename=some-route.tsx
|
|
93
|
+
import { Route } from "./+types/some-route";
|
|
94
|
+
|
|
95
|
+
export async function clientLoader({
|
|
96
|
+
params,
|
|
97
|
+
}: Route.ClientLoaderArgs) {
|
|
98
|
+
let data = await fetch(`/some/api/stuff/${params.id}`);
|
|
99
|
+
return data;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function clientAction({
|
|
103
|
+
request,
|
|
104
|
+
}: Route.ClientActionArgs) {
|
|
105
|
+
let formData = await request.formData();
|
|
106
|
+
return await processPayment(formData);
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## 4. Direct all URLs to index.html
|
|
111
|
+
|
|
112
|
+
After running `react-router build`, deploy the `build/client` directory to whatever static host you prefer.
|
|
113
|
+
|
|
114
|
+
Common to deploying any SPA, you'll need to configure your host to direct all URLs to the `index.html` of the client build. Some hosts do this by default, but others don't. As an example, a host may support a `_redirects` file to do this:
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
/* /index.html 200
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
If you're getting 404s at valid routes for your app, it's likely you need to configure your host.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Status Codes
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Status Codes
|
|
6
|
+
|
|
7
|
+
[MODES: framework ,data]
|
|
8
|
+
|
|
9
|
+
<br/>
|
|
10
|
+
<br/>
|
|
11
|
+
|
|
12
|
+
Set status codes from loaders and actions with `data`.
|
|
13
|
+
|
|
14
|
+
```tsx filename=app/project.tsx lines=[3,12-15,20,23]
|
|
15
|
+
// route('/projects/:projectId', './project.tsx')
|
|
16
|
+
import type { Route } from "./+types/project";
|
|
17
|
+
import { data } from "react-router";
|
|
18
|
+
import { fakeDb } from "../db";
|
|
19
|
+
|
|
20
|
+
export async function action({
|
|
21
|
+
request,
|
|
22
|
+
}: Route.ActionArgs) {
|
|
23
|
+
let formData = await request.formData();
|
|
24
|
+
let title = formData.get("title");
|
|
25
|
+
if (!title) {
|
|
26
|
+
return data(
|
|
27
|
+
{ message: "Invalid title" },
|
|
28
|
+
{ status: 400 },
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!projectExists(title)) {
|
|
33
|
+
let project = await fakeDb.createProject({ title });
|
|
34
|
+
return data(project, { status: 201 });
|
|
35
|
+
} else {
|
|
36
|
+
let project = await fakeDb.updateProject({ title });
|
|
37
|
+
// the default status code is 200, no need for `data`
|
|
38
|
+
return project;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
See [Form Validation](./form-validation) for more information on rendering form errors like this.
|
|
44
|
+
|
|
45
|
+
Another common status code is 404:
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
// route('/projects/:projectId', './project.tsx')
|
|
49
|
+
import type { Route } from "./+types/project";
|
|
50
|
+
import { data } from "react-router";
|
|
51
|
+
import { fakeDb } from "../db";
|
|
52
|
+
|
|
53
|
+
export async function loader({ params }: Route.ActionArgs) {
|
|
54
|
+
let project = await fakeDb.getProject(params.id);
|
|
55
|
+
if (!project) {
|
|
56
|
+
// throw to ErrorBoundary
|
|
57
|
+
throw data(null, { status: 404 });
|
|
58
|
+
}
|
|
59
|
+
return project;
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
See the [Error Boundaries](./error-boundary) for more information on thrown `data`.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Streaming with Suspense
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Streaming with Suspense
|
|
6
|
+
|
|
7
|
+
[MODES: framework, data]
|
|
8
|
+
|
|
9
|
+
<br/>
|
|
10
|
+
<br/>
|
|
11
|
+
|
|
12
|
+
Streaming with React Suspense allows apps to speed up initial renders by deferring non-critical data and unblocking UI rendering.
|
|
13
|
+
|
|
14
|
+
React Router supports React Suspense by returning promises from loaders and actions.
|
|
15
|
+
|
|
16
|
+
## 1. Return a promise from loader
|
|
17
|
+
|
|
18
|
+
React Router awaits route loaders before rendering route components. To unblock the loader for non-critical data, return the promise instead of awaiting it in the loader.
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
import type { Route } from "./+types/my-route";
|
|
22
|
+
|
|
23
|
+
export async function loader({}: Route.LoaderArgs) {
|
|
24
|
+
// note this is NOT awaited
|
|
25
|
+
let nonCriticalData = new Promise((res) =>
|
|
26
|
+
setTimeout(() => res("non-critical"), 5000),
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
let criticalData = await new Promise((res) =>
|
|
30
|
+
setTimeout(() => res("critical"), 300),
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
return { nonCriticalData, criticalData };
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Note you can't return a single promise, it must be an object with keys.
|
|
38
|
+
|
|
39
|
+
## 2. Render the fallback and resolved UI
|
|
40
|
+
|
|
41
|
+
The promise will be available on `loaderData`, `<Await>` will await the promise and trigger `<Suspense>` to render the fallback UI.
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import * as React from "react";
|
|
45
|
+
import { Await } from "react-router";
|
|
46
|
+
|
|
47
|
+
// [previous code]
|
|
48
|
+
|
|
49
|
+
export default function MyComponent({
|
|
50
|
+
loaderData,
|
|
51
|
+
}: Route.ComponentProps) {
|
|
52
|
+
let { criticalData, nonCriticalData } = loaderData;
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<div>
|
|
56
|
+
<h1>Streaming example</h1>
|
|
57
|
+
<h2>Critical data value: {criticalData}</h2>
|
|
58
|
+
|
|
59
|
+
<React.Suspense fallback={<div>Loading...</div>}>
|
|
60
|
+
<Await resolve={nonCriticalData}>
|
|
61
|
+
{(value) => <h3>Non critical value: {value}</h3>}
|
|
62
|
+
</Await>
|
|
63
|
+
</React.Suspense>
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## With React 19
|
|
70
|
+
|
|
71
|
+
If you're using React 19, you can use `React.use` instead of `Await`, but you'll need to create a new component and pass the promise down to trigger the suspense fallback.
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
<React.Suspense fallback={<div>Loading...</div>}>
|
|
75
|
+
<NonCriticalUI p={nonCriticalData} />
|
|
76
|
+
</React.Suspense>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
function NonCriticalUI({ p }: { p: Promise<string> }) {
|
|
81
|
+
let value = React.use(p);
|
|
82
|
+
return <h3>Non critical value {value}</h3>;
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Timeouts
|
|
87
|
+
|
|
88
|
+
By default, loaders and actions reject any outstanding promises after 4950ms. You can control this by exporting a `streamTimeout` numerical value from your `entry.server.tsx`.
|
|
89
|
+
|
|
90
|
+
```ts filename=entry.server.tsx
|
|
91
|
+
// Reject all pending promises from handler functions after 10 seconds
|
|
92
|
+
export const streamTimeout = 10_000;
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Handling early rejections (Node)
|
|
96
|
+
|
|
97
|
+
React Router waits for all loaders to settle (via `Promise.all`) before it begins streaming the response. Once streaming has started, React Router catches subsequent rejections of your streamed promises and surfaces them to your `<Await>` (or React 19 `React.use`) error UI.
|
|
98
|
+
|
|
99
|
+
However, if a streamed promise rejects _before_ all of the route's loaders have settled, React Router has not yet been able to attach a handler to it. In Node, an unhandled promise rejection will crash the process unless you have a top-level handler registered.
|
|
100
|
+
|
|
101
|
+
For example, this can happen if a parent route's loader takes longer to resolve than a child route's streamed promise takes to reject:
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
// parent.tsx — slow loader
|
|
105
|
+
export async function loader() {
|
|
106
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
107
|
+
return { parent: "data" };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// child.tsx — fast-rejecting streamed promise
|
|
111
|
+
export async function loader() {
|
|
112
|
+
let lazy = new Promise((_, reject) =>
|
|
113
|
+
setTimeout(() => reject(new Error("boom")), 100),
|
|
114
|
+
);
|
|
115
|
+
return { lazy };
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
When `lazy` rejects before the parent loader resolves, the rejection bubbles to the node process as an unhandled rejection, which will crash the process without a user-defined handler.
|
|
120
|
+
|
|
121
|
+
To prevent this, register a process-level `unhandledRejection` handler in your server entry:
|
|
122
|
+
|
|
123
|
+
```ts filename=entry.server.ts
|
|
124
|
+
process.on("unhandledRejection", (reason, promise) => {
|
|
125
|
+
console.error(
|
|
126
|
+
"Unhandled Rejection at:",
|
|
127
|
+
promise,
|
|
128
|
+
"reason:",
|
|
129
|
+
reason,
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
```
|