);
}
```
---
# useQuery Hook
## Real-time Updates
The `useQuery()` hook is live-updating! It causes the React component to rerender automatically when data changes. Convex is a perfect fit for collaborative, live-updating websites.
## Return Values
- `undefined` - Query is loading
- `null` - Query returned null (e.g., user not found)
- `data` - Query returned data
```tsx
function UserProfile({ userId }: { userId: Id<"users"> }) {
const user = useQuery(api.users.get, { userId });
// Loading state
if (user === undefined) {
return
Loading...
;
}
// Not found
if (user === null) {
return
User not found
;
}
// Data loaded
return
{user.name}
;
}
```
---
# Conditional Queries with "skip"
## CRITICAL: Never Use Hooks Conditionally
```tsx
// WRONG - Will cause React hook errors!
const avatarUrl = profile?.avatarId
? useQuery(api.profiles.getAvatarUrl, { storageId: profile.avatarId })
: null;
// CORRECT - Use "skip" to conditionally skip the query
const avatarUrl = useQuery(
api.profiles.getAvatarUrl,
profile?.avatarId ? { storageId: profile.avatarId } : "skip"
);
```
## More Examples
```tsx
function Dashboard() {
const user = useQuery(api.auth.loggedInUser);
// Skip queries until we have user data
const userPosts = useQuery(
api.posts.getByUser,
user ? { userId: user._id } : "skip"
);
const userSettings = useQuery(
api.settings.get,
user ? { userId: user._id } : "skip"
);
if (user === undefined) {
return ;
}
if (user === null) {
return ;
}
return (
);
}
```
---
# Importing the API Object
When writing a UI component and you want to use a Convex function, you MUST import the `api` object:
```tsx
import { api } from "../convex/_generated/api";
```
You can use the `api` object to call any public Convex function.
Always make sure:
1. The functions you are calling are defined in the `convex/` directory
2. Use the `api` object for public functions
3. You are using the correct arguments for convex functions
4. If arguments are not optional, make sure they are not null
---
# Pagination with usePaginatedQuery
```tsx
import { usePaginatedQuery } from "convex/react";
import { api } from "../convex/_generated/api";
function InfiniteMessageList({ channelId }: { channelId: Id<"channels"> }) {
const { results, status, loadMore } = usePaginatedQuery(
api.messages.list,
{ channelId },
{ initialNumItems: 20 }
);
return (
;
}
return ;
}
```
---
# Provider Setup
```tsx
// main.tsx or _app.tsx
import { ConvexProvider, ConvexReactClient } from "convex/react";
import { ConvexAuthProvider } from "@convex-dev/auth/react";
const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL);
function App() {
return (
);
}
```
---
# Best Practices
## 1. Never Call Hooks Conditionally
```tsx
// WRONG
if (isLoggedIn) {
const data = useQuery(api.data.get);
}
// CORRECT
const data = useQuery(api.data.get, isLoggedIn ? {} : "skip");
```
## 2. Handle All States
```tsx
function DataDisplay() {
const data = useQuery(api.data.get);
// Always handle: undefined (loading), null (not found), and data
if (data === undefined) return ;
if (data === null) return ;
return ;
}
```
## 3. Use TypeScript Properly
```tsx
import { Id } from "../convex/_generated/dataModel";
interface Props {
userId: Id<"users">; // Use Id<> type, not string
}
```
## 4. Avoid Prop Drilling with Queries
```tsx
// Instead of passing data through many components,
// query it where needed
function DeepNestedComponent({ itemId }: { itemId: Id<"items"> }) {
// Query directly in the component that needs it
const item = useQuery(api.items.get, { id: itemId });
// ...
}
```
## 5. Do NOT Use External UI Libraries Unless Specified
If you want to use a UI element, you MUST create it. DO NOT use external libraries like Shadcn/UI unless explicitly asked.
## 6. Do NOT Use sharp for Image Compression
Always use `canvas` for image compression, not `sharp`.