Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/green-rabbits-share.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@tanstack/react-start': patch
---

Fix `@tanstack/react-start/server` imports inside React Server Components by adding a `react-server` export condition that resolves to the request/response APIs without pulling in the SSR renderer entrypoints.

This fixes RSC routes that call `createServerFn` loaders and read request headers in dev with `@vitejs/plugin-rsc` enabled.
21 changes: 21 additions & 0 deletions e2e/react-start/rsc/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { Route as RscSsrFalseRouteImport } from './routes/rsc-ssr-false'
import { Route as RscSsrDataOnlyRouteImport } from './routes/rsc-ssr-data-only'
import { Route as RscSlotsRouteImport } from './routes/rsc-slots'
import { Route as RscSlotJsxArgsRouteImport } from './routes/rsc-slot-jsx-args'
import { Route as RscRequestHeadersRouteImport } from './routes/rsc-request-headers'
import { Route as RscReactCacheRouteImport } from './routes/rsc-react-cache'
import { Route as RscParallelRouteImport } from './routes/rsc-parallel'
import { Route as RscNestedStructureRouteImport } from './routes/rsc-nested-structure'
Expand Down Expand Up @@ -102,6 +103,11 @@ const RscSlotJsxArgsRoute = RscSlotJsxArgsRouteImport.update({
path: '/rsc-slot-jsx-args',
getParentRoute: () => rootRouteImport,
} as any)
const RscRequestHeadersRoute = RscRequestHeadersRouteImport.update({
id: '/rsc-request-headers',
path: '/rsc-request-headers',
getParentRoute: () => rootRouteImport,
} as any)
const RscReactCacheRoute = RscReactCacheRouteImport.update({
id: '/rsc-react-cache',
path: '/rsc-react-cache',
Expand Down Expand Up @@ -291,6 +297,7 @@ export interface FileRoutesByFullPath {
'/rsc-nested-structure': typeof RscNestedStructureRoute
'/rsc-parallel': typeof RscParallelRoute
'/rsc-react-cache': typeof RscReactCacheRoute
'/rsc-request-headers': typeof RscRequestHeadersRoute
'/rsc-slot-jsx-args': typeof RscSlotJsxArgsRoute
'/rsc-slots': typeof RscSlotsRoute
'/rsc-ssr-data-only': typeof RscSsrDataOnlyRoute
Expand Down Expand Up @@ -335,6 +342,7 @@ export interface FileRoutesByTo {
'/rsc-nested-structure': typeof RscNestedStructureRoute
'/rsc-parallel': typeof RscParallelRoute
'/rsc-react-cache': typeof RscReactCacheRoute
'/rsc-request-headers': typeof RscRequestHeadersRoute
'/rsc-slot-jsx-args': typeof RscSlotJsxArgsRoute
'/rsc-slots': typeof RscSlotsRoute
'/rsc-ssr-data-only': typeof RscSsrDataOnlyRoute
Expand Down Expand Up @@ -380,6 +388,7 @@ export interface FileRoutesById {
'/rsc-nested-structure': typeof RscNestedStructureRoute
'/rsc-parallel': typeof RscParallelRoute
'/rsc-react-cache': typeof RscReactCacheRoute
'/rsc-request-headers': typeof RscRequestHeadersRoute
'/rsc-slot-jsx-args': typeof RscSlotJsxArgsRoute
'/rsc-slots': typeof RscSlotsRoute
'/rsc-ssr-data-only': typeof RscSsrDataOnlyRoute
Expand Down Expand Up @@ -426,6 +435,7 @@ export interface FileRouteTypes {
| '/rsc-nested-structure'
| '/rsc-parallel'
| '/rsc-react-cache'
| '/rsc-request-headers'
| '/rsc-slot-jsx-args'
| '/rsc-slots'
| '/rsc-ssr-data-only'
Expand Down Expand Up @@ -470,6 +480,7 @@ export interface FileRouteTypes {
| '/rsc-nested-structure'
| '/rsc-parallel'
| '/rsc-react-cache'
| '/rsc-request-headers'
| '/rsc-slot-jsx-args'
| '/rsc-slots'
| '/rsc-ssr-data-only'
Expand Down Expand Up @@ -514,6 +525,7 @@ export interface FileRouteTypes {
| '/rsc-nested-structure'
| '/rsc-parallel'
| '/rsc-react-cache'
| '/rsc-request-headers'
| '/rsc-slot-jsx-args'
| '/rsc-slots'
| '/rsc-ssr-data-only'
Expand Down Expand Up @@ -559,6 +571,7 @@ export interface RootRouteChildren {
RscNestedStructureRoute: typeof RscNestedStructureRoute
RscParallelRoute: typeof RscParallelRoute
RscReactCacheRoute: typeof RscReactCacheRoute
RscRequestHeadersRoute: typeof RscRequestHeadersRoute
RscSlotJsxArgsRoute: typeof RscSlotJsxArgsRoute
RscSlotsRoute: typeof RscSlotsRoute
RscSsrDataOnlyRoute: typeof RscSsrDataOnlyRoute
Expand Down Expand Up @@ -648,6 +661,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof RscSlotJsxArgsRouteImport
parentRoute: typeof rootRouteImport
}
'/rsc-request-headers': {
id: '/rsc-request-headers'
path: '/rsc-request-headers'
fullPath: '/rsc-request-headers'
preLoaderRoute: typeof RscRequestHeadersRouteImport
parentRoute: typeof rootRouteImport
}
'/rsc-react-cache': {
id: '/rsc-react-cache'
path: '/rsc-react-cache'
Expand Down Expand Up @@ -903,6 +923,7 @@ const rootRouteChildren: RootRouteChildren = {
RscNestedStructureRoute: RscNestedStructureRoute,
RscParallelRoute: RscParallelRoute,
RscReactCacheRoute: RscReactCacheRoute,
RscRequestHeadersRoute: RscRequestHeadersRoute,
RscSlotJsxArgsRoute: RscSlotJsxArgsRoute,
RscSlotsRoute: RscSlotsRoute,
RscSsrDataOnlyRoute: RscSsrDataOnlyRoute,
Expand Down
8 changes: 8 additions & 0 deletions e2e/react-start/rsc/src/routes/__root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,14 @@ function RootComponent() {
>
Global CSS
</Link>
<Link
to="/rsc-request-headers"
className="nav-link"
activeProps={{ className: 'nav-link active' }}
data-testid="nav-request-headers"
>
Request Headers
</Link>
<Link
to="/rsc-react-cache"
className="nav-link"
Expand Down
7 changes: 7 additions & 0 deletions e2e/react-start/rsc/src/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,13 @@ const examples = linkOptions([
'Global CSS in server components - demonstrates plain CSS imports (import "styles.css") within RSC',
icon: '🖌️',
},
{
to: '/rsc-request-headers',
title: 'RSC Request Headers',
description:
'Loader calls a server function that reads the incoming Cookie header in the RSC environment',
icon: '🍪',
},
{
to: '/rsc-react-cache',
title: 'RSC React.cache',
Expand Down
38 changes: 38 additions & 0 deletions e2e/react-start/rsc/src/routes/rsc-request-headers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { createFileRoute } from '@tanstack/react-router'
import { createServerFn } from '@tanstack/react-start'
import { getRequestHeaders } from '@tanstack/react-start/server'
import { pageStyles } from '~/utils/styles'

const getCookies = createServerFn({
method: 'GET',
}).handler(async () => {
return getRequestHeaders().get('cookie') ?? ''
})

export const Route = createFileRoute('/rsc-request-headers')({
loader: async () => {
const cookies = await getCookies()

return {
cookies,
}
},
component: RscRequestHeadersComponent,
})

function RscRequestHeadersComponent() {
const { cookies } = Route.useLoaderData()

return (
<div style={pageStyles.container}>
<h1 data-testid="rsc-request-headers-title" style={pageStyles.title}>
RSC Request Headers
</h1>
<p style={pageStyles.description}>
A route loader calling a server function can read request headers in the
RSC environment.
</p>
<pre data-testid="rsc-request-headers-cookies">{cookies}</pre>
</div>
)
}
28 changes: 28 additions & 0 deletions e2e/react-start/rsc/tests/rsc-request-headers.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { expect } from '@playwright/test'
import { test } from '@tanstack/router-e2e-utils'

test.describe('RSC Request Headers Tests', () => {
test('loader server functions can read cookies with vite-rsc enabled', async ({
page,
context,
}) => {
await context.setExtraHTTPHeaders({
Cookie: 'session=abc123; theme=dark',
})

const response = await page.goto('/rsc-request-headers')
await page.waitForURL('/rsc-request-headers')

expect(response?.status()).toBe(200)

await expect(page.getByTestId('rsc-request-headers-title')).toHaveText(
'RSC Request Headers',
)
await expect(page.getByTestId('rsc-request-headers-cookies')).toContainText(
'session=abc123',
)
await expect(page.getByTestId('rsc-request-headers-cookies')).toContainText(
'theme=dark',
)
})
})
4 changes: 4 additions & 0 deletions packages/react-start/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@
}
},
"./server": {
"react-server": {
"types": "./dist/esm/server.rsc.d.ts",
"default": "./dist/esm/server.rsc.js"
},
"import": {
"types": "./dist/esm/server.d.ts",
"default": "./dist/esm/server.js"
Expand Down
1 change: 1 addition & 0 deletions packages/react-start/src/server.rsc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from '@tanstack/start-server-core'
1 change: 1 addition & 0 deletions packages/react-start/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export default mergeConfig(
'./src/client.tsx',
'./src/client-rpc.ts',
'./src/server.tsx',
'./src/server.rsc.ts',
'./src/server-rpc.ts',
'./src/ssr-rpc.ts',
'./src/rsc.tsx',
Expand Down
Loading