Next.js

@atolljs/nextjs — the React hooks re-exported for App Router client components. The server-side surface — node:worker_threads pools inside route handlers via @atolljs/node — is documented under Backend → Next.js.

Install

frontend
npm install @atolljs/core @atolljs/nextjs

Client components

Components calling the hooks carry the 'use client' directive — that boundary component is the edge; app/page.tsx can stay a server component that just renders it. Importing the connectWorker client is SSR-safe: the pool spawns lazily on the first method call, never during a server render, and field reads return undefined until the contract binds on the client.

components/Counter.tsx — a client componentfrontend
'use client';   // required — the hooks read browser-side state

import { useSharedValue, useTask } from '@atolljs/nextjs';
import { counterMemory } from '../counter.memory';
import { counter } from '../counter';   // connectWorker client — safe to import under SSR

export function Counter() {
  const count = useSharedValue(counterMemory, 'count');
  const increment = useTask(counter.increment);  // client method → latest-wins task

  return (
    <button onClick={() => increment.run(1)}>
      count: {count ?? '…'}
    </button>
  );
}

Binding API

ExportSignatureWhat it does
useObservableuseObservable(source: ObservableValue<T>): TSubscribe to any observable snapshot (task or field).
useSharedValueuseSharedValue(memory, key, select?, options?): T | undefinedBind one shared-memory field to React state; optional selector + equality to slice updates.
useTaskuseTask(task | asyncFn): { data, pending, settled, elapsedMs, error, run, runOnce }Bind an AsyncTask — or any async fn (e.g. a client method, wrapped via toTask) — to state and get its triggers.

Deployment

  • Next.js needs a Node runtime — next start, a Node host, or a platform like Vercel. Static file hosts (GitHub Pages, S3) can't run it, which is why the Pages deploy mounts every other example but not this one.
  • output: 'export' produces static HTML — client components still hydrate and client-side pools still spawn, but route handlers are dropped and next.config.ts headers() is not emitted. A shared-memory pool then needs the host (or a COI service worker, see Hosting & headers) to send COOP/COEP; a message-only pool needs neither.

Notes

  • 'use client' is required on any component calling the hooks; app/page.tsx can stay a server component that just renders it.
  • The connectWorker client is SSR-safe to import — its pool spawns lazily on the first method call, never during a server render.
  • COOP/COEP in next.config.ts headers() is only needed when the pool uses sharedMemory; a message-only pool needs neither headers nor SharedArrayBuffer.