jepcd
jepcd8mo ago

Using trpcState with Remix

Following the guide, seems like the serialized stuff is sent down, but not used by the client.
6 Replies
jepcd
jepcdOP8mo ago
Was able to get it to work by following teh react query docs for remix prefetching ^ if anyone else finds it useful
sh03
sh032mo ago
@jepcd Were you able to find a solution?
jepcd
jepcdOP2mo ago
SSR | TanStack Query React Docs
React Query supports two ways of prefetching data on the server and passing that to the queryClient. Prefetch the data yourself and pass it in as initialData Quick to set up for simple cases Has some...
sh03
sh032mo ago
Right, but the QueryClient is not typed. Were you able to use tRPC's syntax by any chance for the prefetching? Also they seem to use Hydrate which does not exist anymore in v11
jepcd
jepcdOP2mo ago
just had a look through the project and this is how I got it working app/root.tsx
export function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className="h-screen w-screen">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<Meta />
<Links />
</head>
<body className="size-full">
{children}
<ScrollRestoration />
<Scripts />
</body>
</html>
);
}

export default function App() {
const [queryClient] = useState(
() =>
new QueryClient({
defaultOptions: {
queries: {
// With SSR/Prefetching, we usually want to set some default staleTime
// above 0 to avoid refetching immediately on the client
staleTime: 60 * 1000,
},
},
}),
);

const [trpcClient] = useState(() =>
trpc.createClient({
links: [httpBatchLink({ url: "/api/trpc" })],
}),
);

const dehydratedState = useDehydratedState();

return (
<HydrationBoundary state={dehydratedState} queryClient={queryClient}>
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>
<Theme appearance="dark" accentColor="lime" radius="full" className="size-full">
<Outlet />
<Modals />
<ThemePanel defaultOpen={false} />
<Toaster richColors theme="dark" />
<ReactQueryDevtools initialIsOpen={false} />
</Theme>
</QueryClientProvider>
</trpc.Provider>
</HydrationBoundary>
);
}
export function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className="h-screen w-screen">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<Meta />
<Links />
</head>
<body className="size-full">
{children}
<ScrollRestoration />
<Scripts />
</body>
</html>
);
}

export default function App() {
const [queryClient] = useState(
() =>
new QueryClient({
defaultOptions: {
queries: {
// With SSR/Prefetching, we usually want to set some default staleTime
// above 0 to avoid refetching immediately on the client
staleTime: 60 * 1000,
},
},
}),
);

const [trpcClient] = useState(() =>
trpc.createClient({
links: [httpBatchLink({ url: "/api/trpc" })],
}),
);

const dehydratedState = useDehydratedState();

return (
<HydrationBoundary state={dehydratedState} queryClient={queryClient}>
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>
<Theme appearance="dark" accentColor="lime" radius="full" className="size-full">
<Outlet />
<Modals />
<ThemePanel defaultOpen={false} />
<Toaster richColors theme="dark" />
<ReactQueryDevtools initialIsOpen={false} />
</Theme>
</QueryClientProvider>
</trpc.Provider>
</HydrationBoundary>
);
}
app/routes/dashboard/index.tsx
export async function loader({ context }: LoaderFunctionArgs) {
await requireUser(context);

const trpcHelpers = getTrpcServerSideHelpers(context);

await trpcHelpers.user.currentUser.prefetch();
await trpcHelpers.entity.restaurant.getAllWithChildren.prefetch();

return json({ dehydratedState: trpcHelpers.dehydrate() });
}

export default function Dashboard() {
return (
<PanelGroup direction="horizontal">
<Panel defaultSize={20} minSize={15} id="sidebar-panel" order={1}>
<SidebarPanel />
</Panel>
<ResizeHandle />
<Panel defaultSize={80} minSize={50} id="index-panel" order={2}>
<div className="flex h-full flex-col">
<DashboardNav />
<div className="grow bg-gray-1 p-4">
<Outlet />
</div>
</div>
</Panel>
</PanelGroup>
);
}
export async function loader({ context }: LoaderFunctionArgs) {
await requireUser(context);

const trpcHelpers = getTrpcServerSideHelpers(context);

await trpcHelpers.user.currentUser.prefetch();
await trpcHelpers.entity.restaurant.getAllWithChildren.prefetch();

return json({ dehydratedState: trpcHelpers.dehydrate() });
}

export default function Dashboard() {
return (
<PanelGroup direction="horizontal">
<Panel defaultSize={20} minSize={15} id="sidebar-panel" order={1}>
<SidebarPanel />
</Panel>
<ResizeHandle />
<Panel defaultSize={80} minSize={50} id="index-panel" order={2}>
<div className="flex h-full flex-col">
<DashboardNav />
<div className="grow bg-gray-1 p-4">
<Outlet />
</div>
</div>
</Panel>
</PanelGroup>
);
}
import { AppLoadContext } from "@remix-run/cloudflare";
import { createServerSideHelpers } from "@trpc/react-query/server";
import { appRouter } from "./routers/index.server";

export function getTrpcServerSideHelpers(context: AppLoadContext) {
return createServerSideHelpers({ ctx: context, router: appRouter });
}
import { AppLoadContext } from "@remix-run/cloudflare";
import { createServerSideHelpers } from "@trpc/react-query/server";
import { appRouter } from "./routers/index.server";

export function getTrpcServerSideHelpers(context: AppLoadContext) {
return createServerSideHelpers({ ctx: context, router: appRouter });
}
i also used v11 so it should work @sh03 HydrationBoundary is from react query
sh03
sh032mo ago
I see. This is awesome. Thank you very much 🙏 I owe you one

Did you find this page helpful?