Files
theater-ziefen-website/.agents/skills/sanity-best-practices/references/remix.md
T
johannes.gasser 57af0b8386
Build and Deploy / build-and-deploy (push) Successful in 2m53s
add skills
2026-05-18 08:39:42 +02:00

135 lines
3.4 KiB
Markdown

---
title: React Router (Remix) & Sanity Integration Rules
description: 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-router` package)
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).
```typescript
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.
```typescript
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.
```typescript
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.
```typescript
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.
```typescript
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`.
```typescript
import { stegaClean } from "@sanity/client/stega"
// ...
if (stegaClean(slug) === 'home') { ... }
```