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.
Files changed (369) hide show
  1. package/CHANGELOG.md +0 -1
  2. package/dist/development/dom-export.d.ts +6 -172
  3. package/dist/development/dom-export.js +12 -1007
  4. package/dist/development/index-react-server-client.d.ts +7 -4
  5. package/dist/development/index-react-server-client.js +8 -52
  6. package/dist/development/index-react-server.d.ts +1645 -1635
  7. package/dist/development/index-react-server.js +2880 -3642
  8. package/dist/development/index.d.ts +43 -1475
  9. package/dist/development/index.js +37 -2621
  10. package/dist/development/lib/actions.js +62 -0
  11. package/dist/development/lib/components.d.ts +1022 -0
  12. package/dist/development/lib/components.js +835 -0
  13. package/dist/development/lib/context.d.ts +83 -0
  14. package/dist/development/lib/context.js +41 -0
  15. package/dist/development/lib/dom/dom.d.ts +119 -0
  16. package/dist/development/lib/dom/dom.js +143 -0
  17. package/dist/development/lib/dom/lib.d.ts +2042 -0
  18. package/dist/development/lib/dom/lib.js +1259 -0
  19. package/dist/development/lib/dom/server.d.ts +138 -0
  20. package/dist/development/lib/dom/server.js +301 -0
  21. package/dist/development/lib/dom/ssr/components.d.ts +196 -0
  22. package/dist/development/lib/dom/ssr/components.js +579 -0
  23. package/dist/development/lib/dom/ssr/data.js +29 -0
  24. package/dist/development/lib/dom/ssr/entry.d.ts +59 -0
  25. package/dist/development/lib/dom/ssr/errorBoundaries.d.ts +27 -0
  26. package/dist/development/lib/dom/ssr/errorBoundaries.js +83 -0
  27. package/dist/development/lib/dom/ssr/errors.d.ts +7 -0
  28. package/dist/development/lib/dom/ssr/errors.js +36 -0
  29. package/dist/development/lib/dom/ssr/fallback.js +28 -0
  30. package/dist/development/lib/dom/ssr/fog-of-war.d.ts +12 -0
  31. package/dist/development/lib/dom/ssr/fog-of-war.js +170 -0
  32. package/dist/development/lib/dom/ssr/hydration.d.ts +32 -0
  33. package/dist/development/lib/dom/ssr/hydration.js +29 -0
  34. package/dist/development/lib/dom/ssr/invariant.js +16 -0
  35. package/dist/development/lib/dom/ssr/links.js +170 -0
  36. package/dist/development/lib/dom/ssr/markup.js +24 -0
  37. package/dist/development/lib/dom/ssr/routeModules.d.ts +206 -0
  38. package/dist/development/lib/dom/ssr/routeModules.js +31 -0
  39. package/dist/development/lib/dom/ssr/routes-test-stub.d.ts +62 -0
  40. package/dist/development/lib/dom/ssr/routes-test-stub.js +108 -0
  41. package/dist/development/lib/dom/ssr/routes.d.ts +33 -0
  42. package/dist/development/lib/dom/ssr/routes.js +303 -0
  43. package/dist/development/lib/dom/ssr/server.d.ts +45 -0
  44. package/dist/development/lib/dom/ssr/server.js +68 -0
  45. package/dist/development/lib/dom/ssr/single-fetch.d.ts +14 -0
  46. package/dist/development/lib/dom/ssr/single-fetch.js +346 -0
  47. package/dist/development/lib/dom-export/dom-router-provider.d.ts +9 -0
  48. package/dist/development/lib/dom-export/dom-router-provider.js +22 -0
  49. package/dist/development/lib/dom-export/hydrated-router.d.ts +125 -0
  50. package/dist/development/lib/dom-export/hydrated-router.js +153 -0
  51. package/dist/development/lib/errors.js +29 -0
  52. package/dist/development/lib/hooks.d.ts +947 -0
  53. package/dist/development/lib/hooks.js +1386 -0
  54. package/dist/development/lib/href.d.ts +20 -0
  55. package/dist/development/lib/href.js +50 -0
  56. package/dist/development/lib/router/history.d.ts +258 -0
  57. package/dist/development/lib/router/history.js +371 -0
  58. package/dist/development/lib/router/instrumentation.d.ts +86 -0
  59. package/dist/development/lib/router/instrumentation.js +213 -0
  60. package/dist/development/lib/router/links.d.ts +113 -0
  61. package/dist/development/lib/router/router.d.ts +663 -0
  62. package/dist/development/lib/router/router.js +2981 -0
  63. package/dist/development/lib/router/utils.d.ts +942 -0
  64. package/dist/development/lib/router/utils.js +791 -0
  65. package/dist/development/lib/rsc/browser.d.ts +137 -0
  66. package/dist/development/lib/rsc/browser.js +599 -0
  67. package/dist/development/lib/rsc/errorBoundaries.d.ts +11 -0
  68. package/dist/development/lib/rsc/errorBoundaries.js +90 -0
  69. package/dist/development/lib/rsc/html-stream/browser.d.ts +48 -0
  70. package/dist/development/lib/rsc/html-stream/browser.js +74 -0
  71. package/dist/development/lib/rsc/html-stream/server.js +78 -0
  72. package/dist/development/lib/rsc/route-modules.js +27 -0
  73. package/dist/development/lib/rsc/server.rsc.d.ts +219 -0
  74. package/dist/development/lib/rsc/server.ssr.d.ts +129 -0
  75. package/dist/development/lib/rsc/server.ssr.js +388 -0
  76. package/dist/development/lib/server-runtime/build.d.ts +66 -0
  77. package/dist/development/lib/server-runtime/cookies.d.ts +66 -0
  78. package/dist/development/lib/server-runtime/cookies.js +139 -0
  79. package/dist/development/lib/server-runtime/crypto.js +43 -0
  80. package/dist/development/lib/server-runtime/data.d.ts +13 -0
  81. package/dist/development/lib/server-runtime/data.js +25 -0
  82. package/dist/development/lib/server-runtime/dev.d.ts +9 -0
  83. package/dist/development/lib/server-runtime/dev.js +26 -0
  84. package/dist/development/lib/server-runtime/entry.js +20 -0
  85. package/dist/development/lib/server-runtime/errors.js +95 -0
  86. package/dist/development/lib/server-runtime/headers.js +73 -0
  87. package/dist/development/lib/server-runtime/invariant.js +19 -0
  88. package/dist/development/lib/server-runtime/mode.d.ts +12 -0
  89. package/dist/development/lib/server-runtime/mode.js +25 -0
  90. package/dist/development/lib/server-runtime/routeMatching.js +28 -0
  91. package/dist/development/lib/server-runtime/routes.d.ts +13 -0
  92. package/dist/development/lib/server-runtime/routes.js +74 -0
  93. package/dist/development/lib/server-runtime/server.d.ts +10 -0
  94. package/dist/development/lib/server-runtime/server.js +351 -0
  95. package/dist/development/lib/server-runtime/serverHandoff.js +17 -0
  96. package/dist/development/lib/server-runtime/sessions/cookieStorage.d.ts +25 -0
  97. package/dist/development/lib/server-runtime/sessions/cookieStorage.js +45 -0
  98. package/dist/development/lib/server-runtime/sessions/memoryStorage.d.ts +23 -0
  99. package/dist/development/lib/server-runtime/sessions/memoryStorage.js +52 -0
  100. package/dist/development/lib/server-runtime/sessions.d.ts +145 -0
  101. package/dist/development/lib/server-runtime/sessions.js +98 -0
  102. package/dist/development/lib/server-runtime/single-fetch.d.ts +7 -0
  103. package/dist/development/lib/server-runtime/single-fetch.js +215 -0
  104. package/dist/development/lib/server-runtime/urls.js +31 -0
  105. package/dist/development/lib/server-runtime/warnings.js +20 -0
  106. package/dist/development/lib/types/future.d.ts +9 -0
  107. package/dist/development/lib/types/internal.d.ts +26 -177
  108. package/dist/development/lib/types/internal.js +3 -2
  109. package/dist/{production/register-CNAx3TXj.d.ts → development/lib/types/register.d.ts} +9 -15
  110. package/dist/development/lib/types/route-data.d.ts +113 -0
  111. package/dist/development/lib/types/route-module-annotations.d.ts +149 -0
  112. package/dist/development/lib/types/route-module.d.ts +19 -0
  113. package/dist/development/lib/types/serializes-to.d.ts +13 -0
  114. package/dist/development/lib/types/utils.d.ts +11 -0
  115. package/dist/development/vendor/turbo-stream-v2/flatten.js +159 -0
  116. package/dist/development/vendor/turbo-stream-v2/turbo-stream.js +178 -0
  117. package/dist/development/vendor/turbo-stream-v2/unflatten.js +198 -0
  118. package/dist/development/vendor/turbo-stream-v2/utils.js +47 -0
  119. package/dist/production/dom-export.d.ts +6 -172
  120. package/dist/production/dom-export.js +12 -1007
  121. package/dist/production/index-react-server-client.d.ts +7 -4
  122. package/dist/production/index-react-server-client.js +8 -52
  123. package/dist/production/index-react-server.d.ts +1645 -1635
  124. package/dist/production/index-react-server.js +2871 -3642
  125. package/dist/production/index.d.ts +43 -1475
  126. package/dist/production/index.js +37 -2621
  127. package/dist/production/lib/actions.js +62 -0
  128. package/dist/production/lib/components.d.ts +1022 -0
  129. package/dist/production/lib/components.js +835 -0
  130. package/dist/production/lib/context.d.ts +83 -0
  131. package/dist/production/lib/context.js +41 -0
  132. package/dist/production/lib/dom/dom.d.ts +119 -0
  133. package/dist/production/lib/dom/dom.js +143 -0
  134. package/dist/production/lib/dom/lib.d.ts +2042 -0
  135. package/dist/production/lib/dom/lib.js +1259 -0
  136. package/dist/production/lib/dom/server.d.ts +138 -0
  137. package/dist/production/lib/dom/server.js +301 -0
  138. package/dist/production/lib/dom/ssr/components.d.ts +196 -0
  139. package/dist/production/lib/dom/ssr/components.js +579 -0
  140. package/dist/production/lib/dom/ssr/data.js +29 -0
  141. package/dist/production/lib/dom/ssr/entry.d.ts +59 -0
  142. package/dist/production/lib/dom/ssr/errorBoundaries.d.ts +27 -0
  143. package/dist/production/lib/dom/ssr/errorBoundaries.js +83 -0
  144. package/dist/production/lib/dom/ssr/errors.d.ts +7 -0
  145. package/dist/production/lib/dom/ssr/errors.js +36 -0
  146. package/dist/production/lib/dom/ssr/fallback.js +21 -0
  147. package/dist/production/lib/dom/ssr/fog-of-war.d.ts +12 -0
  148. package/dist/production/lib/dom/ssr/fog-of-war.js +170 -0
  149. package/dist/production/lib/dom/ssr/hydration.d.ts +32 -0
  150. package/dist/production/lib/dom/ssr/hydration.js +29 -0
  151. package/dist/production/lib/dom/ssr/invariant.js +16 -0
  152. package/dist/production/lib/dom/ssr/links.js +170 -0
  153. package/dist/production/lib/dom/ssr/markup.js +24 -0
  154. package/dist/production/lib/dom/ssr/routeModules.d.ts +206 -0
  155. package/dist/production/lib/dom/ssr/routeModules.js +31 -0
  156. package/dist/production/lib/dom/ssr/routes-test-stub.d.ts +62 -0
  157. package/dist/production/lib/dom/ssr/routes-test-stub.js +108 -0
  158. package/dist/production/lib/dom/ssr/routes.d.ts +33 -0
  159. package/dist/production/lib/dom/ssr/routes.js +303 -0
  160. package/dist/production/lib/dom/ssr/server.d.ts +45 -0
  161. package/dist/production/lib/dom/ssr/server.js +68 -0
  162. package/dist/production/lib/dom/ssr/single-fetch.d.ts +14 -0
  163. package/dist/production/lib/dom/ssr/single-fetch.js +346 -0
  164. package/dist/production/lib/dom-export/dom-router-provider.d.ts +9 -0
  165. package/dist/production/lib/dom-export/dom-router-provider.js +22 -0
  166. package/dist/production/lib/dom-export/hydrated-router.d.ts +125 -0
  167. package/dist/production/lib/dom-export/hydrated-router.js +153 -0
  168. package/dist/production/lib/errors.js +29 -0
  169. package/dist/production/lib/hooks.d.ts +947 -0
  170. package/dist/production/lib/hooks.js +1371 -0
  171. package/dist/production/lib/href.d.ts +20 -0
  172. package/dist/production/lib/href.js +50 -0
  173. package/dist/production/lib/router/history.d.ts +258 -0
  174. package/dist/production/lib/router/history.js +371 -0
  175. package/dist/production/lib/router/instrumentation.d.ts +86 -0
  176. package/dist/production/lib/router/instrumentation.js +213 -0
  177. package/dist/production/lib/router/links.d.ts +113 -0
  178. package/dist/production/lib/router/router.d.ts +663 -0
  179. package/dist/production/lib/router/router.js +2981 -0
  180. package/dist/production/lib/router/utils.d.ts +942 -0
  181. package/dist/production/lib/router/utils.js +782 -0
  182. package/dist/production/lib/rsc/browser.d.ts +137 -0
  183. package/dist/production/lib/rsc/browser.js +599 -0
  184. package/dist/production/lib/rsc/errorBoundaries.d.ts +11 -0
  185. package/dist/production/lib/rsc/errorBoundaries.js +90 -0
  186. package/dist/production/lib/rsc/html-stream/browser.d.ts +48 -0
  187. package/dist/production/lib/rsc/html-stream/browser.js +74 -0
  188. package/dist/production/lib/rsc/html-stream/server.js +78 -0
  189. package/dist/production/lib/rsc/route-modules.js +27 -0
  190. package/dist/production/lib/rsc/server.rsc.d.ts +219 -0
  191. package/dist/production/lib/rsc/server.ssr.d.ts +129 -0
  192. package/dist/production/lib/rsc/server.ssr.js +388 -0
  193. package/dist/production/lib/server-runtime/build.d.ts +66 -0
  194. package/dist/production/lib/server-runtime/cookies.d.ts +66 -0
  195. package/dist/production/lib/server-runtime/cookies.js +139 -0
  196. package/dist/production/lib/server-runtime/crypto.js +43 -0
  197. package/dist/production/lib/server-runtime/data.d.ts +13 -0
  198. package/dist/production/lib/server-runtime/data.js +25 -0
  199. package/dist/production/lib/server-runtime/dev.d.ts +9 -0
  200. package/dist/production/lib/server-runtime/dev.js +26 -0
  201. package/dist/production/lib/server-runtime/entry.js +20 -0
  202. package/dist/production/lib/server-runtime/errors.js +95 -0
  203. package/dist/production/lib/server-runtime/headers.js +73 -0
  204. package/dist/production/lib/server-runtime/invariant.js +19 -0
  205. package/dist/production/lib/server-runtime/mode.d.ts +12 -0
  206. package/dist/production/lib/server-runtime/mode.js +25 -0
  207. package/dist/production/lib/server-runtime/routeMatching.js +28 -0
  208. package/dist/production/lib/server-runtime/routes.d.ts +13 -0
  209. package/dist/production/lib/server-runtime/routes.js +74 -0
  210. package/dist/production/lib/server-runtime/server.d.ts +10 -0
  211. package/dist/production/lib/server-runtime/server.js +351 -0
  212. package/dist/production/lib/server-runtime/serverHandoff.js +17 -0
  213. package/dist/production/lib/server-runtime/sessions/cookieStorage.d.ts +25 -0
  214. package/dist/production/lib/server-runtime/sessions/cookieStorage.js +45 -0
  215. package/dist/production/lib/server-runtime/sessions/memoryStorage.d.ts +23 -0
  216. package/dist/production/lib/server-runtime/sessions/memoryStorage.js +52 -0
  217. package/dist/production/lib/server-runtime/sessions.d.ts +145 -0
  218. package/dist/production/lib/server-runtime/sessions.js +98 -0
  219. package/dist/production/lib/server-runtime/single-fetch.d.ts +7 -0
  220. package/dist/production/lib/server-runtime/single-fetch.js +215 -0
  221. package/dist/production/lib/server-runtime/urls.js +31 -0
  222. package/dist/production/lib/server-runtime/warnings.js +20 -0
  223. package/dist/production/lib/types/future.d.ts +9 -0
  224. package/dist/production/lib/types/internal.d.ts +26 -177
  225. package/dist/production/lib/types/internal.js +3 -2
  226. package/dist/{development/register-CNAx3TXj.d.ts → production/lib/types/register.d.ts} +9 -15
  227. package/dist/production/lib/types/route-data.d.ts +113 -0
  228. package/dist/production/lib/types/route-module-annotations.d.ts +149 -0
  229. package/dist/production/lib/types/route-module.d.ts +19 -0
  230. package/dist/production/lib/types/serializes-to.d.ts +13 -0
  231. package/dist/production/lib/types/utils.d.ts +11 -0
  232. package/dist/production/vendor/turbo-stream-v2/flatten.js +159 -0
  233. package/dist/production/vendor/turbo-stream-v2/turbo-stream.js +178 -0
  234. package/dist/production/vendor/turbo-stream-v2/unflatten.js +198 -0
  235. package/dist/production/vendor/turbo-stream-v2/utils.js +47 -0
  236. package/docs/explanation/backend-for-frontend.md +50 -0
  237. package/docs/explanation/code-splitting.md +77 -0
  238. package/docs/explanation/concurrency.md +135 -0
  239. package/docs/explanation/form-vs-fetcher.md +292 -0
  240. package/docs/explanation/hot-module-replacement.md +137 -0
  241. package/docs/explanation/hydration.md +14 -0
  242. package/docs/explanation/index-query-param.md +86 -0
  243. package/docs/explanation/index.md +4 -0
  244. package/docs/explanation/lazy-route-discovery.md +78 -0
  245. package/docs/explanation/location.md +6 -0
  246. package/docs/explanation/progressive-enhancement.md +150 -0
  247. package/docs/explanation/race-conditions.md +88 -0
  248. package/docs/explanation/react-transitions.md +160 -0
  249. package/docs/explanation/route-matching.md +7 -0
  250. package/docs/explanation/server-client-execution.md +4 -0
  251. package/docs/explanation/sessions-and-cookies.md +465 -0
  252. package/docs/explanation/special-files.md +16 -0
  253. package/docs/explanation/state-management.md +524 -0
  254. package/docs/explanation/styling.md +87 -0
  255. package/docs/explanation/type-safety.md +82 -0
  256. package/docs/how-to/accessibility.md +44 -0
  257. package/docs/how-to/client-data.md +199 -0
  258. package/docs/how-to/data-strategy.md +317 -0
  259. package/docs/how-to/error-boundary.md +231 -0
  260. package/docs/how-to/error-reporting.md +134 -0
  261. package/docs/how-to/fetchers.md +307 -0
  262. package/docs/how-to/file-route-conventions.md +410 -0
  263. package/docs/how-to/file-uploads.md +217 -0
  264. package/docs/how-to/form-validation.md +120 -0
  265. package/docs/how-to/headers.md +164 -0
  266. package/docs/how-to/index.md +4 -0
  267. package/docs/how-to/instrumentation.md +556 -0
  268. package/docs/how-to/meta.md +40 -0
  269. package/docs/how-to/middleware.md +728 -0
  270. package/docs/how-to/navigation-blocking.md +233 -0
  271. package/docs/how-to/optimize-revalidation.md +12 -0
  272. package/docs/how-to/pre-rendering.md +225 -0
  273. package/docs/how-to/presets.md +103 -0
  274. package/docs/how-to/react-server-components.md +899 -0
  275. package/docs/how-to/resource-routes.md +126 -0
  276. package/docs/how-to/route-module-type-safety.md +100 -0
  277. package/docs/how-to/search-params.md +4 -0
  278. package/docs/how-to/security.md +30 -0
  279. package/docs/how-to/server-bundles.md +66 -0
  280. package/docs/how-to/spa.md +120 -0
  281. package/docs/how-to/status.md +63 -0
  282. package/docs/how-to/suspense.md +132 -0
  283. package/docs/how-to/using-handle.md +117 -0
  284. package/docs/how-to/view-transitions.md +237 -0
  285. package/docs/how-to/webhook.md +50 -0
  286. package/docs/index.md +39 -0
  287. package/docs/start/data/actions.md +138 -0
  288. package/docs/start/data/custom.md +198 -0
  289. package/docs/start/data/data-loading.md +44 -0
  290. package/docs/start/data/index.md +4 -0
  291. package/docs/start/data/installation.md +52 -0
  292. package/docs/start/data/navigating.md +12 -0
  293. package/docs/start/data/pending-ui.md +12 -0
  294. package/docs/start/data/route-object.md +248 -0
  295. package/docs/start/data/routing.md +281 -0
  296. package/docs/start/data/testing.md +8 -0
  297. package/docs/start/declarative/index.md +4 -0
  298. package/docs/start/declarative/installation.md +43 -0
  299. package/docs/start/declarative/navigating.md +133 -0
  300. package/docs/start/declarative/routing.md +237 -0
  301. package/docs/start/declarative/url-values.md +65 -0
  302. package/docs/start/framework/actions.md +175 -0
  303. package/docs/start/framework/data-loading.md +201 -0
  304. package/docs/start/framework/deploying.md +96 -0
  305. package/docs/start/framework/index.md +4 -0
  306. package/docs/start/framework/installation.md +42 -0
  307. package/docs/start/framework/navigating.md +182 -0
  308. package/docs/start/framework/pending-ui.md +142 -0
  309. package/docs/start/framework/rendering.md +59 -0
  310. package/docs/start/framework/route-module.md +527 -0
  311. package/docs/start/framework/routing.md +362 -0
  312. package/docs/start/framework/testing.md +133 -0
  313. package/docs/start/index.md +4 -0
  314. package/docs/start/modes.md +201 -0
  315. package/docs/upgrading/component-routes.md +363 -0
  316. package/docs/upgrading/future.md +31 -0
  317. package/docs/upgrading/index.md +4 -0
  318. package/docs/upgrading/remix.md +403 -0
  319. package/docs/upgrading/router-provider.md +442 -0
  320. package/docs/upgrading/v6.md +379 -0
  321. package/package.json +44 -87
  322. package/dist/development/browser-D3uq9sI1.d.ts +0 -318
  323. package/dist/development/browser-nIQ4Nsyi.d.mts +0 -318
  324. package/dist/development/chunk-IBI7OMNB.js +0 -1363
  325. package/dist/development/chunk-QUQL4437.mjs +0 -11529
  326. package/dist/development/chunk-S54KXAEJ.mjs +0 -2585
  327. package/dist/development/chunk-SRID2YZ2.js +0 -10229
  328. package/dist/development/chunk-XEJDWL2B.js +0 -188
  329. package/dist/development/context-m8rizgnE.d.mts +0 -1771
  330. package/dist/development/data-D4xhSy90.d.ts +0 -1732
  331. package/dist/development/data-U8FS-wNn.d.mts +0 -1732
  332. package/dist/development/dom-export.d.mts +0 -172
  333. package/dist/development/dom-export.mjs +0 -1008
  334. package/dist/development/index-react-server-client-BLiUx67a.d.ts +0 -3655
  335. package/dist/development/index-react-server-client-CdKROblb.d.mts +0 -2600
  336. package/dist/development/index-react-server-client.d.mts +0 -4
  337. package/dist/development/index-react-server-client.mjs +0 -59
  338. package/dist/development/index-react-server.d.mts +0 -2703
  339. package/dist/development/index-react-server.mjs +0 -3780
  340. package/dist/development/index.d.mts +0 -1478
  341. package/dist/development/index.mjs +0 -277
  342. package/dist/development/instrumentation-1q4YhLGP.d.ts +0 -715
  343. package/dist/development/lib/types/internal.d.mts +0 -184
  344. package/dist/development/lib/types/internal.mjs +0 -10
  345. package/dist/development/register-CqK96Zfk.d.mts +0 -30
  346. package/dist/production/browser-D3uq9sI1.d.ts +0 -318
  347. package/dist/production/browser-nIQ4Nsyi.d.mts +0 -318
  348. package/dist/production/chunk-EAQNHM3N.js +0 -188
  349. package/dist/production/chunk-NALGHHKE.mjs +0 -2585
  350. package/dist/production/chunk-Q65P7S7Y.mjs +0 -11529
  351. package/dist/production/chunk-SKEDDLRM.js +0 -1363
  352. package/dist/production/chunk-Y7DNFQZP.js +0 -10229
  353. package/dist/production/context-m8rizgnE.d.mts +0 -1771
  354. package/dist/production/data-D4xhSy90.d.ts +0 -1732
  355. package/dist/production/data-U8FS-wNn.d.mts +0 -1732
  356. package/dist/production/dom-export.d.mts +0 -172
  357. package/dist/production/dom-export.mjs +0 -1008
  358. package/dist/production/index-react-server-client-BLiUx67a.d.ts +0 -3655
  359. package/dist/production/index-react-server-client-CdKROblb.d.mts +0 -2600
  360. package/dist/production/index-react-server-client.d.mts +0 -4
  361. package/dist/production/index-react-server-client.mjs +0 -59
  362. package/dist/production/index-react-server.d.mts +0 -2703
  363. package/dist/production/index-react-server.mjs +0 -3780
  364. package/dist/production/index.d.mts +0 -1478
  365. package/dist/production/index.mjs +0 -277
  366. package/dist/production/instrumentation-1q4YhLGP.d.ts +0 -715
  367. package/dist/production/lib/types/internal.d.mts +0 -184
  368. package/dist/production/lib/types/internal.mjs +0 -10
  369. package/dist/production/register-CqK96Zfk.d.mts +0 -30
@@ -0,0 +1,410 @@
1
+ ---
2
+ title: File Route Conventions
3
+ ---
4
+
5
+ # File Route Conventions
6
+
7
+ [MODES: framework]
8
+
9
+ <br/>
10
+ <br/>
11
+
12
+ The `@react-router/fs-routes` package enables file-convention based route config.
13
+
14
+ ## Setting up
15
+
16
+ First install the `@react-router/fs-routes` package:
17
+
18
+ ```shellscript nonumber
19
+ npm i @react-router/fs-routes
20
+ ```
21
+
22
+ Then use it to provide route config in your `app/routes.ts` file:
23
+
24
+ ```tsx filename=app/routes.ts
25
+ import { type RouteConfig } from "@react-router/dev/routes";
26
+ import { flatRoutes } from "@react-router/fs-routes";
27
+
28
+ export default flatRoutes() satisfies RouteConfig;
29
+ ```
30
+
31
+ Any modules in the `app/routes` directory will become routes in your application by default.
32
+ The `ignoredRouteFiles` option allows you to specify files that should not be included as routes:
33
+
34
+ ```tsx filename=app/routes.ts
35
+ import { type RouteConfig } from "@react-router/dev/routes";
36
+ import { flatRoutes } from "@react-router/fs-routes";
37
+
38
+ export default flatRoutes({
39
+ ignoredRouteFiles: ["home.tsx"],
40
+ }) satisfies RouteConfig;
41
+ ```
42
+
43
+ This will look for routes in the `app/routes` directory by default, but this can be configured via the `rootDirectory` option which is relative to your app directory:
44
+
45
+ ```tsx filename=app/routes.ts
46
+ import { type RouteConfig } from "@react-router/dev/routes";
47
+ import { flatRoutes } from "@react-router/fs-routes";
48
+
49
+ export default flatRoutes({
50
+ rootDirectory: "file-routes",
51
+ }) satisfies RouteConfig;
52
+ ```
53
+
54
+ The rest of this guide will assume you're using the default `app/routes` directory.
55
+
56
+ ## Basic Routes
57
+
58
+ The filename maps to the route's URL pathname, except for `_index.tsx` which is the [index route][index_route] for the [root route][root_route]. You can use `.js`, `.jsx`, `.ts` or `.tsx` file extensions.
59
+
60
+ ```text lines=[3-4]
61
+ app/
62
+ ├── routes/
63
+ │ ├── _index.tsx
64
+ │ └── about.tsx
65
+ └── root.tsx
66
+ ```
67
+
68
+ | URL | Matched Routes |
69
+ | -------- | ----------------------- |
70
+ | `/` | `app/routes/_index.tsx` |
71
+ | `/about` | `app/routes/about.tsx` |
72
+
73
+ Note that these routes will be rendered in the outlet of `app/root.tsx` because of [nested routing][nested_routing].
74
+
75
+ ## Dot Delimiters
76
+
77
+ Adding a `.` to a route filename will create a `/` in the URL.
78
+
79
+ ```text lines=[5-7]
80
+ app/
81
+ ├── routes/
82
+ │ ├── _index.tsx
83
+ │ ├── about.tsx
84
+ │ ├── concerts.trending.tsx
85
+ │ ├── concerts.salt-lake-city.tsx
86
+ │ └── concerts.san-diego.tsx
87
+ └── root.tsx
88
+ ```
89
+
90
+ | URL | Matched Route |
91
+ | -------------------------- | ---------------------------------------- |
92
+ | `/` | `app/routes/_index.tsx` |
93
+ | `/about` | `app/routes/about.tsx` |
94
+ | `/concerts/trending` | `app/routes/concerts.trending.tsx` |
95
+ | `/concerts/salt-lake-city` | `app/routes/concerts.salt-lake-city.tsx` |
96
+ | `/concerts/san-diego` | `app/routes/concerts.san-diego.tsx` |
97
+
98
+ The dot delimiter also creates nesting, see the [nesting section][nested_routes] for more information.
99
+
100
+ ## Dynamic Segments
101
+
102
+ Usually your URLs aren't static but data-driven. Dynamic segments allow you to match segments of the URL and use that value in your code. You create them with the `$` prefix.
103
+
104
+ ```text lines=[5]
105
+ app/
106
+ ├── routes/
107
+ │ ├── _index.tsx
108
+ │ ├── about.tsx
109
+ │ ├── concerts.$city.tsx
110
+ │ └── concerts.trending.tsx
111
+ └── root.tsx
112
+ ```
113
+
114
+ | URL | Matched Route |
115
+ | -------------------------- | ---------------------------------- |
116
+ | `/` | `app/routes/_index.tsx` |
117
+ | `/about` | `app/routes/about.tsx` |
118
+ | `/concerts/trending` | `app/routes/concerts.trending.tsx` |
119
+ | `/concerts/salt-lake-city` | `app/routes/concerts.$city.tsx` |
120
+ | `/concerts/san-diego` | `app/routes/concerts.$city.tsx` |
121
+
122
+ The value will be parsed from the URL and passed to various APIs. We call these values "URL Parameters". The most useful places to access the URL params are in [loaders] and [actions].
123
+
124
+ ```tsx
125
+ export async function loader({ params }) {
126
+ return fakeDb.getAllConcertsForCity(params.city);
127
+ }
128
+ ```
129
+
130
+ You'll note the property name on the `params` object maps directly to the name of your file: `$city.tsx` becomes `params.city`.
131
+
132
+ Routes can have multiple dynamic segments, like `concerts.$city.$date`, both are accessed on the params object by name:
133
+
134
+ ```tsx
135
+ export async function loader({ params }) {
136
+ return fake.db.getConcerts({
137
+ date: params.date,
138
+ city: params.city,
139
+ });
140
+ }
141
+ ```
142
+
143
+ See the [routing guide][routing_guide] for more information.
144
+
145
+ ## Nested Routes
146
+
147
+ Nested Routing is the general idea of coupling segments of the URL to component hierarchy and data. You can read more about it in the [Routing Guide][nested_routing].
148
+
149
+ You create nested routes with [dot delimiters][dot_delimiters]. If the filename before the `.` matches another route filename, it automatically becomes a child route to the matching parent. Consider these routes:
150
+
151
+ ```text lines=[5-8]
152
+ app/
153
+ ├── routes/
154
+ │ ├── _index.tsx
155
+ │ ├── about.tsx
156
+ │ ├── concerts._index.tsx
157
+ │ ├── concerts.$city.tsx
158
+ │ ├── concerts.trending.tsx
159
+ │ └── concerts.tsx
160
+ └── root.tsx
161
+ ```
162
+
163
+ All the routes that start with `app/routes/concerts.` will be child routes of `app/routes/concerts.tsx` and render inside the [parent route's outlet][nested_routing].
164
+
165
+ | URL | Matched Route | Layout |
166
+ | -------------------------- | ---------------------------------- | ------------------------- |
167
+ | `/` | `app/routes/_index.tsx` | `app/root.tsx` |
168
+ | `/about` | `app/routes/about.tsx` | `app/root.tsx` |
169
+ | `/concerts` | `app/routes/concerts._index.tsx` | `app/routes/concerts.tsx` |
170
+ | `/concerts/trending` | `app/routes/concerts.trending.tsx` | `app/routes/concerts.tsx` |
171
+ | `/concerts/salt-lake-city` | `app/routes/concerts.$city.tsx` | `app/routes/concerts.tsx` |
172
+
173
+ Note you typically want to add an index route when you add nested routes so that something renders inside the parent's outlet when users visit the parent URL directly.
174
+
175
+ For example, if the URL is `/concerts/salt-lake-city` then the UI hierarchy will look like this:
176
+
177
+ ```tsx
178
+ <Root>
179
+ <Concerts>
180
+ <City />
181
+ </Concerts>
182
+ </Root>
183
+ ```
184
+
185
+ ## Nested URLs without Layout Nesting
186
+
187
+ Sometimes you want the URL to be nested, but you don't want the automatic layout nesting. You can opt out of nesting with a trailing underscore on the parent segment:
188
+
189
+ ```text lines=[8]
190
+ app/
191
+ ├── routes/
192
+ │ ├── _index.tsx
193
+ │ ├── about.tsx
194
+ │ ├── concerts.$city.tsx
195
+ │ ├── concerts.trending.tsx
196
+ │ ├── concerts.tsx
197
+ │ └── concerts_.mine.tsx
198
+ └── root.tsx
199
+ ```
200
+
201
+ | URL | Matched Route | Layout |
202
+ | -------------------------- | ---------------------------------- | ------------------------- |
203
+ | `/` | `app/routes/_index.tsx` | `app/root.tsx` |
204
+ | `/about` | `app/routes/about.tsx` | `app/root.tsx` |
205
+ | `/concerts/mine` | `app/routes/concerts_.mine.tsx` | `app/root.tsx` |
206
+ | `/concerts/trending` | `app/routes/concerts.trending.tsx` | `app/routes/concerts.tsx` |
207
+ | `/concerts/salt-lake-city` | `app/routes/concerts.$city.tsx` | `app/routes/concerts.tsx` |
208
+
209
+ Note that `/concerts/mine` does not nest with `app/routes/concerts.tsx` anymore, but `app/root.tsx`. The `trailing_` underscore creates a path segment, but it does not create layout nesting.
210
+
211
+ Think of the `trailing_` underscore as the long bit at the end of your parent's signature, writing you out of the will, removing the segment that follows from the layout nesting.
212
+
213
+ ## Nested Layouts without Nested URLs
214
+
215
+ We call these <a name="pathless-routes"><b>Pathless Routes</b></a>
216
+
217
+ Sometimes you want to share a layout with a group of routes without adding any path segments to the URL. A common example is a set of authentication routes that have a different header/footer than the public pages or the logged in app experience. You can do this with a `_leading` underscore.
218
+
219
+ ```text lines=[3-5]
220
+ app/
221
+ ├── routes/
222
+ │ ├── _auth.login.tsx
223
+ │ ├── _auth.register.tsx
224
+ │ ├── _auth.tsx
225
+ │ ├── _index.tsx
226
+ │ ├── concerts.$city.tsx
227
+ │ └── concerts.tsx
228
+ └── root.tsx
229
+ ```
230
+
231
+ | URL | Matched Route | Layout |
232
+ | -------------------------- | ------------------------------- | ------------------------- |
233
+ | `/` | `app/routes/_index.tsx` | `app/root.tsx` |
234
+ | `/login` | `app/routes/_auth.login.tsx` | `app/routes/_auth.tsx` |
235
+ | `/register` | `app/routes/_auth.register.tsx` | `app/routes/_auth.tsx` |
236
+ | `/concerts` | `app/routes/concerts.tsx` | `app/routes/concerts.tsx` |
237
+ | `/concerts/salt-lake-city` | `app/routes/concerts.$city.tsx` | `app/routes/concerts.tsx` |
238
+
239
+ Think of the `_leading` underscore as a blanket you're pulling over the filename, hiding the filename from the URL.
240
+
241
+ ## Optional Segments
242
+
243
+ Wrapping a route segment in parentheses will make the segment optional.
244
+
245
+ ```text lines=[3-5]
246
+ app/
247
+ ├── routes/
248
+ │ ├── ($lang)._index.tsx
249
+ │ ├── ($lang).$productId.tsx
250
+ │ └── ($lang).categories.tsx
251
+ └── root.tsx
252
+ ```
253
+
254
+ | URL | Matched Route |
255
+ | -------------------------- | ----------------------------------- |
256
+ | `/` | `app/routes/($lang)._index.tsx` |
257
+ | `/categories` | `app/routes/($lang).categories.tsx` |
258
+ | `/en/categories` | `app/routes/($lang).categories.tsx` |
259
+ | `/fr/categories` | `app/routes/($lang).categories.tsx` |
260
+ | `/american-flag-speedo` | `app/routes/($lang)._index.tsx` |
261
+ | `/en/american-flag-speedo` | `app/routes/($lang).$productId.tsx` |
262
+ | `/fr/american-flag-speedo` | `app/routes/($lang).$productId.tsx` |
263
+
264
+ You may wonder why `/american-flag-speedo` is matching the `($lang)._index.tsx` route instead of `($lang).$productId.tsx`. This is because when you have an optional dynamic param segment followed by another dynamic param, it cannot reliably be determined if a single-segment URL such as `/american-flag-speedo` should match `/:lang` `/:productId`. Optional segments match eagerly and thus it will match `/:lang`. If you have this type of setup it's recommended to look at `params.lang` in the `($lang)._index.tsx` loader and redirect to `/:lang/american-flag-speedo` for the current/default language if `params.lang` is not a valid language code.
265
+
266
+ ## Splat Routes
267
+
268
+ While [dynamic segments][dynamic_segments] match a single path segment (the stuff between two `/` in a URL), a splat route will match the rest of a URL, including the slashes.
269
+
270
+ ```text lines=[4,6]
271
+ app/
272
+ ├── routes/
273
+ │ ├── _index.tsx
274
+ │ ├── $.tsx
275
+ │ ├── about.tsx
276
+ │ └── files.$.tsx
277
+ └── root.tsx
278
+ ```
279
+
280
+ | URL | Matched Route |
281
+ | -------------------------------------------- | ------------------------ |
282
+ | `/` | `app/routes/_index.tsx` |
283
+ | `/about` | `app/routes/about.tsx` |
284
+ | `/beef/and/cheese` | `app/routes/$.tsx` |
285
+ | `/files` | `app/routes/files.$.tsx` |
286
+ | `/files/talks/react-conf_old.pdf` | `app/routes/files.$.tsx` |
287
+ | `/files/talks/react-conf_final.pdf` | `app/routes/files.$.tsx` |
288
+ | `/files/talks/react-conf-FINAL-MAY_2024.pdf` | `app/routes/files.$.tsx` |
289
+
290
+ Similar to dynamic route parameters, you can access the value of the matched path on the splat route's `params` with the `"*"` key.
291
+
292
+ ```tsx filename=app/routes/files.$.tsx
293
+ export async function loader({ params }) {
294
+ const filePath = params["*"];
295
+ return fake.getFileInfo(filePath);
296
+ }
297
+ ```
298
+
299
+ ## Catch-all Route
300
+
301
+ To create a route that will match any requests that don't match other defined routes (such as a 404 page), create a file named `$.tsx` within your routes directory:
302
+
303
+ | URL | Matched Route |
304
+ | ------------------------------ | ----------------------- |
305
+ | `/` | `app/routes/_index.tsx` |
306
+ | `/about` | `app/routes/about.tsx` |
307
+ | `/any-invalid-path-will-match` | `app/routes/$.tsx` |
308
+
309
+ By default the matched route will return a 200 response, so be sure to modify your catchall route to return a 404 instead:
310
+
311
+ ```tsx filename=app/routes/$.tsx
312
+ export async function loader() {
313
+ return data({}, 404);
314
+ }
315
+ ```
316
+
317
+ ## Escaping Special Characters
318
+
319
+ If you want one of the special characters used for these route conventions to actually be a part of the URL, you can escape the conventions with `[]` characters. This can be especially helpful for [resource routes][resource_routes] that include an extension in the URL.
320
+
321
+ | Filename | URL |
322
+ | ----------------------------------- | ------------------- |
323
+ | `app/routes/sitemap[.]xml.tsx` | `/sitemap.xml` |
324
+ | `app/routes/[sitemap.xml].tsx` | `/sitemap.xml` |
325
+ | `app/routes/weird-url.[_index].tsx` | `/weird-url/_index` |
326
+ | `app/routes/dolla-bills-[$].tsx` | `/dolla-bills-$` |
327
+ | `app/routes/[[so-weird]].tsx` | `/[so-weird]` |
328
+ | `app/routes/reports.$id[.pdf].ts` | `/reports/123.pdf` |
329
+
330
+ ## Folders for Organization
331
+
332
+ Routes can also be folders with a `route.tsx` file inside defining the route module. The rest of the files in the folder will not become routes. This allows you to organize your code closer to the routes that use them instead of repeating the feature names across other folders.
333
+
334
+ The files inside a folder have no meaning for the route paths, the route path is completely defined by the folder name.
335
+
336
+ Consider these routes:
337
+
338
+ ```text
339
+ app/
340
+ ├── routes/
341
+ │ ├── _landing._index.tsx
342
+ │ ├── _landing.about.tsx
343
+ │ ├── _landing.tsx
344
+ │ ├── app._index.tsx
345
+ │ ├── app.projects.tsx
346
+ │ ├── app.tsx
347
+ │ └── app_.projects.$id.roadmap.tsx
348
+ └── root.tsx
349
+ ```
350
+
351
+ Some, or all of them can be folders holding their own `route` module inside.
352
+
353
+ ```text
354
+ app/
355
+ ├── routes/
356
+ │ ├── _landing._index/
357
+ │ │ ├── route.tsx
358
+ │ │ └── scroll-experience.tsx
359
+ │ ├── _landing.about/
360
+ │ │ ├── employee-profile-card.tsx
361
+ │ │ ├── get-employee-data.server.ts
362
+ │ │ ├── route.tsx
363
+ │ │ └── team-photo.jpg
364
+ │ ├── _landing/
365
+ │ │ ├── footer.tsx
366
+ │ │ ├── header.tsx
367
+ │ │ └── route.tsx
368
+ │ ├── app._index/
369
+ │ │ ├── route.tsx
370
+ │ │ └── stats.tsx
371
+ │ ├── app.projects/
372
+ │ │ ├── get-projects.server.ts
373
+ │ │ ├── project-buttons.tsx
374
+ │ │ ├── project-card.tsx
375
+ │ │ └── route.tsx
376
+ │ ├── app/
377
+ │ │ ├── footer.tsx
378
+ │ │ ├── primary-nav.tsx
379
+ │ │ └── route.tsx
380
+ │ ├── app_.projects.$id.roadmap/
381
+ │ │ ├── chart.tsx
382
+ │ │ ├── route.tsx
383
+ │ │ └── update-timeline.server.ts
384
+ │ └── contact-us.tsx
385
+ └── root.tsx
386
+ ```
387
+
388
+ Note that when you turn a route module into a folder, the route module becomes `folder/route.tsx`, all other modules in the folder will not become routes. For example:
389
+
390
+ ```
391
+ # these are the same route:
392
+ app/routes/app.tsx
393
+ app/routes/app/route.tsx
394
+
395
+ # as are these
396
+ app/routes/app._index.tsx
397
+ app/routes/app._index/route.tsx
398
+ ```
399
+
400
+ [route-config-file]: ../start/framework/routing#configuring-routes
401
+ [loaders]: ../start/framework/data-loading
402
+ [actions]: ../start/framework/actions
403
+ [routing_guide]: ../start/framework/routing
404
+ [root_route]: ../start/framework/route-module
405
+ [index_route]: ../start/framework/routing#index-routes
406
+ [nested_routing]: ../start/framework/routing#nested-routes
407
+ [nested_routes]: #nested-routes
408
+ [dot_delimiters]: #dot-delimiters
409
+ [dynamic_segments]: #dynamic-segments
410
+ [resource_routes]: ../how-to/resource-routes
@@ -0,0 +1,217 @@
1
+ ---
2
+ title: File Uploads
3
+ ---
4
+
5
+ # File Uploads
6
+
7
+ [MODES: framework]
8
+
9
+ <br/>
10
+ <br/>
11
+
12
+ _Thank you to David Adams for [writing an original guide](https://programmingarehard.com/2024/09/06/remix-file-uploads-updated.html/) on which this doc is based. You can refer to it for even more examples._
13
+
14
+ ## Basic File Upload
15
+
16
+ ### 1. Setup some routes
17
+
18
+ You can setup your routes however you like. This example uses the following structure:
19
+
20
+ ```ts filename=routes.ts
21
+ import {
22
+ type RouteConfig,
23
+ route,
24
+ } from "@react-router/dev/routes";
25
+
26
+ export default [
27
+ // ... other routes
28
+ route("user/:id", "pages/user-profile.tsx", [
29
+ route("avatar", "api/avatar.tsx"),
30
+ ]),
31
+ ] satisfies RouteConfig;
32
+ ```
33
+
34
+ ### 2. Add the form data parser
35
+
36
+ `form-data-parser` is a wrapper around `request.formData()` that provides streaming support for handling file uploads.
37
+
38
+ ```shellscript
39
+ npm i @remix-run/form-data-parser
40
+ ```
41
+
42
+ [See the `form-data-parser` docs for more information][form-data-parser]
43
+
44
+ ### 3. Create a route with an upload action
45
+
46
+ The `parseFormData` function takes an `uploadHandler` function as an argument. This function will be called for each file upload in the form.
47
+
48
+ <docs-warning>
49
+
50
+ You must set the form's `enctype` to `multipart/form-data` for file uploads to work.
51
+
52
+ </docs-warning>
53
+
54
+ ```tsx filename=pages/user-profile.tsx
55
+ import {
56
+ type FileUpload,
57
+ parseFormData,
58
+ } from "@remix-run/form-data-parser";
59
+ import type { Route } from "./+types/user-profile";
60
+
61
+ export async function action({
62
+ request,
63
+ }: Route.ActionArgs) {
64
+ const uploadHandler = async (fileUpload: FileUpload) => {
65
+ if (fileUpload.fieldName === "avatar") {
66
+ // process the upload and return a File
67
+ }
68
+ };
69
+
70
+ const formData = await parseFormData(
71
+ request,
72
+ uploadHandler,
73
+ );
74
+ // 'avatar' has already been processed at this point
75
+ const file = formData.get("avatar");
76
+ }
77
+
78
+ export default function Component() {
79
+ return (
80
+ <form method="post" encType="multipart/form-data">
81
+ <input type="file" name="avatar" />
82
+ <button>Submit</button>
83
+ </form>
84
+ );
85
+ }
86
+ ```
87
+
88
+ ## Local Storage Implementation
89
+
90
+ ### 1. Add the storage package
91
+
92
+ `file-storage` is a key/value interface for storing [File objects][file] in JavaScript. Similar to how `localStorage` allows you to store key/value pairs of strings in the browser, file-storage allows you to store key/value pairs of files on the server.
93
+
94
+ ```shellscript
95
+ npm i @remix-run/file-storage
96
+ ```
97
+
98
+ [See the `file-storage` docs for more information][file-storage]
99
+
100
+ ### 2. Create a storage configuration
101
+
102
+ Create a file that exports a `LocalFileStorage` instance to be used by different routes.
103
+
104
+ ```ts filename=avatar-storage.server.ts
105
+ import { LocalFileStorage } from "@remix-run/file-storage/local";
106
+
107
+ export const fileStorage = new LocalFileStorage(
108
+ "./uploads/avatars",
109
+ );
110
+
111
+ export function getStorageKey(userId: string) {
112
+ return `user-${userId}-avatar`;
113
+ }
114
+ ```
115
+
116
+ ### 3. Implement the upload handler
117
+
118
+ Update the form's `action` to store files in the `fileStorage` instance.
119
+
120
+ ```tsx filename=pages/user-profile.tsx
121
+ import {
122
+ type FileUpload,
123
+ parseFormData,
124
+ } from "@remix-run/form-data-parser";
125
+ import {
126
+ fileStorage,
127
+ getStorageKey,
128
+ } from "~/avatar-storage.server";
129
+ import type { Route } from "./+types/user-profile";
130
+
131
+ export async function action({
132
+ request,
133
+ params,
134
+ }: Route.ActionArgs) {
135
+ async function uploadHandler(fileUpload: FileUpload) {
136
+ if (
137
+ fileUpload.fieldName === "avatar" &&
138
+ fileUpload.type.startsWith("image/")
139
+ ) {
140
+ let storageKey = getStorageKey(params.id);
141
+
142
+ // FileUpload objects are not meant to stick around for very long (they are
143
+ // streaming data from the request.body); store them as soon as possible.
144
+ await fileStorage.set(storageKey, fileUpload);
145
+
146
+ // Return a File for the FormData object. This is a LazyFile that knows how
147
+ // to access the file's content if needed (using e.g. file.stream()) but
148
+ // waits until it is requested to actually read anything.
149
+ return fileStorage.get(storageKey);
150
+ }
151
+ }
152
+
153
+ const formData = await parseFormData(
154
+ request,
155
+ uploadHandler,
156
+ );
157
+ }
158
+
159
+ export default function UserPage({
160
+ actionData,
161
+ params,
162
+ }: Route.ComponentProps) {
163
+ return (
164
+ <div>
165
+ <h1>User {params.id}</h1>
166
+ <form
167
+ method="post"
168
+ // The form's enctype must be set to "multipart/form-data" for file uploads
169
+ encType="multipart/form-data"
170
+ >
171
+ <input type="file" name="avatar" accept="image/*" />
172
+ <button>Submit</button>
173
+ </form>
174
+
175
+ <img
176
+ src={`/user/${params.id}/avatar`}
177
+ alt="user avatar"
178
+ />
179
+ </div>
180
+ );
181
+ }
182
+ ```
183
+
184
+ ### 4. Add a route to serve the uploaded file
185
+
186
+ Create a [resource route][resource-route] that streams the file as a response.
187
+
188
+ ```tsx filename=api/avatar.tsx
189
+ import {
190
+ fileStorage,
191
+ getStorageKey,
192
+ } from "~/avatar-storage.server";
193
+ import type { Route } from "./+types/avatar";
194
+
195
+ export async function loader({ params }: Route.LoaderArgs) {
196
+ const storageKey = getStorageKey(params.id);
197
+ const file = await fileStorage.get(storageKey);
198
+
199
+ if (!file) {
200
+ throw new Response("User avatar not found", {
201
+ status: 404,
202
+ });
203
+ }
204
+
205
+ return new Response(file.stream(), {
206
+ headers: {
207
+ "Content-Type": file.type,
208
+ "Content-Disposition": `attachment; filename=${file.name}`,
209
+ },
210
+ });
211
+ }
212
+ ```
213
+
214
+ [form-data-parser]: https://www.npmjs.com/package/@remix-run/form-data-parser
215
+ [file-storage]: https://www.npmjs.com/package/@remix-run/file-storage
216
+ [file]: https://developer.mozilla.org/en-US/docs/Web/API/File
217
+ [resource-route]: ../how-to/resource-routes