Data Loading
Phyre uses React Router's loader pattern for server-side data fetching.
Basic Loader
Export an async loader function from any route to fetch data before rendering:
jsx
// src/client/routes/posts/index.jsx import { useLoaderData, Link } from 'react-router'; // Loader runs on the server (SSR) or client (navigation) export async function loader() { const response = await fetch('http://localhost:3000/api/posts'); const data = await response.json(); return data; } export default function Posts() { const posts = useLoaderData(); return ( <div> <h1>Posts</h1> {posts.data.map(post => ( <Link key={post.id} to={`/posts/${post.id}`}> {post.title} </Link> ))} </div> ); }
Loader with Params
Access route parameters in the loader:
jsx
// src/client/routes/posts/[id].jsx import { useLoaderData } from 'react-router'; export async function loader({ params }) { const response = await fetch(`http://localhost:3000/api/posts/${params.id}`); const data = await response.json(); return data; } export default function PostDetail() { const post = useLoaderData(); return ( <div> <h1>{post.title}</h1> <p>{post.content}</p> </div> ); }
Loader with Request
Access the full request object:
jsx
export async function loader({ request, params }) { const url = new URL(request.url); const searchQuery = url.searchParams.get('q'); const response = await fetch( `http://localhost:3000/api/search?q=${searchQuery}` ); return response.json(); }
Multiple Data Sources
Fetch from multiple APIs in parallel:
jsx
export async function loader() { const [posts, categories, user] = await Promise.all([ fetch('http://localhost:3000/api/posts').then(r => r.json()), fetch('http://localhost:3000/api/categories').then(r => r.json()), fetch('http://localhost:3000/api/user').then(r => r.json()) ]); return { posts, categories, user }; } export default function Dashboard() { const { posts, categories, user } = useLoaderData(); // ... }
Error Handling
Handle errors in loaders:
jsx
export async function loader({ params }) { const response = await fetch(`http://localhost:3000/api/posts/${params.id}`); if (!response.ok) { throw new Response('Not Found', { status: 404 }); } return response.json(); } // Optional: Define error boundary export function ErrorBoundary() { return ( <div> <h1>Error loading post</h1> <p>The post could not be found.</p> </div> ); }
Best Practices
- Always return data - Don't return undefined
- Handle errors - Use try/catch or check response.ok
- Use absolute URLs - Full URL in loaders (for SSR)
- Keep loaders fast - Heavy logic should be in API routes
- Parallel fetching - Use Promise.all for multiple requests
Note: Loaders run on both server (SSR) and client (navigation). Make sure your code works in both environments.
