3.4 KiB
3.4 KiB
title, description
| title | description |
|---|---|
| React Router (Remix) & Sanity Integration Rules | Integration guide for React Router (formerly Remix) with Sanity, including Loaders and Visual Editing. |
React Router (Remix) & Sanity Integration Rules
Version Note
This guide covers both:
- Remix v2 (
@remix-run/*packages) - React Router v7 (the successor to Remix,
react-routerpackage)
The Sanity integration pattern is the same for both. Import paths differ slightly:
| Remix v2 | React Router v7 |
|---|---|
@remix-run/node |
react-router |
@remix-run/react |
react-router |
remix.config.js |
react-router.config.ts |
The examples below use Remix v2 imports. Adjust if using React Router v7.
1. Setup & Client Pattern
To support both server-side fetching and client-side live previews, use the Split Loader Pattern.
A. Shared Loader (app/sanity/loader.ts)
Defines the store config (SSR enabled, client deferred).
import { createQueryStore } from '@sanity/react-loader'
export const {
loadQuery,
setServerClient,
useQuery,
useLiveMode,
} = createQueryStore({ client: false, ssr: true })
B. Server Loader (app/sanity/loader.server.ts)
Initializes the server client.
import { createClient } from '@sanity/client'
import { loadQuery, setServerClient } from './loader'
const client = createClient({
projectId: process.env.SANITY_PROJECT_ID,
dataset: process.env.SANITY_DATASET,
useCdn: true,
apiVersion: '2026-02-01',
stega: {
enabled: true,
studioUrl: 'https://my-studio-url.com',
},
})
setServerClient(client)
export { loadQuery }
2. Data Fetching (Loaders)
Use loadQuery from your server file in route loaders.
import type { LoaderFunctionArgs } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { loadQuery } from "~/sanity/loader.server";
import { POSTS_QUERY } from "~/sanity/queries";
export async function loader({ params }: LoaderFunctionArgs) {
const initial = await loadQuery(POSTS_QUERY, params);
return { initial, query: POSTS_QUERY, params };
}
export default function Index() {
const { initial, query, params } = useLoaderData<typeof loader>();
// ... pass to component
}
3. Real-time Preview & Visual Editing
A. Use useQuery in Components
Import useQuery from your shared loader file.
import { useQuery } from "~/sanity/loader";
export default function Page() {
const { initial, query, params } = useLoaderData<typeof loader>();
const { data, encodeDataAttribute } = useQuery(query, params, {
initial
});
return (
<h1 data-sanity={encodeDataAttribute('title')}>
{data?.title}
</h1>
);
}
B. Enable Live Mode (VisualEditing.tsx)
Create a component to handle the connection.
import { enableVisualEditing } from '@sanity/visual-editing'
import { useLiveMode } from '~/sanity/loader'
import { client } from '~/sanity/client' // Your browser-safe client
import { useEffect } from 'react'
export default function VisualEditing() {
useEffect(() => enableVisualEditing(), [])
useLiveMode({ client })
return null
}
Render this component in root.tsx only when valid (e.g., check env vars or user session).
4. Stega Cleaning
When using data for logic (routing, classNames), use stegaClean.
import { stegaClean } from "@sanity/client/stega"
// ...
if (stegaClean(slug) === 'home') { ... }