@tanstack/react-router 1.121.39 → 1.121.41

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.
@@ -5182,6 +5182,9 @@ export const Route = createFileRoute('/search')({
5182
5182
 
5183
5183
  # SSR
5184
5184
 
5185
+ > [!WARNING]
5186
+ > While every effort has been made to separate these APIs from changes to Tanstack Start, there are underlying shared implementations internally. Therefore these can be subject to change and should be regarded as experimental until Start reaches stable status.
5187
+
5185
5188
  Server Side Rendering (SSR) is the process of rendering a component on the server and sending the HTML markup to the client. The client then hydrates the markup into a fully interactive component.
5186
5189
 
5187
5190
  There are usually two different flavors of SSR to be considered:
@@ -5200,21 +5203,36 @@ Non-Streaming server-side rendering is the classic process of rendering the mark
5200
5203
 
5201
5204
  To implement non-streaming SSR with TanStack Router, you will need the following utilities:
5202
5205
 
5203
- - \`StartServer\` from \`@tanstack/react-start/server\`
5204
- - e.g. \`<StartServer router={router} />\`
5205
- - Rendering this component in your server entry will render your application and also automatically handle application-level hydration/dehydration and implement the \`Wrap\` component option on \`Router\`
5206
- - \`StartClient\` from \`@tanstack/react-start\`
5207
- - e.g. \`<StartClient router={router} />\`
5206
+ - \`RouterClient\` from \`@tanstack/react-router\`
5207
+ - e.g. \`<RouterClient router={router} />\`
5208
5208
  - Rendering this component in your client entry will render your application and also automatically implement the \`Wrap\` component option on \`Router\`
5209
+ - And, either:
5210
+ - \`defaultRenderHandler\` from \`@tanstack/react-router\`
5211
+ - This will render your application in your server entry and also automatically handle application-level hydration/dehydration and also automatically implement the RouterServer component.
5212
+ or:
5213
+ - \`renderRouterToString\` from \`@tanstack/react-router\`
5214
+ - This differs from defaultRenderHandler in that it allows you to manually specify the \`Wrap\` component option on \`Router\` together with any other providers you may need to wrap it with.
5215
+ - \`RouterServer\` from \`@tanstack/react-router\`
5216
+ - This implements the \`Wrap\` component option on \`Router\`
5217
+
5218
+ ### Automatic Server History
5219
+
5220
+ On the client, Router defaults to using an instance of \`createBrowserHistory\`, which is the preferred type of history to use on the client. On the server, however, you will want to use an instance of \`createMemoryHistory\` instead. This is because \`createBrowserHistory\` uses the \`window\` object, which does not exist on the server. This is handled automatically for you in the RouterServer component.
5221
+
5222
+ ### Automatic Loader Dehydration/Hydration
5223
+
5224
+ Resolved loader data fetched by routes is automatically dehydrated and rehydrated by TanStack Router so long as you complete the standard SSR steps outlined in this guide.
5225
+
5226
+ ⚠️ If you are using deferred data streaming, you will also need to ensure that you have implemented the [SSR Streaming & Stream Transform](#streaming-ssr) pattern near the end of this guide.
5227
+
5228
+ For more information on how to utilize data loading, see the [Data Loading](../data-loading.md) guide.
5209
5229
 
5210
5230
  ### Router Creation
5211
5231
 
5212
5232
  Since your router will exist both on the server and the client, it's important that you create your router in a way that is consistent between both of these environments. The easiest way to do this is to expose a \`createRouter\` function in a shared file that can be imported and called by both your server and client entry files.
5213
5233
 
5214
- - \`src/router.tsx\`
5215
-
5216
5234
  \`\`\`tsx
5217
- import * as React from 'react'
5235
+ // src/router.tsx
5218
5236
  import { createRouter as createTanstackRouter } from '@tanstack/react-router'
5219
5237
  import { routeTree } from './routeTree.gen'
5220
5238
 
@@ -5229,153 +5247,131 @@ declare module '@tanstack/react-router' {
5229
5247
  }
5230
5248
  \`\`\`
5231
5249
 
5232
- Now you can import this function in both your server and client entry files and create your router.
5250
+ ### Rendering the Application on the Server
5251
+
5252
+ Now that you have a router instance that has loaded all of the critical data for the current URL, you can render your application on the server:
5233
5253
 
5234
- - \`src/entry-server.tsx\`
5254
+ using \`defaultRenderToString\`
5235
5255
 
5236
5256
  \`\`\`tsx
5257
+ // src/entry-server.tsx
5258
+ import {
5259
+ createRequestHandler,
5260
+ defaultRenderToString,
5261
+ } from '@tanstack/react-router/ssr/server'
5237
5262
  import { createRouter } from './router'
5238
5263
 
5239
- export async function render(req, res) {
5240
- const router = createRouter()
5264
+ export async function render({ request }: { request: Request }) {
5265
+ const handler = createRequestHandler({ request, createRouter })
5266
+
5267
+ return await handler(defaultRenderToString)
5241
5268
  }
5242
5269
  \`\`\`
5243
5270
 
5244
- - \`src/entry-client.tsx\`
5271
+ using \`renderRouterToString\`
5245
5272
 
5246
5273
  \`\`\`tsx
5274
+ // src/entry-server.tsx
5275
+ import {
5276
+ createRequestHandler,
5277
+ renderRouterToString,
5278
+ RouterServer,
5279
+ } from '@tanstack/react-router/ssr/server'
5247
5280
  import { createRouter } from './router'
5248
5281
 
5249
- const router = createRouter()
5250
- \`\`\`
5251
-
5252
- ### Server History
5253
-
5254
- On the client, Router defaults to using an instance of \`createBrowserHistory\`, which is the preferred type of history to use on the client. On the server, however, you will want to use an instance of \`createMemoryHistory\` instead. This is because \`createBrowserHistory\` uses the \`window\` object, which does not exist on the server.
5255
-
5256
- > 🧠 Make sure you initialize your memory history with the server URL that is being rendered.
5282
+ export function render({ request }: { request: Request }) {
5283
+ const handler = createRequestHandler({ request, createRouter })
5257
5284
 
5258
- - \`src/entry-server.tsx\`
5259
-
5260
- \`\`\`tsx
5261
- const router = createRouter()
5262
-
5263
- const memoryHistory = createMemoryHistory({
5264
- initialEntries: [opts.url],
5265
- })
5285
+ return handler(({ request, responseHeaders, router }) =>
5286
+ renderRouterToString({
5287
+ request,
5288
+ responseHeaders,
5289
+ router,
5290
+ children: <RouterServer router={router} />,
5291
+ }),
5292
+ )
5293
+ }
5266
5294
  \`\`\`
5267
5295
 
5268
- After creating the memory history instance, you can update the router to use it.
5296
+ NOTE: The createRequestHandler method requires a web api standard Request object, while the handler method will return a web api standard Response promise.
5269
5297
 
5270
- - \`src/entry-server.tsx\`
5298
+ Should you be using a server framework like Express that uses its own Request and Response objects you would need to convert from the one to the other. Please have a look at the examples for how such an implementation might look like.
5271
5299
 
5272
- \`\`\`tsx
5273
- router.update({
5274
- history: memoryHistory,
5275
- })
5276
- \`\`\`
5300
+ ## Rendering the Application on the Client
5277
5301
 
5278
- ### Loading Critical Router Data on the Server
5302
+ On the client, things are much simpler.
5279
5303
 
5280
- In order to render your application on the server, you will need to ensure that the router has loaded any critical data via it's route loaders. To do this, you can \`await router.load()\` before rendering your application. This will quite literally wait for each of the matching route matches found for this url to run their route's \`loader\` functions in parallel.
5304
+ - Create your router instance
5305
+ - Render your application using the \`<RouterClient />\` component
5281
5306
 
5282
- - \`src/entry-server.tsx\`
5307
+ [//]: # 'ClientEntryFileExample'
5283
5308
 
5284
5309
  \`\`\`tsx
5285
- await router.load()
5286
- \`\`\`
5287
-
5288
- ## Automatic Loader Dehydration/Hydration
5289
-
5290
- Resolved loader data fetched by routes is automatically dehydrated and rehydrated by TanStack Router so long as you complete the standard SSR steps outlined in this guide.
5291
-
5292
- ⚠️ If you are using deferred data streaming, you will also need to ensure that you have implemented the [SSR Streaming & Stream Transform](#streaming-ssr) pattern near the end of this guide.
5293
-
5294
- For more information on how to utilize data loading, see the [Data Loading](../data-loading.md) guide.
5310
+ // src/entry-client.tsx
5311
+ import { hydrateRoot } from 'react-dom/client'
5312
+ import { RouterClient } from '@tanstack/react-router/ssr/client'
5313
+ import { createRouter } from './router'
5295
5314
 
5296
- ### Rendering the Application on the Server
5315
+ const router = createRouter()
5297
5316
 
5298
- Now that you have a router instance that has loaded all of the critical data for the current URL, you can render your application on the server:
5317
+ hydrateRoot(document, <RouterClient router={router} />)
5318
+ \`\`\`
5299
5319
 
5300
- \`\`\`tsx
5301
- // src/entry-server.tsx
5320
+ [//]: # 'ClientEntryFileExample'
5302
5321
 
5303
- const html = ReactDOMServer.renderToString(<StartServer router={router} />)
5304
- \`\`\`
5322
+ With this setup, your application will be rendered on the server and then hydrated on the client!
5305
5323
 
5306
- ### Handling Not Found Errors
5324
+ ## Streaming SSR
5307
5325
 
5308
- \`router\` has a method \`hasNotFoundMatch\` to check if a not-found error has occurred during the rendering process. Use this method to check if a not-found error has occurred and set the response status code accordingly:
5326
+ Streaming SSR is the most modern flavor of SSR and is the process of continuously and incrementally sending HTML markup to the client as it is rendered on the server. This is slightly different from traditional SSR in concept because beyond being able to dehydrate and rehydrate a critical first paint, markup and data with less priority or slower response times can be streamed to the client after the initial render, but in the same request.
5309
5327
 
5310
- \`\`\`tsx
5311
- // src/entry-server.tsx
5312
- if (router.hasNotFoundMatch()) statusCode = 404
5313
- \`\`\`
5328
+ This pattern can be useful for pages that have slow or high-latency data fetching requirements. For example, if you have a page that needs to fetch data from a third-party API, you can stream the critical initial markup and data to the client and then stream the less-critical third-party data to the client as it is resolved.
5314
5329
 
5315
- ### All Together Now!
5330
+ > [!NOTE]
5331
+ > This streaming pattern is all automatic as long as you are using either \`defaultStreamHandler\` or \`renderRouterToStream\`.
5316
5332
 
5317
- Here is a complete example of a server entry file that uses all of the concepts discussed above.
5333
+ using \`defaultStreamHandler\`
5318
5334
 
5319
5335
  \`\`\`tsx
5320
5336
  // src/entry-server.tsx
5321
- import * as React from 'react'
5322
- import ReactDOMServer from 'react-dom/server'
5323
- import { createMemoryHistory } from '@tanstack/react-router'
5324
- import { StartServer } from '@tanstack/react-start/server'
5337
+ import {
5338
+ createRequestHandler,
5339
+ defaultStreamHandler,
5340
+ } from '@tanstack/react-router/ssr/server'
5325
5341
  import { createRouter } from './router'
5326
5342
 
5327
- export async function render(url, response) {
5328
- const router = createRouter()
5329
-
5330
- const memoryHistory = createMemoryHistory({
5331
- initialEntries: [url],
5332
- })
5333
-
5334
- router.update({
5335
- history: memoryHistory,
5336
- })
5337
-
5338
- await router.load()
5339
-
5340
- const appHtml = ReactDOMServer.renderToString(<StartServer router={router} />)
5343
+ export async function render({ request }: { request: Request }) {
5344
+ const handler = createRequestHandler({ request, createRouter })
5341
5345
 
5342
- response.statusCode = router.hasNotFoundMatch() ? 404 : 200
5343
- response.setHeader('Content-Type', 'text/html')
5344
- response.end(\`<!DOCTYPE html>\${appHtml}\`)
5346
+ return await handler(defaultStreamHandler)
5345
5347
  }
5346
5348
  \`\`\`
5347
5349
 
5348
- ## Rendering the Application on the Client
5349
-
5350
- On the client, things are much simpler.
5351
-
5352
- - Create your router instance
5353
- - Render your application using the \`<StartClient />\` component
5350
+ using \`renderRouterToStream\`
5354
5351
 
5355
5352
  \`\`\`tsx
5356
- // src/entry-client.tsx
5357
-
5358
- import * as React from 'react'
5359
- import ReactDOM from 'react-dom/client'
5360
-
5361
- import { StartClient } from '@tanstack/react-start'
5353
+ // src/entry-server.tsx
5354
+ import {
5355
+ createRequestHandler,
5356
+ renderRouterToStream,
5357
+ RouterServer,
5358
+ } from '@tanstack/react-router/ssr/server'
5362
5359
  import { createRouter } from './router'
5363
5360
 
5364
- const router = createRouter()
5361
+ export function render({ request }: { request: Request }) {
5362
+ const handler = createRequestHandler({ request, createRouter })
5365
5363
 
5366
- ReactDOM.hydrateRoot(document, <StartClient router={router} />)
5364
+ return handler(({ request, responseHeaders, router }) =>
5365
+ renderRouterToStream({
5366
+ request,
5367
+ responseHeaders,
5368
+ router,
5369
+ children: <RouterServer router={router} />,
5370
+ }),
5371
+ )
5372
+ }
5367
5373
  \`\`\`
5368
5374
 
5369
- With this setup, your application will be rendered on the server and then hydrated on the client!
5370
-
5371
- ## Streaming SSR
5372
-
5373
- Streaming SSR is the most modern flavor of SSR and is the process of continuously and incrementally sending HTML markup to the client as it is rendered on the server. This is slightly different from traditional SSR in concept because beyond being able to dehydrate and rehydrate a critical first paint, markup and data with less priority or slower response times can be streamed to the client after the initial render, but in the same request.
5374
-
5375
- This pattern can be useful for pages that have slow or high-latency data fetching requirements. For example, if you have a page that needs to fetch data from a third-party API, you can stream the critical initial markup and data to the client and then stream the less-critical third-party data to the client as it is resolved.
5376
-
5377
- **This streaming pattern is all automatic as long as you are using \`renderToPipeableStream\`**.
5378
-
5379
5375
  ## Streaming Dehydration/Hydration
5380
5376
 
5381
5377
  Streaming dehydration/hydration is an advanced pattern that goes beyond markup and allows you to dehydrate and stream any supporting data from the server to the client and rehydrate it on arrival. This is useful for applications that may need to further use/manage the underlying data that was used to render the initial markup on the server.
@@ -5395,22 +5391,6 @@ If you feel that there are other types that should be supported by default, plea
5395
5391
 
5396
5392
  If you are using more complex data types like \`Map\`, \`Set\`, \`BigInt\`, etc, you may need to use a custom serializer to ensure that your type-definitions are accurate and your data is correctly serialized and deserialized. We are currently working on both a more robust serializer and a way to customize the serializer for your application. Open an issue if you are interested in helping out!
5397
5393
 
5398
- <!-- This is where the \`serializer\` option on \`createRouter\` comes in. -->
5399
-
5400
- The Data Serialization API allows the usage of a custom serializer that can allow us to transparently use these data types when communicating across the network.
5401
-
5402
- <!-- The following example shows usage with [SuperJSON](https://github.com/blitz-js/superjson), however, anything that implements [\`Start Serializer\`](../../api/router/RouterOptionsType.md#serializer-property) can be used. -->
5403
-
5404
- \`\`\`tsx
5405
- import { SuperJSON } from 'superjson'
5406
-
5407
- const router = createRouter({
5408
- serializer: SuperJSON,
5409
- })
5410
- \`\`\`
5411
-
5412
- Just like that, TanStack Router will now appropriately use SuperJSON to serialize data across the network.
5413
-
5414
5394
  # Static Route Data
5415
5395
 
5416
5396
  When creating routes, you can optionally specify a \`staticData\` property in the route's options. This object can literally contain anything you want as long as it's synchronously available when you create your route.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/react-router",
3
- "version": "1.121.39",
3
+ "version": "1.121.41",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -80,8 +80,8 @@
80
80
  "tiny-invariant": "^1.3.3",
81
81
  "tiny-warning": "^1.0.3",
82
82
  "isbot": "^5.1.22",
83
- "@tanstack/history": "1.121.34",
84
- "@tanstack/router-core": "1.121.39"
83
+ "@tanstack/router-core": "1.121.40",
84
+ "@tanstack/history": "1.121.34"
85
85
  },
86
86
  "devDependencies": {
87
87
  "@testing-library/jest-dom": "^6.6.3",
package/src/link.tsx CHANGED
@@ -82,6 +82,7 @@ export function useLinkProps<
82
82
  state: _state,
83
83
  mask: _mask,
84
84
  reloadDocument: _reloadDocument,
85
+ unsafeRelative: _unsafeRelative,
85
86
  ...propsSafeToSpread
86
87
  } = rest
87
88