Client + Server Loader
Client Loader Calls Server Loader
When you have both a server loader and a client loader, the client loader can call serverLoader() to fetch server data and then enrich it with client-side information. This pattern combines the security of server-side data loading with client-specific enhancements.
Why Use This Pattern?
- Security: Keep sensitive data fetching on the server
- Client Context: Add browser-specific data (localStorage, navigator, etc.)
- Caching: Layer client-side caching on top of server data
- Transformation: Transform server data for client rendering
Server Data
- Message
- Secure data from server
- Timestamp
- 2026-01-11T20:48:38.289Z
- Secret
- API Key Hash: 527kl5ym
Client Enrichment
- Client Time
- Browser
- Combined
Client Loader Calling Server (client-calls-server.tsx)
import type { RouteLoaderArgs } from "@udibo/juniper";
// ServerData type is defined in the same .tsx file
export interface ServerData {
serverMessage: string;
serverTimestamp: string;
secretData: string;
}
interface ClientEnrichedData extends ServerData {
clientTimestamp: string;
browserInfo: string;
combinedMessage: string;
}
export async function loader({
serverLoader,
}: RouteLoaderArgs): Promise<ClientEnrichedData> {
const serverData = await serverLoader() as ServerData;
return {
...serverData,
clientTimestamp: new Date().toISOString(),
browserInfo: navigator.userAgent.split(" ").slice(-1)[0],
combinedMessage: `${serverData.serverMessage} + client enrichment`,
};
}Note: During SSR, the server loader runs directly. On client-side navigation, the client loader calls serverLoader() which fetches data from a server endpoint, then enriches it with client context.