# Remix / React Router 7

> Loader-based SSR for both Remix and React Router 7 framework mode.

## Index loader

```tsx
// app/routes/blog._index.tsx
import { json, useLoaderData, type LoaderFunctionArgs } from "react-router";

export async function loader({ context }: LoaderFunctionArgs) {
  const res = await fetch(`${process.env.MENTIONWELL_API_URL}/api/public/${process.env.MENTIONWELL_SITE_SLUG}/posts?limit=24`, {
    headers: { Authorization: `Bearer ${process.env.MENTIONWELL_API_KEY}` }
  });
  const { posts } = await res.json();
  return json({ posts }, { headers: { "Cache-Control": "public, s-maxage=300, stale-while-revalidate=86400" } });
}

export default function BlogIndex() {
  const { posts } = useLoaderData<typeof loader>();
  return <ul>{posts.map((p: any) => <li key={p.slug}>{p.title}</li>)}</ul>;
}
```

## Detail loader

```tsx
// app/routes/blog.$slug.tsx
import { json, useLoaderData, type LoaderFunctionArgs } from "react-router";

export async function loader({ params }: LoaderFunctionArgs) {
  const res = await fetch(`${process.env.MENTIONWELL_API_URL}/api/public/${process.env.MENTIONWELL_SITE_SLUG}/posts/${params.slug}`, {
    headers: { Authorization: `Bearer ${process.env.MENTIONWELL_API_KEY}` }
  });
  if (res.status === 404) throw new Response("Not Found", { status: 404 });
  const { post } = await res.json();
  return json({ post });
}

export default function BlogPost() {
  const { post } = useLoaderData<typeof loader>();
  return (
    <article>
      <h1>{post.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: post.html }} />
    </article>
  );
}
```


---

Canonical URL: https://mentionwell.com/docs/frameworks/remix
Live HTML version: https://mentionwell.com/docs/frameworks/remix
Section: Quickstarts by stack
Site index for AI ingestion: https://mentionwell.com/llms.txt
Full reference: https://mentionwell.com/llms-full.txt
