Isomorphic Execution
Archibald code is isomorphic: the same components and hooks run on the server (SSR), in the browser, and — for app projects — in React Native. What differs per environment is not your import, but which build of a package that import resolves to.
One import, three entry points
Every @archibald/* package declares conditional exports in its package.json, so import { useFetch } from '@archibald/client' resolves to a different bundle per environment:
| Condition | Resolves to | Runs in |
|---|---|---|
browser | lib/client.* | the browser |
node (and default) | lib/node.* | the SSR server |
react-native | lib/expo.* / lib/native.* | the native app |
The node build typically re-exports everything the client build has plus server-only modules — which is why getSession from @archibald/auth exists on the server and throws in the browser, and why server-only code (Hapi routes, controllers, providers) never bloats the browser bundle. Application code follows the same split via the src/<platform>/client and src/<platform>/server trees.
SSR, then hydration
A request renders in two phases:
- Server render — components run in Node. Data hooks like
useFetchexecute their actions on the server; results are serialized into the HTML as transferred state. - Hydration — the browser re-runs the same components. Data hooks find the transferred state in the cache and do not refetch; interactive islands attach their event handlers.
Both phases execute your component code, so it must not assume a browser. Branch with useSSR where the environments genuinely differ:
import { useSSR } from '@archibald/core';
function Widget() {
const { isServer } = useSSR();
if (isServer) {
return <StaticFallback />;
}
return <BrowserOnlyWidget />;
}
See useSSR and the hydration architecture for how islands, state transfer, and event replay work in detail.
Why hooks live in @archibald/core vs @archibald/client
The split follows the execution boundary, not topic:
@archibald/coreholds the isomorphic foundation — the router hooks (useLocation,useNavigate,useParams,useSearchParams),useSSR, theDataClientclass itself. Everything here is safe in any environment, including during the server render.@archibald/clientholds the React data/UI layer on top —useFetch,useMutation,useDataClient, lifecycle hooks. It ships distinct browser, node, and native builds because rendering and hydration behave differently in each.
So a feature package like @archibald/search imports useFetch from @archibald/client and FetchKey from @archibald/core — same runtime, two layers. When you write your own isomorphic module, mirror this: shared logic and interfaces in a common entry, environment-specific wiring behind client/node/native entry points.