# Server rendering

Both packages render on a server. Nothing reads `window` during render, the reveal's overlay only mounts in the browser, and the plain functions (`rollCards`, `chances`, `themeOf`) run anywhere. This page is about the three things left to you: where the client boundary goes, where the stylesheet goes, and what a returning visitor sees first.

## What is in the HTML

| Component | On the server |
| --- | --- |
| `CardHunt` | The whole panel: headline, button, grid, badges. Readable with JavaScript off. |
| `ScanGrid` | The grid, veiled or not. |
| `Card` | The card, face up or down. |
| `CardOpening` | Nothing. The overlay is a portal that mounts after hydration. |

## Next.js

`app/layout.tsx`

```tsx
import "@react-cards-opening/ui/styles.css";
```

`app/hunt.tsx`

```tsx
"use client";

import { useOpenedCards } from "@react-cards-opening/core";
import { CardHunt } from "@react-cards-opening/ui";

export function Hunt({ cards, levels }) {
  const { opened, open } = useOpenedCards("deal");
  return <CardHunt cards={cards} opened={opened} onOpen={open} levels={levels} />;
}
```

The file that uses the hooks is a client component: the packages do not mark themselves with `use client`, so that functions like `rollCards` stay callable from server code. The cards and the grid can come from a server component above it as props: they are plain data.

## Returning visitors

The server does not know what is in local storage, so its HTML shows every card unopened. `useOpenedCards` reads storage before the first paint, which is soon enough for most pages. If the two states look very different, hold the piece back until `ready`:

```tsx
const { opened, open, ready } = useOpenedCards("deal");
if (!ready) return <div style={{ minHeight: 360 }} />;
```

Or keep opened cards on the server and render the right state the first time. See [Keep opened cards](https://bettertyped.github.io/react-cards-opening/docs/guides/keep-opened-cards/).

## Random things

Anything random that is rendered has to be the same on both sides. Deal with a `seed` in [rollCards](https://bettertyped.github.io/react-cards-opening/docs/core/rolls/), and build the grid from data, not from `Math.random()`. Where a card hides is random on purpose, and it is picked in the browser when the scan starts, so it never reaches the HTML.

## Related

- [Installation](https://bettertyped.github.io/react-cards-opening/docs/installation/)
- [Keep opened cards](https://bettertyped.github.io/react-cards-opening/docs/guides/keep-opened-cards/)
- [useOpenedCards](https://bettertyped.github.io/react-cards-opening/docs/core/use-opened-cards/)
- [Rolls and odds](https://bettertyped.github.io/react-cards-opening/docs/core/rolls/)

Source: https://bettertyped.github.io/react-cards-opening/docs/guides/server-rendering/
