# Next.js SSR

A Next.js App Router app built on `@ic-reactor/core` and `@ic-reactor/react`
with TanStack Query's own hooks. It reads the ICP, ckBTC and ckETH ledgers on
mainnet through one module generated from `icrc1.did` by `candid-core-cli gen`,
and shows the server-rendering story end to end:

- **One client per request.** A Server Component builds
  `createClient({ network: "ic", identity: "anonymous" })` inside the request
  (React's `cache()`), prefetches every ledger's name, symbol, decimals, fee,
  total supply, minting account and metadata with `{ id }` targets, and hands
  `dehydrate(client.queryClient)` to a `<HydrationBoundary>`.
- **The provider factory in a `'use client'` module.**
  `<ReactorProvider client={() => createClient({ network: "ic", auth: () => new AuthClient() })}>`
  builds a client per mounted tree. The server render and the browser's
  hydrating render build keys for the anonymous principal, so every visitor
  gets the server's keys, a tab still signed in from an earlier visit
  included, and the cards hydrate from them with nothing fetched on load.
- **Lossless hydration.** `bigint` amounts, principals and `Uint8Array`
  values arrive exact; the page prints each value's run-time type.
- **No JavaScript needed** to read the pages: the values are in the server's
  HTML, which a smoke script checks against a running `next start`.
- **Streaming.** On the account page, certified balances (replicated calls)
  stream in after the rest of the page, from an async Server Component inside
  `<Suspense>`.
- **Progressive enhancement.** The account lookup is a GET form validated on
  the server with `isPrincipal` and `principal()`.
- **A Route Handler** with its own client per request answers JSON, with
  amounts as decimal text and base units, and maps a `ReactorError` to an
  HTTP status by its `kind`, reading `httpStatus` where the kind is not enough:
  a canister that does not exist is a 502, not a 503 that invites a retry.
- **Errors on the server.** A canister that is not a ledger renders its own
  section with the error's `kind`; the rest of the page renders as usual.
- **Sign-in after hydration.** Signing in with Internet Identity moves every
  read to keys that hold the user's principal; nothing read anonymously is
  reused for them. A tab that reloads signed in hydrates without a mismatch,
  then reads its own keys the same way.

The tests run the real client on `createTestClient()` from
`@ic-reactor/core/testing`, with the ledgers mocked over in-memory replicas,
and never reach mainnet.

The [SSR and hydration guide](https://ic-reactor.b3pay.net/v4/guides/ssr/) explains each rule, and
[Auth](https://ic-reactor.b3pay.net/v4/guides/auth/) covers sign-in and the caller.

StackBlitz installs the example from npm and runs the tests (`npm test`):
you can read and change the code there and run them again. It cannot serve
the app. Next 16 keeps each request's state in Node's `AsyncLocalStorage`,
which StackBlitz does not implement, so `next dev` fails there on every page
with Next's own "Invariant: Expected workStore to be initialized", and a
production build renders through the same storage. Clone the repository to
run the app.

[Open in StackBlitz](https://stackblitz.com/github/b3pay/ic-reactor/tree/v4/examples/next-ssr?file=src/app/page.tsx)
  [View on GitHub](https://github.com/b3pay/ic-reactor/tree/v4/examples/next-ssr)