# Use it in a React app (/docs/start/guides/react-app)

> Serve a rendered record from a Next.js or React Router route, set one section inside a React page, or attach one on the client.

An app keeps its own React, routes and styles. mdxui adds a page or one section as HTML: `renderPage` returns a whole document, and `renderBlock` returns one section. Every snippet below is a part of `react-app.ts`, which each docs build runs against Harbor tides' records.

## Prerequisites

- An app on React 19, Next.js App Router or React Router, with `@mdxui/site@7.0.0-alpha.0` installed as in the [Quickstart](https://next.mdxui.dev/docs/start/quickstart).
- Records in `pages/`, such as the [Harbor tides records](https://next.mdxui.dev/sites/harbor/records.zip).

## Serve a whole page from a route

A rendered record is a complete document, so a route answers with it as `text/html`. In Next.js, export it as `GET` from `app/pricing/route.ts`; in React Router, export it as a resource route's `loader`.

```ts
import { readFile } from 'node:fs/promises';
import { PageSchema, renderPage } from '@mdxui/site';

/**
 * A route handler that answers with a rendered record. It is a plain
 * Request-to-Response function, so it drops into a Next.js App Router
 * `app/pricing/route.ts` as `GET`, a React Router resource route as `loader`,
 * or a Worker's `fetch`.
 */
export async function GET(): Promise<Response> {
  const record = JSON.parse(await readFile('pages/pricing.json', 'utf8'));
  const html = renderPage(PageSchema.parse(record));
  return new Response(html, { headers: { 'content-type': 'text/html; charset=utf-8' } });
}
```

The page keeps its own Theme and stylesheet; nothing from the app's CSS reaches it.

## Set one section inside a React page

A server component renders one section into a declarative shadow root. The section's styles stay inside the root, and the app's styles stay out. No script runs.

```ts
import { createElement } from 'react';
import { renderToString } from 'react-dom/server';
import { BlockSchema, baseCss, blocksCss, context, primitivesCss, renderBlock } from '@mdxui/site';
import { THEMES, type ThemeName } from '@mdxui/themes';

/**
 * One section inside a React page, as a server component: a declarative
 * shadow root keeps the section's styles in and the app's styles out, and
 * needs no script. In JSX: <Section block={block} theme="quaypoint" />.
 */
export function Section({ block, theme }: { block: unknown; theme: ThemeName }) {
  const { theme: t, css } = THEMES[theme];
  const html = renderBlock(BlockSchema.parse(block), context(t, 'paper', t.icons));
  const styles = `<style>${css().replaceAll(':root', ':host')}${baseCss}${primitivesCss}${blocksCss}</style>`;
  return createElement(
    'div',
    { className: 'mdxui-section' },
    createElement('template', { shadowrootmode: 'open', dangerouslySetInnerHTML: { __html: `${styles}${html}` } }),
  );
}
```

Use it as `<Section block={block} theme="quaypoint" />`. The block is any section's `block` from a record; `BlockSchema.parse` refuses one past its bounds at its path.

## Load the Theme's typefaces

A shadow root cannot load a webfont, and the Theme's CSS holds no `@font-face`: the typefaces load from `<link>` tags in the document's `<head>`. Hoist them from the Theme's `head`, or the section sets in the system's fallback font:

```ts
/**
 * The Theme's typefaces load at document level: a shadow root cannot load a
 * webfont for its section, and css() holds no @font-face. Hoist the Theme's
 * head into the document <head>: its <link> tags, minus the theme-color meta,
 * which is the whole page's to set.
 *
 * Next.js App Router, in app/layout.tsx: render <ThemeHead theme="quaypoint" />
 * inside <head>. React Router: return themeLinks('quaypoint') from links().
 */
export function themeLinks(theme: ThemeName): { rel: string; href: string; crossOrigin?: 'anonymous' }[] {
  return [...THEMES[theme].head.matchAll(/<link\b([^>]*)>/g)].map(([, attrs]) => {
    const at = (name: string) => attrs.match(new RegExp(`\\b${name}="([^"]*)"`))?.[1];
    return { rel: at('rel') ?? '', href: at('href') ?? '', ...(/\bcrossorigin\b/.test(attrs) ? { crossOrigin: 'anonymous' as const } : {}) };
  });
}

export function ThemeHead({ theme }: { theme: ThemeName }) {
  return themeLinks(theme).map((l) => createElement('link', { key: l.href, ...l }));
}
```

In Next.js, render `<ThemeHead theme="quaypoint" />` inside `<head>` in `app/layout.tsx`. In React Router, return `themeLinks('quaypoint')` from the route's `links`.

## Attach a section on the client

A client component that receives the section's HTML as a string attaches the shadow root itself, after hydration:

```ts
/**
 * In a client component, after hydration: attach the shadow root yourself,
 * from HTML your server rendered with renderBlock and sent as a string.
 */
export function attachSection(host: HTMLElement, html: string): void {
  const root = host.shadowRoot ?? host.attachShadow({ mode: 'open' });
  root.innerHTML = html;
}
```

Render the string on the server with `renderBlock`, as in the server component, and send it to the client as a prop.

## Next steps

  - [Ship a Site](https://next.mdxui.dev/docs/start/ship): Every record in pages/ as one Site, on Node, Vite, React Router or a Worker.
  - [Embed one section](https://next.mdxui.dev/docs/components/renderings/embed): renderBlock in a shadow root, in any page.

