HydrateFallback
A HydrateFallback
component is your way of informing Remix that you do not want to render your route component until after the clientLoader
has run on hydration. When exported, Remix will render the fallback during SSR instead of your default route component, and will render your route component client-side once the clientLoader
completes.
The most common use-cases for this are client-only routes (such an in-browser canvas game) and augmenting your server data with client-side data (such as saved user preferences).
export async function clientLoader() {
const data = await loadSavedGameOrPrepareNewGame();
return data;
}
// Note clientLoader.hydrate is implied without a server loader
export function HydrateFallback() {
return <p>Loading Game...</p>;
}
export default function Component() {
const data = useLoaderData<typeof clientLoader>();
return <Game data={data} />;
}
export async function loader() {
const data = getServerData();
return json(data);
}
export async function clientLoader({
request,
params,
serverLoader,
}: ClientLoaderFunctionArgs) {
const [serverData, preferences] = await Promise.all([
serverLoader(),
getUserPreferences(),
]);
return {
...serverData,
preferences,
};
}
clientLoader.hydrate = true;
export function HydrateFallback() {
return <p>Loading user preferences...</p>;
}
export default function Component() {
const data = useLoaderData<typeof clientLoader>();
if (data.preferences.display === "list") {
return <ListView items={data.items} />;
} else {
return <GridView items={data.items} />;
}
}
There are a few nuances worth noting around the behavior of HydrateFallback
:
clientLoader.hydrate=true
on a given routeclientLoader
without a server loader
, as this implies clientLoader.hydrate=true
since there is otherwise no loader data at all to return from useLoaderData
HydrateFallback
in this case, Remix will not render your route component and will bubble up to any ancestor HydrateFallback
componentuseLoaderData
remains "happy-path"loader
, useLoaderData
would return undefined
in any rendered route components<Outlet/>
in a HydrateFallback
because children routes can't be guaranteed to operate correctly since their ancestor loader data may not yet be available if they are running clientLoader
functions on hydration (i.e., use cases such as useRouteLoaderData()
or useMatches()
)See also: