or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

hook-creation.mdindex.mdmutation-hooks.mdquery-hooks.mdquery-keys.mdquery-utilities.mdreact-server-components.mdserver-side-helpers.mdsubscription-hooks.md

query-hooks.mddocs/

0

# Query Hooks

1

2

React hooks for data fetching with caching, background updates, and full type safety. These hooks are automatically generated for each query procedure in your tRPC router.

3

4

## Capabilities

5

6

### useQuery

7

8

Primary hook for data fetching with caching and background synchronization.

9

10

```typescript { .api }

11

/**

12

* Hook for fetching data from a tRPC query procedure

13

* @param input - Input parameters for the procedure

14

* @param opts - Query configuration options

15

* @returns Query result with data, loading states, and error information

16

*/

17

procedure.useQuery<TQueryFnData = TOutput, TData = TQueryFnData>(

18

input: TInput | SkipToken,

19

opts?: UseTRPCQueryOptions<TQueryFnData, TData, TError, TOutput>

20

): UseTRPCQueryResult<TData, TError>;

21

22

// Overload for defined initial data

23

procedure.useQuery<TQueryFnData = TOutput, TData = TQueryFnData>(

24

input: TInput | SkipToken,

25

opts: DefinedUseTRPCQueryOptions<TQueryFnData, TData, TError, TOutput>

26

): DefinedUseTRPCQueryResult<TData, TError>;

27

28

interface UseTRPCQueryOptions<TOutput, TData, TError, TQueryOptsData = TOutput>

29

extends Omit<UseBaseQueryOptions<TOutput, TError, TData, TQueryOptsData>, 'queryKey'> {

30

trpc?: TRPCReactRequestOptions;

31

}

32

33

interface UseTRPCQueryResult<TData, TError> extends UseQueryResult<TData, TError> {

34

trpc: TRPCHookResult;

35

}

36

37

interface TRPCHookResult {

38

path: string[];

39

}

40

```

41

42

**Usage Examples:**

43

44

```typescript

45

import { trpc } from "./utils/trpc";

46

47

function UserProfile({ userId }: { userId: number }) {

48

// Basic query

49

const { data, error, isLoading } = trpc.user.get.useQuery({ id: userId });

50

51

// With options

52

const { data: user } = trpc.user.get.useQuery(

53

{ id: userId },

54

{

55

enabled: !!userId,

56

staleTime: 5 * 60 * 1000, // 5 minutes

57

refetchOnWindowFocus: false,

58

}

59

);

60

61

// With data transformation

62

const { data: userName } = trpc.user.get.useQuery(

63

{ id: userId },

64

{

65

select: (user) => user.name,

66

}

67

);

68

69

// Skip query conditionally

70

const { data } = trpc.user.get.useQuery(

71

userId ? { id: userId } : skipToken

72

);

73

74

if (error) return <div>Error: {error.message}</div>;

75

if (isLoading) return <div>Loading...</div>;

76

77

return <div>Hello, {data?.name}!</div>;

78

}

79

```

80

81

### useSuspenseQuery

82

83

Suspense-enabled query hook that automatically suspends the component during loading.

84

85

```typescript { .api }

86

/**

87

* Suspense-enabled hook for fetching data from a tRPC query procedure

88

* @param input - Input parameters for the procedure

89

* @param opts - Suspense query configuration options

90

* @returns Tuple with data and query result (data is always defined)

91

*/

92

procedure.useSuspenseQuery<TQueryFnData = TOutput, TData = TQueryFnData>(

93

input: TInput,

94

opts?: UseTRPCSuspenseQueryOptions<TQueryFnData, TData, TError>

95

): [TData, UseSuspenseQueryResult<TData, TError> & TRPCHookResult];

96

97

interface UseTRPCSuspenseQueryOptions<TOutput, TData, TError>

98

extends Omit<UseSuspenseQueryOptions<TOutput, TError, TData>, 'queryKey'> {

99

trpc?: TRPCReactRequestOptions;

100

}

101

```

102

103

**Usage Examples:**

104

105

```typescript

106

import { Suspense } from "react";

107

108

function UserProfileSuspense({ userId }: { userId: number }) {

109

// Suspense query - data is always defined

110

const [user, query] = trpc.user.get.useSuspenseQuery({ id: userId });

111

112

return (

113

<div>

114

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

115

<p>Email: {user.email}</p>

116

{query.isFetching && <span>Updating...</span>}

117

</div>

118

);

119

}

120

121

function App() {

122

return (

123

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

124

<UserProfileSuspense userId={1} />

125

</Suspense>

126

);

127

}

128

```

129

130

### useInfiniteQuery

131

132

Hook for paginated/infinite data fetching with cursor-based pagination support.

133

134

```typescript { .api }

135

/**

136

* Hook for infinite/paginated data fetching (only available for procedures with cursor input)

137

* @param input - Input parameters excluding cursor and direction

138

* @param opts - Infinite query configuration options

139

* @returns Infinite query result with pages data and pagination controls

140

*/

141

procedure.useInfiniteQuery<TData = InfiniteData<TOutput>>(

142

input: Omit<TInput, 'cursor' | 'direction'> | SkipToken,

143

opts?: UseTRPCInfiniteQueryOptions<TOutput, TData, TError, TCursor>

144

): UseTRPCInfiniteQueryResult<TData, TError>;

145

146

interface UseTRPCInfiniteQueryOptions<TOutput, TData, TError, TCursor>

147

extends Omit<UseInfiniteQueryOptions<TOutput, TError, TData>, 'queryKey' | 'initialPageParam'> {

148

trpc?: TRPCReactRequestOptions;

149

initialCursor?: TCursor;

150

}

151

152

interface UseTRPCInfiniteQueryResult<TData, TError>

153

extends UseInfiniteQueryResult<TData, TError> {

154

trpc: TRPCHookResult;

155

}

156

```

157

158

**Usage Examples:**

159

160

```typescript

161

function PostsList() {

162

// Infinite query for paginated posts

163

const {

164

data,

165

fetchNextPage,

166

fetchPreviousPage,

167

hasNextPage,

168

hasPreviousPage,

169

isFetchingNextPage,

170

isFetchingPreviousPage,

171

} = trpc.posts.list.useInfiniteQuery(

172

{ limit: 10 },

173

{

174

getNextPageParam: (lastPage) => lastPage.nextCursor,

175

getPreviousPageParam: (firstPage) => firstPage.prevCursor,

176

initialCursor: null,

177

}

178

);

179

180

return (

181

<div>

182

<button

183

onClick={() => fetchPreviousPage()}

184

disabled={!hasPreviousPage || isFetchingPreviousPage}

185

>

186

Load Previous

187

</button>

188

189

{data?.pages.map((page, i) => (

190

<div key={i}>

191

{page.posts.map((post) => (

192

<article key={post.id}>

193

<h3>{post.title}</h3>

194

<p>{post.content}</p>

195

</article>

196

))}

197

</div>

198

))}

199

200

<button

201

onClick={() => fetchNextPage()}

202

disabled={!hasNextPage || isFetchingNextPage}

203

>

204

{isFetchingNextPage ? "Loading..." : "Load More"}

205

</button>

206

</div>

207

);

208

}

209

```

210

211

### useSuspenseInfiniteQuery

212

213

Suspense-enabled infinite query hook for paginated data with automatic suspense handling.

214

215

```typescript { .api }

216

/**

217

* Suspense-enabled infinite query hook (only available for procedures with cursor input)

218

* @param input - Input parameters excluding cursor and direction

219

* @param opts - Suspense infinite query configuration options

220

* @returns Tuple with infinite data and query result

221

*/

222

procedure.useSuspenseInfiniteQuery(

223

input: Omit<TInput, 'cursor' | 'direction'>,

224

opts: UseTRPCSuspenseInfiniteQueryOptions<TOutput, TCursor>

225

): [InfiniteData<TOutput>, UseSuspenseInfiniteQueryResult<InfiniteData<TOutput>, TError> & TRPCHookResult];

226

227

interface UseTRPCSuspenseInfiniteQueryOptions<TOutput, TCursor>

228

extends Omit<UseSuspenseInfiniteQueryOptions<TOutput, TError, InfiniteData<TOutput>>, 'queryKey' | 'initialPageParam'> {

229

trpc?: TRPCReactRequestOptions;

230

initialCursor?: TCursor;

231

}

232

```

233

234

**Usage Examples:**

235

236

```typescript

237

import { Suspense } from "react";

238

239

function PostsListSuspense() {

240

const [data, query] = trpc.posts.list.useSuspenseInfiniteQuery(

241

{ limit: 10 },

242

{

243

getNextPageParam: (lastPage) => lastPage.nextCursor,

244

}

245

);

246

247

return (

248

<div>

249

{data.pages.map((page, i) => (

250

<div key={i}>

251

{page.posts.map((post) => (

252

<article key={post.id}>

253

<h3>{post.title}</h3>

254

<p>{post.content}</p>

255

</article>

256

))}

257

</div>

258

))}

259

260

<button

261

onClick={() => query.fetchNextPage()}

262

disabled={!query.hasNextPage}

263

>

264

Load More

265

</button>

266

</div>

267

);

268

}

269

270

function App() {

271

return (

272

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

273

<PostsListSuspense />

274

</Suspense>

275

);

276

}

277

```

278

279

### usePrefetchQuery

280

281

Hook for prefetching query data without rendering the result.

282

283

```typescript { .api }

284

/**

285

* Hook for prefetching query data

286

* @param input - Input parameters for the procedure

287

* @param opts - Prefetch configuration options

288

*/

289

procedure.usePrefetchQuery(

290

input: TInput | SkipToken,

291

opts?: TRPCFetchQueryOptions<TOutput, TError>

292

): void;

293

294

interface TRPCFetchQueryOptions<TData, TError>

295

extends Omit<FetchQueryOptions<TData, TError>, 'queryKey'> {

296

trpc?: TRPCReactRequestOptions;

297

}

298

```

299

300

**Usage Examples:**

301

302

```typescript

303

function UserList() {

304

const { data: users } = trpc.users.list.useQuery();

305

306

// Prefetch user details on hover

307

const handleUserHover = (userId: number) => {

308

trpc.user.get.usePrefetchQuery({ id: userId });

309

};

310

311

return (

312

<ul>

313

{users?.map((user) => (

314

<li

315

key={user.id}

316

onMouseEnter={() => handleUserHover(user.id)}

317

>

318

<Link to={`/users/${user.id}`}>{user.name}</Link>

319

</li>

320

))}

321

</ul>

322

);

323

}

324

```

325

326

### usePrefetchInfiniteQuery

327

328

Hook for prefetching infinite query data.

329

330

```typescript { .api }

331

/**

332

* Hook for prefetching infinite query data (only available for procedures with cursor input)

333

* @param input - Input parameters excluding cursor and direction

334

* @param opts - Prefetch infinite query configuration options

335

*/

336

procedure.usePrefetchInfiniteQuery(

337

input: Omit<TInput, 'cursor' | 'direction'> | SkipToken,

338

opts: TRPCFetchInfiniteQueryOptions<TInput, TOutput, TError>

339

): void;

340

341

interface TRPCFetchInfiniteQueryOptions<TInput, TOutput, TError>

342

extends Omit<FetchInfiniteQueryOptions<TOutput, TError>, 'queryKey' | 'initialPageParam'> {

343

trpc?: TRPCReactRequestOptions;

344

initialCursor?: ExtractCursorType<TInput>;

345

}

346

```

347

348

**Usage Examples:**

349

350

```typescript

351

function PostsNavigation() {

352

// Prefetch first page of posts

353

trpc.posts.list.usePrefetchInfiniteQuery(

354

{ limit: 10 },

355

{

356

initialCursor: null,

357

}

358

);

359

360

return (

361

<nav>

362

<Link to="/posts">View Posts</Link>

363

</nav>

364

);

365

}

366

```

367

368

## Common Patterns

369

370

### Conditional Queries

371

372

```typescript

373

function ConditionalQuery({ userId }: { userId?: number }) {

374

// Skip query when userId is undefined

375

const { data } = trpc.user.get.useQuery(

376

userId ? { id: userId } : skipToken

377

);

378

379

// Alternative with enabled option

380

const { data: user } = trpc.user.get.useQuery(

381

{ id: userId! },

382

{ enabled: !!userId }

383

);

384

385

return <div>{data?.name}</div>;

386

}

387

```

388

389

### Error Handling

390

391

```typescript

392

function UserWithErrorHandling({ userId }: { userId: number }) {

393

const { data, error, isError } = trpc.user.get.useQuery({ id: userId });

394

395

if (isError) {

396

if (error.data?.code === "NOT_FOUND") {

397

return <div>User not found</div>;

398

}

399

return <div>Error: {error.message}</div>;

400

}

401

402

return <div>{data?.name}</div>;

403

}

404

```

405

406

### Data Transformation

407

408

```typescript

409

function TransformedQuery({ userId }: { userId: number }) {

410

// Transform data in the select function

411

const { data: userInfo } = trpc.user.get.useQuery(

412

{ id: userId },

413

{

414

select: (user) => ({

415

displayName: `${user.firstName} ${user.lastName}`,

416

initials: `${user.firstName[0]}${user.lastName[0]}`,

417

}),

418

}

419

);

420

421

return <div>{userInfo?.displayName}</div>;

422

}

423

```