or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

app-router.mdcache-links.mdindex.mdpages-router.mdserver-actions.mdssr.md

ssr.mddocs/

0

# Server-Side Rendering (SSR)

1

2

Advanced SSR capabilities with automatic query prefetching, cache dehydration/hydration, and response meta handling for Next.js Pages Router.

3

4

## Capabilities

5

6

### ssrPrepass Function

7

8

Built-in SSR prepass implementation that automatically prefetches queries during server-side rendering.

9

10

```typescript { .api }

11

/**

12

* Built-in SSR prepass implementation for automatic query prefetching

13

* Handles server-side rendering with query cache dehydration and hydration

14

* @param opts - Configuration object with parent options, components, and prepass helper

15

*/

16

export const ssrPrepass: TRPCPrepassHelper;

17

18

type TRPCPrepassHelper = (opts: {

19

parent: WithTRPCSSROptions<AnyRouter>;

20

WithTRPC: NextComponentType<any, any, any>;

21

AppOrPage: NextComponentType<any, any, any>;

22

}) => void;

23

```

24

25

**Usage Examples:**

26

27

```typescript

28

import { withTRPC } from "@trpc/next";

29

import { httpBatchLink } from "@trpc/client";

30

import { ssrPrepass } from "@trpc/next/ssrPrepass";

31

import type { AppRouter } from "../pages/api/[trpc]";

32

33

const MyApp = ({ Component, pageProps }) => {

34

return <Component {...pageProps} />;

35

};

36

37

export default withTRPC<AppRouter>({

38

config({ ctx }) {

39

return {

40

links: [

41

httpBatchLink({

42

url: "/api/trpc",

43

headers: ctx?.req?.headers,

44

}),

45

],

46

};

47

},

48

ssr: true,

49

ssrPrepass, // Use the built-in prepass implementation

50

})(MyApp);

51

```

52

53

### SSR Configuration Options

54

55

#### SSR Enablement

56

57

Configure when SSR should be enabled for your application.

58

59

```typescript { .api }

60

interface WithTRPCSSROptions<TRouter extends AnyRouter> {

61

/** Enable SSR with optional conditional function */

62

ssr: true | ((opts: { ctx: NextPageContext }) => boolean | Promise<boolean>);

63

}

64

```

65

66

**Usage Examples:**

67

68

```typescript

69

// Always enable SSR

70

const alwaysSSR = {

71

ssr: true,

72

ssrPrepass,

73

config() { /* ... */ }

74

};

75

76

// Conditional SSR based on route

77

const conditionalSSR = {

78

ssr: ({ ctx }) => {

79

// Only SSR for dashboard pages

80

return ctx.pathname?.startsWith("/dashboard") ?? false;

81

},

82

ssrPrepass,

83

config() { /* ... */ }

84

};

85

86

// Async conditional SSR

87

const asyncConditionalSSR = {

88

ssr: async ({ ctx }) => {

89

// Check user agent or other async conditions

90

const userAgent = ctx.req?.headers["user-agent"];

91

return !userAgent?.includes("bot");

92

},

93

ssrPrepass,

94

config() { /* ... */ }

95

};

96

```

97

98

#### Response Meta Handling

99

100

Configure HTTP response status codes and headers based on tRPC errors and application state.

101

102

```typescript { .api }

103

interface WithTRPCSSROptions<TRouter extends AnyRouter> {

104

/** Function to set response meta like headers and status codes */

105

responseMeta?: (opts: {

106

ctx: NextPageContext;

107

clientErrors: TRPCClientError<TRouter>[];

108

}) => ResponseMeta;

109

}

110

111

interface ResponseMeta {

112

status?: number;

113

headers?: Record<string, string>;

114

}

115

```

116

117

**Usage Examples:**

118

119

```typescript

120

const withResponseMeta = {

121

ssr: true,

122

ssrPrepass,

123

config() { /* ... */ },

124

responseMeta({ ctx, clientErrors }) {

125

const firstError = clientErrors[0];

126

127

if (firstError) {

128

// Set appropriate HTTP status based on tRPC error

129

switch (firstError.data?.code) {

130

case "UNAUTHORIZED":

131

return { status: 401 };

132

case "FORBIDDEN":

133

return { status: 403 };

134

case "NOT_FOUND":

135

return { status: 404 };

136

case "INTERNAL_SERVER_ERROR":

137

return { status: 500 };

138

default:

139

return { status: 400 };

140

}

141

}

142

143

// Set security headers

144

return {

145

headers: {

146

"X-Frame-Options": "DENY",

147

"X-Content-Type-Options": "nosniff",

148

},

149

};

150

},

151

};

152

```

153

154

### SSR Context and Props

155

156

#### TRPCPrepassProps

157

158

Props passed to components during the SSR prepass phase.

159

160

```typescript { .api }

161

interface TRPCPrepassProps<TRouter extends AnyRouter, TSSRContext extends NextPageContext = NextPageContext> {

162

/** tRPC client configuration used during prepass */

163

config: WithTRPCConfig<TRouter>;

164

/** React Query client instance */

165

queryClient: QueryClient;

166

/** tRPC client instance for making requests */

167

trpcClient: TRPCUntypedClient<TRouter> | TRPCClient<TRouter>;

168

/** Current SSR state indicator */

169

ssrState: 'prepass';

170

/** Next.js page context during SSR */

171

ssrContext: TSSRContext;

172

}

173

```

174

175

### Custom SSR Prepass Implementation

176

177

For advanced use cases, you can implement custom SSR prepass logic.

178

179

```typescript

180

// Custom prepass example

181

const customPrepass: TRPCPrepassHelper = ({ parent, WithTRPC, AppOrPage }) => {

182

WithTRPC.getInitialProps = async (appOrPageCtx) => {

183

const ctx = appOrPageCtx.ctx;

184

185

// Your custom SSR logic here

186

const config = parent.config({ ctx });

187

const queryClient = getQueryClient(config);

188

const trpcClient = createTRPCUntypedClient(config);

189

190

// Manually prefetch specific queries

191

await queryClient.prefetchQuery({

192

queryKey: ["user", "profile"],

193

queryFn: () => trpcClient.user.getProfile.query(),

194

});

195

196

// Dehydrate and return state

197

const dehydratedState = dehydrate(queryClient);

198

return {

199

pageProps: {

200

trpcState: transformer.input.serialize(dehydratedState),

201

},

202

};

203

};

204

};

205

```

206

207

### SSR Best Practices

208

209

#### Headers and Cookies

210

211

Pass request headers and cookies to maintain session state during SSR.

212

213

```typescript

214

import { httpBatchLink } from "@trpc/client";

215

216

const ssrConfig = {

217

config({ ctx }) {

218

return {

219

links: [

220

httpBatchLink({

221

url: "/api/trpc",

222

headers: {

223

// Forward all request headers

224

...ctx?.req?.headers,

225

// Or specific headers

226

cookie: ctx?.req?.headers?.cookie,

227

authorization: ctx?.req?.headers?.authorization,

228

},

229

}),

230

],

231

};

232

},

233

ssr: true,

234

ssrPrepass,

235

};

236

```

237

238

#### Error Boundaries

239

240

Handle SSR errors gracefully to prevent complete page failures.

241

242

```typescript

243

const robustSSRConfig = {

244

config() { /* ... */ },

245

ssr: async ({ ctx }) => {

246

try {

247

// Check if SSR is appropriate

248

return ctx.pathname !== "/client-only-page";

249

} catch (error) {

250

// Fall back to client-side rendering on error

251

console.error("SSR check failed:", error);

252

return false;

253

}

254

},

255

responseMeta({ clientErrors }) {

256

// Don't fail the entire page for client errors

257

if (clientErrors.length > 0) {

258

console.error("Client errors during SSR:", clientErrors);

259

}

260

return {};

261

},

262

ssrPrepass,

263

};

264

```

265

266

## Types

267

268

```typescript { .api }

269

// Next.js page context type

270

interface NextPageContext {

271

req?: IncomingMessage;

272

res?: ServerResponse;

273

pathname: string;

274

query: ParsedUrlQuery;

275

asPath?: string;

276

locale?: string;

277

locales?: string[];

278

defaultLocale?: string;

279

}

280

281

// tRPC configuration with SSR support

282

interface WithTRPCConfig<TRouter extends AnyRouter> extends CreateTRPCClientOptions<TRouter>, CreateTRPCReactQueryClientConfig {

283

abortOnUnmount?: boolean;

284

}

285

286

// tRPC client error type

287

interface TRPCClientError<TRouter extends AnyRouter> extends Error {

288

data?: {

289

code: string;

290

httpStatus?: number;

291

};

292

shape?: any;

293

}

294

```