or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

data-loading-hooks.mdindex.mdnavigation-components.mdnavigation-hooks.mdroute-configuration.mdrouter-components.mdrouter-creation.mdserver-side-rendering.mdutilities.md

server-side-rendering.mddocs/

0

# Server-Side Rendering

1

2

Components and utilities for server-side rendering including static routing, data loading on the server, and hydration support.

3

4

## Capabilities

5

6

### StaticRouter

7

8

Router component for server-side rendering with a fixed location.

9

10

```typescript { .api }

11

/**

12

* Router component for server-side rendering

13

* @param props - StaticRouter configuration options

14

* @returns Router component with static location

15

*/

16

function StaticRouter(props: StaticRouterProps): JSX.Element;

17

18

interface StaticRouterProps {

19

/** Base URL for all routes */

20

basename?: string;

21

/** Child routes and components */

22

children?: React.ReactNode;

23

/** Current location for rendering */

24

location: Partial<Location> | string;

25

}

26

27

interface Location {

28

pathname: string;

29

search: string;

30

hash: string;

31

state: any;

32

key: string;

33

}

34

```

35

36

**Usage Example:**

37

38

```tsx

39

import { StaticRouter } from "react-router-dom";

40

import { renderToString } from "react-dom/server";

41

42

// Server-side rendering

43

export function render(request: Request) {

44

const html = renderToString(

45

<StaticRouter location={request.url}>

46

<App />

47

</StaticRouter>

48

);

49

50

return `

51

<!DOCTYPE html>

52

<html>

53

<head><title>My App</title></head>

54

<body>

55

<div id="root">${html}</div>

56

<script src="/client.js"></script>

57

</body>

58

</html>

59

`;

60

}

61

```

62

63

### StaticRouterProvider

64

65

Provider component for static routers with data loading capabilities.

66

67

```typescript { .api }

68

/**

69

* Provider component for static routers with data support

70

* @param props - StaticRouterProvider configuration

71

* @returns Router provider with static handler context

72

*/

73

function StaticRouterProvider(props: StaticRouterProviderProps): JSX.Element;

74

75

interface StaticRouterProviderProps {

76

/** Router instance from createStaticRouter */

77

router: DataRouter;

78

/** Static handler context with data */

79

context: StaticHandlerContext;

80

/** Whether to hydrate on client */

81

hydrate?: boolean;

82

/** Nonce for CSP */

83

nonce?: string;

84

}

85

86

interface StaticHandlerContext {

87

/** Base URL path */

88

basename: string;

89

/** Current location */

90

location: Location;

91

/** Matched routes */

92

matches: StaticHandlerMatch[];

93

/** Data from route loaders */

94

loaderData: Record<string, any>;

95

/** Data from route actions */

96

actionData: Record<string, any> | null;

97

/** Route errors */

98

errors: Record<string, any> | null;

99

/** HTTP status code */

100

statusCode: number;

101

/** Response headers from loaders */

102

loaderHeaders: Record<string, Headers>;

103

/** Response headers from actions */

104

actionHeaders: Record<string, Headers> | null;

105

/** Deferred data promises */

106

activeDeferreds: Record<string, DeferredData> | null;

107

}

108

```

109

110

**Usage Example:**

111

112

```tsx

113

import {

114

createStaticRouter,

115

createStaticHandler,

116

StaticRouterProvider

117

} from "react-router-dom";

118

119

export async function render(request: Request) {

120

const handler = createStaticHandler(routes);

121

const context = await handler.query(request);

122

123

if (context instanceof Response) {

124

return context; // Handle redirects

125

}

126

127

const router = createStaticRouter(routes, context.location);

128

129

const html = renderToString(

130

<StaticRouterProvider router={router} context={context} />

131

);

132

133

return new Response(html, {

134

status: context.statusCode,

135

headers: { "Content-Type": "text/html" },

136

});

137

}

138

```

139

140

### HydratedRouter

141

142

Specialized router for React Server Components and framework-mode applications that hydrates from server state.

143

144

```typescript { .api }

145

/**

146

* Framework-mode router for hydrating from server state

147

* @param props - HydratedRouter configuration options

148

* @returns Hydrated router component for SSR applications

149

*/

150

function HydratedRouter(props: HydratedRouterProps): JSX.Element;

151

152

interface HydratedRouterProps {

153

/** Context function for clientAction/clientLoader */

154

unstable_getContext?: RouterInit["unstable_getContext"];

155

/** Error handler for application errors */

156

unstable_onError?: (error: any, errorInfo: React.ErrorInfo) => void;

157

}

158

159

interface RouterInit {

160

/** Function to get context for client-side route functions */

161

unstable_getContext?: () => any;

162

}

163

```

164

165

**Usage Example:**

166

167

```tsx

168

import { HydratedRouter } from "react-router-dom";

169

170

// Client-side entry point for framework apps

171

function App() {

172

return (

173

<HydratedRouter

174

unstable_onError={(error, errorInfo) => {

175

console.error("App error:", error, errorInfo);

176

reportError(error);

177

}}

178

/>

179

);

180

}

181

```

182

183

### ServerRouter

184

185

Server-side router component for React Server Components.

186

187

```typescript { .api }

188

/**

189

* Server router component for React Server Components

190

* @param props - ServerRouter configuration

191

* @returns Server-side router for RSC applications

192

*/

193

function ServerRouter(props: ServerRouterProps): JSX.Element;

194

195

interface ServerRouterProps {

196

/** Current request context */

197

context: EntryContext;

198

/** Request URL */

199

url: string;

200

/** Abort signal for request cancellation */

201

abortDelay?: number;

202

/** Nonce for content security policy */

203

nonce?: string;

204

}

205

206

interface EntryContext {

207

/** Server manifest */

208

manifest: AssetsManifest;

209

/** Route modules */

210

routeModules: RouteModules;

211

/** Server build */

212

serverBuild: ServerBuild;

213

/** Static handler context */

214

staticHandlerContext: StaticHandlerContext;

215

}

216

```

217

218

### Document Components

219

220

Components for managing document head and scripts in SSR applications.

221

222

```typescript { .api }

223

/**

224

* Component for rendering document metadata

225

* @param props - Meta component options

226

* @returns Meta tags for document head

227

*/

228

function Meta(props?: { nonce?: string }): JSX.Element;

229

230

/**

231

* Component for rendering document links

232

* @param props - Links component options

233

* @returns Link tags for document head

234

*/

235

function Links(props?: LinksProps): JSX.Element;

236

237

/**

238

* Component for rendering application scripts

239

* @param props - Scripts component options

240

* @returns Script tags for document body

241

*/

242

function Scripts(props?: ScriptsProps): JSX.Element;

243

244

/**

245

* Component for prefetching page links

246

* @param props - PrefetchPageLinks options

247

* @returns Prefetch link tags

248

*/

249

function PrefetchPageLinks(props?: { page?: string }): JSX.Element;

250

251

interface LinksProps {

252

/** Nonce for CSP */

253

nonce?: string;

254

}

255

256

interface ScriptsProps {

257

/** Nonce for CSP */

258

nonce?: string;

259

/** Async script loading */

260

async?: boolean;

261

/** Cross-origin setting */

262

crossOrigin?: string;

263

}

264

```

265

266

**Usage Example:**

267

268

```tsx

269

import { Links, Meta, Scripts } from "react-router-dom";

270

271

function Document({ children, title }) {

272

return (

273

<html lang="en">

274

<head>

275

<meta charSet="utf-8" />

276

<meta name="viewport" content="width=device-width,initial-scale=1" />

277

<title>{title}</title>

278

<Meta />

279

<Links />

280

</head>

281

<body>

282

{children}

283

<Scripts />

284

</body>

285

</html>

286

);

287

}

288

```

289

290

### Route Module Functions

291

292

Server-side functions for generating metadata and handling requests.

293

294

```typescript { .api }

295

/**

296

* Function for generating document metadata

297

*/

298

type MetaFunction<

299

Loader = unknown,

300

ParentsLoaders extends Record<string, unknown> = {}

301

> = (args: MetaArgs<Loader, ParentsLoaders>) => MetaDescriptor[];

302

303

interface MetaArgs<

304

Loader = unknown,

305

ParentsLoaders extends Record<string, unknown> = {}

306

> {

307

/** Route data from loader */

308

data: Loader;

309

/** Route parameters */

310

params: Params;

311

/** Current location */

312

location: Location;

313

/** Parent route data */

314

matches: UIMatch<unknown, unknown>[];

315

/** Error from route (if any) */

316

error?: unknown;

317

}

318

319

type MetaDescriptor =

320

| { title: string }

321

| { name: string; content: string }

322

| { property: string; content: string }

323

| { httpEquiv: string; content: string }

324

| { charset: string }

325

| { [key: string]: string };

326

327

/**

328

* Function for generating document links

329

*/

330

type LinksFunction = () => LinkDescriptor[];

331

332

type LinkDescriptor =

333

| PageLinkDescriptor

334

| HtmlLinkDescriptor;

335

336

interface PageLinkDescriptor {

337

page: string;

338

[key: string]: unknown;

339

}

340

341

interface HtmlLinkDescriptor {

342

rel: string;

343

href?: string;

344

[key: string]: unknown;

345

}

346

347

/**

348

* Function for generating response headers

349

*/

350

type HeadersFunction = (args: HeadersArgs) => Headers | HeadersInit;

351

352

interface HeadersArgs {

353

loaderHeaders: Headers;

354

parentHeaders: Headers;

355

actionHeaders: Headers;

356

errorHeaders: Headers | undefined;

357

}

358

```

359

360

**Usage Examples:**

361

362

```tsx

363

// Route with metadata

364

export const meta: MetaFunction<typeof loader> = ({ data, location }) => {

365

return [

366

{ title: data.post.title },

367

{

368

name: "description",

369

content: data.post.excerpt

370

},

371

{

372

property: "og:title",

373

content: data.post.title,

374

},

375

{

376

property: "og:url",

377

content: `https://example.com${location.pathname}`,

378

},

379

];

380

};

381

382

// Route with links

383

export const links: LinksFunction = () => {

384

return [

385

{ rel: "stylesheet", href: "/styles/post.css" },

386

{ rel: "prefetch", href: "/api/related-posts" },

387

];

388

};

389

390

// Route with headers

391

export const headers: HeadersFunction = ({ loaderHeaders }) => {

392

return {

393

"Cache-Control": "public, max-age=3600",

394

"Vary": "Accept-Encoding",

395

...Object.fromEntries(loaderHeaders.entries()),

396

};

397

};

398

```

399

400

### Client-Side Functions

401

402

Functions that run on the client for enhanced interactivity.

403

404

```typescript { .api }

405

/**

406

* Client-side loader function

407

*/

408

type ClientLoaderFunction<T = any> = (

409

args: ClientLoaderFunctionArgs

410

) => Promise<T> | T;

411

412

interface ClientLoaderFunctionArgs {

413

request: Request;

414

params: Params;

415

serverLoader: () => Promise<any>;

416

}

417

418

/**

419

* Client-side action function

420

*/

421

type ClientActionFunction<T = any> = (

422

args: ClientActionFunctionArgs

423

) => Promise<T> | T;

424

425

interface ClientActionFunctionArgs {

426

request: Request;

427

params: Params;

428

serverAction: () => Promise<any>;

429

}

430

```

431

432

**Usage Examples:**

433

434

```tsx

435

// Client loader that enhances server data

436

export const clientLoader: ClientLoaderFunction = async ({

437

serverLoader,

438

params

439

}) => {

440

const serverData = await serverLoader();

441

442

// Add client-specific data

443

const clientData = {

444

...serverData,

445

clientTimestamp: Date.now(),

446

isOnline: navigator.onLine,

447

};

448

449

return clientData;

450

};

451

452

// Client action with optimistic updates

453

export const clientAction: ClientActionFunction = async ({

454

request,

455

serverAction

456

}) => {

457

const formData = await request.formData();

458

459

// Optimistic update

460

updateUIOptimistically(formData);

461

462

try {

463

return await serverAction();

464

} catch (error) {

465

// Revert optimistic update

466

revertOptimisticUpdate();

467

throw error;

468

}

469

};

470

```

471

472

### Testing Utilities

473

474

Utilities for testing server-side rendered components.

475

476

```typescript { .api }

477

/**

478

* Create route stub for testing

479

* @param routes - Route definitions for testing

480

* @param opts - Test configuration options

481

* @returns Component for testing routes

482

*/

483

function createRoutesStub(

484

routes: RouteObject[],

485

opts?: {

486

basename?: string;

487

initialEntries?: string[];

488

initialIndex?: number;

489

}

490

): React.ComponentType<RoutesTestStubProps>;

491

492

interface RoutesTestStubProps {

493

children?: React.ReactNode;

494

}

495

```

496

497

**Usage Example:**

498

499

```tsx

500

import { createRoutesStub } from "react-router-dom";

501

import { render, screen } from "@testing-library/react";

502

503

const RouteStub = createRoutesStub([

504

{

505

path: "/users/:id",

506

element: <UserProfile />,

507

loader: ({ params }) => ({ id: params.id, name: "John Doe" }),

508

},

509

]);

510

511

test("renders user profile", async () => {

512

render(<RouteStub initialEntries={["/users/123"]} />);

513

514

expect(await screen.findByText("John Doe")).toBeInTheDocument();

515

});

516

```

517

518

## SSR Patterns

519

520

### Basic Server Setup

521

522

```tsx

523

// server.ts

524

import { createStaticHandler, createStaticRouter } from "react-router-dom";

525

526

const handler = createStaticHandler(routes);

527

528

export async function handleRequest(request: Request) {

529

const context = await handler.query(request);

530

531

if (context instanceof Response) {

532

return context;

533

}

534

535

const router = createStaticRouter(routes, context.location);

536

537

const html = renderToString(

538

<StaticRouterProvider router={router} context={context} />

539

);

540

541

return new Response(

542

`<!DOCTYPE html><html><body>${html}</body></html>`,

543

{

544

status: context.statusCode,

545

headers: { "Content-Type": "text/html" },

546

}

547

);

548

}

549

```

550

551

### Data Loading on Server

552

553

```tsx

554

// Route with server data loading

555

export const loader = async ({ request, params }) => {

556

const user = await fetchUser(params.id);

557

const posts = await fetchUserPosts(params.id);

558

559

return json({ user, posts }, {

560

headers: {

561

"Cache-Control": "public, max-age=300",

562

},

563

});

564

};

565

566

// Client hydration

567

const router = createBrowserRouter(routes);

568

569

hydrateRoot(

570

document.getElementById("root"),

571

<RouterProvider router={router} />

572

);

573

```

574

575

### Error Handling in SSR

576

577

```tsx

578

// Error boundary for SSR

579

function RootErrorBoundary() {

580

const error = useRouteError();

581

582

if (isRouteErrorResponse(error)) {

583

return (

584

<div>

585

<h1>{error.status} {error.statusText}</h1>

586

<p>{error.data}</p>

587

</div>

588

);

589

}

590

591

return (

592

<div>

593

<h1>Something went wrong!</h1>

594

<pre>{error.message}</pre>

595

</div>

596

);

597

}

598

```