Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

clientLoader: loading in the browser

A regular loader runs only on the server. But some data exists only in the browser — for example, a draft in localStorage. For that a route module exports clientLoader: the same kind of loading function, but running in the browser.

Drafts

Let the /drafts page show a post draft saved in the browser. Add the route route("drafts", "routes/drafts.tsx") and create the file:

// app/routes/drafts.tsx
import type { Route } from "./+types/drafts";
 
export async function clientLoader() {
  const draft = localStorage.getItem("draft") ?? "";
  return { draft };
}
 
export function HydrateFallback() {
  return <p>Loading the draft...</p>;
}
 
export default function Drafts({ loaderData }: Route.ComponentProps) {
  return loaderData.draft ? <p>{loaderData.draft}</p> : <p>No drafts</p>;
}

On the first visit the server cannot run clientLoader — it has no localStorage. So the server renders HydrateFallback, and the browser, once JavaScript loads, calls clientLoader and shows the draft. On link navigation clientLoader runs in the browser right away.

Together with a server loader

If a route has both loader and clientLoader, browser navigations call clientLoader. It can get the server data with serverLoader():

// app/routes/post.tsx
export async function clientLoader({ serverLoader }: Route.ClientLoaderArgs) {
  const serverData = await serverLoader();
  const readLater = localStorage.getItem("read-later") === String(serverData.post.id);
  return { ...serverData, readLater };
}

The component gets the post from the server and the readLater flag from the browser. This is also how you add a browser cache to avoid asking the server again.

Running on the first load

By default, when a loader exists, the first visit uses only its data, and clientLoader starts working from later navigations. To make it run on the first load too, add this line and a HydrateFallback:

// app/routes/post.tsx
clientLoader.hydrate = true as const;

Which one to choose

You needFunction
Database data, secret keysloader
localStorage, browser detailsclientLoader
Server plus a browser cacheBoth, with serverLoader() inside

Common mistake

Moving database queries or API calls with a secret key into clientLoader. The code of clientLoader ships to the browser in full, and any visitor can see the key. Keep everything that needs secrets or the database in loader.

Thanks for reading!

Donate to the site — we work hard for you.