or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

application-entry-points.mddata-loading-hooks.mddata-response-utilities.mddocument-components.mdforms-and-navigation.mdindex.mdreact-router-integration.mdtype-definitions.md

data-response-utilities.mddocs/

0

# Data Response Utilities

1

2

Server runtime utilities for creating responses, redirects, and handling deferred data in Remix loader and action functions.

3

4

## Capabilities

5

6

### JSON Response

7

8

Create JSON responses with proper headers and serialization.

9

10

```typescript { .api }

11

/**

12

* Creates a Response with JSON data and appropriate headers

13

* Automatically serializes data and sets Content-Type header

14

* @param data - Data to serialize as JSON

15

* @param init - Optional response initialization options

16

* @returns Response object with JSON data

17

*/

18

function json<Data>(data: Data, init?: ResponseInit): Response;

19

```

20

21

**Usage Examples:**

22

23

```typescript

24

import { json } from "@remix-run/react";

25

import type { LoaderFunctionArgs } from "@remix-run/node";

26

27

// Basic JSON response

28

export async function loader({ params }: LoaderFunctionArgs) {

29

const user = await getUser(params.userId);

30

31

if (!user) {

32

throw json({ error: "User not found" }, { status: 404 });

33

}

34

35

return json({

36

user,

37

lastUpdated: new Date(),

38

preferences: user.preferences,

39

});

40

}

41

42

// JSON response with custom headers

43

export async function loader() {

44

const data = await getPublicData();

45

46

return json(data, {

47

headers: {

48

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

49

"X-Custom-Header": "value",

50

},

51

});

52

}

53

54

// JSON error response

55

export async function action({ request }: ActionFunctionArgs) {

56

const formData = await request.formData();

57

58

try {

59

const result = await processForm(formData);

60

return json({ success: true, result });

61

} catch (error) {

62

return json(

63

{

64

success: false,

65

error: error.message,

66

field: "email"

67

},

68

{ status: 400 }

69

);

70

}

71

}

72

```

73

74

### Redirect Response

75

76

Create redirect responses for navigation and flow control.

77

78

```typescript { .api }

79

/**

80

* Creates a redirect Response to the specified URL

81

* @param url - URL to redirect to

82

* @param init - Optional response initialization options

83

* @returns Response object with redirect status and Location header

84

*/

85

function redirect(url: string, init?: ResponseInit): Response;

86

87

/**

88

* Creates a redirect Response that reloads the entire document

89

* Forces a full page reload instead of client-side navigation

90

* @param url - URL to redirect to

91

* @param init - Optional response initialization options

92

* @returns Response object with redirect and document reload

93

*/

94

function redirectDocument(url: string, init?: ResponseInit): Response;

95

96

/**

97

* Creates a redirect Response that replaces the current entry in the history stack

98

* Uses replace navigation instead of pushing a new entry

99

* @param url - URL to redirect to

100

* @param init - Optional response initialization options (defaults to 302)

101

* @returns Response object with redirect status and Location header

102

*/

103

function replace(url: string, init?: ResponseInit): Response;

104

```

105

106

**Usage Examples:**

107

108

```typescript

109

import { redirect, redirectDocument, replace } from "@remix-run/react";

110

import type { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/node";

111

112

// Basic redirect

113

export async function loader({ request }: LoaderFunctionArgs) {

114

const user = await getCurrentUser(request);

115

116

if (!user) {

117

return redirect("/login");

118

}

119

120

return json({ user });

121

}

122

123

// Redirect with custom status

124

export async function action({ request }: ActionFunctionArgs) {

125

const formData = await request.formData();

126

const result = await createPost(formData);

127

128

// Redirect to the new post with 201 status

129

return redirect(`/posts/${result.id}`, { status: 201 });

130

}

131

132

// Document redirect (full page reload)

133

export async function action({ request }: ActionFunctionArgs) {

134

await processPayment(request);

135

136

// Force full page reload for payment confirmation

137

return redirectDocument("/payment/success");

138

}

139

140

// Replace redirect (replaces current history entry)

141

export async function action({ request }: ActionFunctionArgs) {

142

const formData = await request.formData();

143

const result = await updateProfile(formData);

144

145

// Replace current URL instead of adding to history

146

return replace("/profile", { status: 302 });

147

}

148

149

// Conditional redirect

150

export async function loader({ request, params }: LoaderFunctionArgs) {

151

const user = await getCurrentUser(request);

152

const post = await getPost(params.postId);

153

154

if (!post) {

155

throw json({ error: "Post not found" }, { status: 404 });

156

}

157

158

if (post.private && post.authorId !== user?.id) {

159

return redirect("/unauthorized");

160

}

161

162

return json({ post, user });

163

}

164

```

165

166

### Deferred Response

167

168

Create responses with deferred data for streaming and progressive loading.

169

170

```typescript { .api }

171

/**

172

* Creates a Response with deferred data for streaming

173

* Allows some data to load immediately while other data streams in later

174

* @param data - Object containing immediate and deferred data

175

* @param init - Optional response initialization options

176

* @returns Response object with streaming deferred data

177

*/

178

function defer<Data extends Record<string, unknown>>(

179

data: Data,

180

init?: ResponseInit

181

): Response;

182

```

183

184

**Usage Examples:**

185

186

```typescript

187

import { defer, json } from "@remix-run/react";

188

import { Await, useLoaderData } from "@remix-run/react";

189

import { Suspense } from "react";

190

import type { LoaderFunctionArgs } from "@remix-run/node";

191

192

// Deferred data loading

193

export async function loader({ params }: LoaderFunctionArgs) {

194

const user = await getUser(params.userId); // Fast query

195

196

// Defer slow queries

197

const posts = getUserPosts(params.userId); // Don't await

198

const analytics = getUserAnalytics(params.userId); // Don't await

199

200

return defer({

201

user, // Immediate data

202

posts, // Deferred promise

203

analytics, // Deferred promise

204

});

205

}

206

207

// Component using deferred data

208

export default function UserProfile() {

209

const { user, posts, analytics } = useLoaderData<typeof loader>();

210

211

return (

212

<div>

213

<h1>{user.name}</h1>

214

215

<Suspense fallback={<div>Loading posts...</div>}>

216

<Await resolve={posts}>

217

{(resolvedPosts) => (

218

<PostsList posts={resolvedPosts} />

219

)}

220

</Await>

221

</Suspense>

222

223

<Suspense fallback={<div>Loading analytics...</div>}>

224

<Await resolve={analytics}>

225

{(resolvedAnalytics) => (

226

<AnalyticsDashboard data={resolvedAnalytics} />

227

)}

228

</Await>

229

</Suspense>

230

</div>

231

);

232

}

233

234

// Mixed immediate and deferred data

235

export async function loader() {

236

const criticalData = await getCriticalData(); // Must load first

237

const slowData = getSlowData(); // Defer this

238

239

return defer({

240

critical: criticalData,

241

slow: slowData,

242

metadata: { loadTime: Date.now() },

243

});

244

}

245

```

246

247

### Replace Response

248

249

Create responses that replace the current history entry.

250

251

```typescript { .api }

252

/**

253

* Creates a response that replaces the current history entry

254

* Useful for preventing back button navigation to intermediate states

255

* @param url - URL to replace with

256

* @param init - Optional response initialization options

257

* @returns Response object with replace behavior

258

*/

259

function replace(url: string, init?: ResponseInit): Response;

260

```

261

262

**Usage Examples:**

263

264

```typescript

265

import { replace, json } from "@remix-run/react";

266

import type { ActionFunctionArgs } from "@remix-run/node";

267

268

// Replace after form submission

269

export async function action({ request }: ActionFunctionArgs) {

270

const formData = await request.formData();

271

272

if (formData.get("step") === "confirm") {

273

await processOrder(formData);

274

// Replace the confirmation step in history

275

return replace("/order/success");

276

}

277

278

return json({ step: "confirm" });

279

}

280

```

281

282

### Generic Data Response

283

284

Create flexible data responses for various content types.

285

286

```typescript { .api }

287

/**

288

* Creates a generic data response with flexible content handling

289

* Supports various data types and custom serialization

290

* @param data - Data to include in the response

291

* @param init - Optional response initialization options

292

* @returns Response object with the provided data

293

*/

294

function data<Data>(data: Data, init?: ResponseInit): Response;

295

```

296

297

**Usage Examples:**

298

299

```typescript

300

import { data } from "@remix-run/react";

301

302

// Custom response with specific content type

303

export async function loader() {

304

const csvData = await generateCSVReport();

305

306

return data(csvData, {

307

headers: {

308

"Content-Type": "text/csv",

309

"Content-Disposition": "attachment; filename=report.csv",

310

},

311

});

312

}

313

314

// Binary data response

315

export async function loader({ params }: LoaderFunctionArgs) {

316

const imageBuffer = await getImage(params.imageId);

317

318

return data(imageBuffer, {

319

headers: {

320

"Content-Type": "image/jpeg",

321

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

322

},

323

});

324

}

325

```

326

327

## Type Definitions

328

329

### Response Initialization

330

331

```typescript { .api }

332

interface ResponseInit {

333

/** HTTP status code */

334

status?: number;

335

/** HTTP status text */

336

statusText?: string;

337

/** Response headers */

338

headers?: HeadersInit;

339

}

340

341

type HeadersInit = Headers | string[][] | Record<string, string>;

342

```

343

344

### Deferred Data Type

345

346

```typescript { .api }

347

/**

348

* Type for deferred data objects

349

* Keys can contain immediate values or promises that resolve later

350

*/

351

type DeferredData<T extends Record<string, unknown>> = {

352

[K in keyof T]: T[K] | Promise<T[K]>;

353

};

354

```

355

356

## Implementation Notes

357

358

- **Streaming**: Deferred responses enable streaming data to the client as it becomes available

359

- **Performance**: Immediate data renders instantly while slow queries complete in the background

360

- **Error Handling**: Deferred promises can reject and be caught by error boundaries

361

- **SEO**: Search engines receive immediate data for indexing while deferred data loads progressively

362

- **Type Safety**: All response utilities maintain full TypeScript type safety

363

- **Headers**: Custom headers can be set for caching, content type, and security policies

364

- **Status Codes**: HTTP status codes can be customized for proper REST API compliance