# Sanity + Remix, what is the supported Croct integration path?

Asked by Darius King on 2026-07-01. Tags: sanity, remix, sdk, integration.

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:

```ts
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

### Accepted answer from Rachel Nunes (2026-07-01)

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:

```ts
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.

#### Reply from Darius King (2026-07-02)

Loader path it is. The context.page.url requirement would have cost me
an hour, appreciate the heads up.

### Answer from mbecker (2026-07-03)

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.
