Sanity + Remix, what is the supported Croct integration path?
We run a Sanity-backed marketing site on Remix. Looking at the Croct docs I see SDKs for Next.js, React, Vue, and Nuxt, but nothing Remix-specific.
My instinct as a Remix dev is that everything belongs in the loader:
export async function loader({request}: LoaderFunctionArgs) { const hero = await getPersonalizedHero(request); return json({hero});}So the question is whether I should fetch from Croct in loaders via the HTTP API, or just drop the React SDK into the components client-side. Is one of these the supported path or are both fine
2 answers
Both work, and your loader instinct is the better default. There is no dedicated Remix SDK today; the documented SDKs are JavaScript, React, Next.js, Vue, Nuxt, and PHP. For Remix that leaves two paths:
Server-side, call the HTTP Content API from the loader. POST to /external/web/content on api.croct.io with your slot ID and a context object. One requirement that trips people up: context.page.url is mandatory, so pass it from the request:
export async function loader({request}: LoaderFunctionArgs) { const response = await fetch('https://api.croct.io/external/web/content', { method: 'POST', headers: {'X-Api-Key': process.env.CROCT_API_KEY}, body: JSON.stringify({ slotId: 'home-hero', context: {page: {url: request.url}}, }), });
return json({hero: await response.json()});}Pages then arrive personalized, with nothing to swap on the client.
Client-side, @croct/plug-react works fine in Remix, but content resolves after load, so the default renders first and the personalized version swaps in. For a marketing site I would keep content in loaders and use the React SDK only for event tracking. Either way your Sanity document remains the fallback when no experience applies.
Loader path it is. The context.page.url requirement would have cost me an hour, appreciate the heads up.
Small addition if you go the loader route: wrap the fetch with a timeout and a catch that returns your Sanity content directly. Then a slow or failed personalization call degrades to the default copy instead of delaying the whole document response. Same fallback philosophy the SDKs give you, just explicit in your own code.