SSR with Vike¶
The Website uses Vike (with vike-react) as its SSR
meta-framework. Vike provides file-based routing and the render hooks; the
Cloudflare Worker drives them through renderPage().
Global configuration¶
pages/+config.ts sets the global Vike behavior:
export default {
title: "Swapps",
extends: vikeReact,
ssr: true, // SSR for all pages
hydrationCanBeAborted: true,
clientRouting: false, // plain HTML navigation, no SPA client router
trailingSlash: true, // URLs always end with "/"
passToClient: [
"urlOriginal", "urlPathname", "routeParams",
"data", "locale", "basePath", "_urlRewritten",
],
};
Key choices:
ssr: true— every page is rendered on the server.clientRouting: false— navigation uses real HTML links, not a client-side router. Each navigation is a fresh server render.trailingSlash: true— canonical URLs end with/(the Worker also issues trailing-slash redirects).passToClient— these fields are serialized into the client so hydration and components (header config, locale) have what they need.
File-based routing¶
Routes are defined by the filesystem under pages/. A page directory contains:
| File | Purpose |
|---|---|
+Page.tsx |
The page React component |
+data.ts |
Server-side data fetching (runs on the server) |
+Head.tsx |
Page-specific <head> tags (optional) |
+Layout.tsx |
Page-specific layout override (optional) |
+route.ts |
Custom route matching (optional) |
Dynamic segments use the @param convention, e.g. pages/blog/@slug/+Page.tsx
matches /blog/<slug>/. See Project structure
for the full page tree.
Rendering pipeline¶
Server: +onRenderHtml.tsx¶
onRenderHtml runs on the Worker and produces the full HTML document:
- Renders the React tree (
<Layout><Page/></Layout>) withrenderToString. - Detects the language (URL
/es/prefix or page data) and resolves SEO metadata — either Yoastyoast_head_jsonfrom the CMS or a default meta block, with title / description / keywords / robots / canonical, Open Graph, and Twitter cards. - Adds hreflang alternate links (from page-provided
alternateUrlsfor blog posts, or derived fromroutes.jsonfor static pages). - Injects JSON-LD, GTM and gtag.js scripts (suppressed for Percy CI runs), and font preloads.
- Inlines critical CSS (reset, container, header, nav) into a
<style>block to avoid FOUC, then mounts the rendered app into<div id="root">.
It returns the document plus the HTTP status code (404 when is404).
Client: +onRenderClient.tsx¶
onRenderClient hydrates the server HTML in the browser with
hydrateRoot(container, <StrictMode><Layout><Page/></Layout></StrictMode>),
recovering gracefully from hydration mismatches via onRecoverableError.
Data fetching¶
Each route's +data.ts exports a data() function that runs on the server during
SSR. It receives the Vike pageContext (including the Cloudflare bindings) and
fetches what the page needs via the shared fetchData() helper — which uses the
API_WORKER Service Binding. The result is exposed to the page component and,
because data is in passToClient, to the client.
See Service bindings and Blog system.
Internationalized routing¶
The Website serves English (default) and Spanish (/es/) from a single set of page
files. The global hook pages/+onBeforeRoute.ts runs before Vike picks a page:
- It detects the locale from the URL (
/es/prefix →es). - For Spanish URLs, it rewrites the path to its canonical English route using
src/locales/routes.json(staticroutesmap +dynamicPatternsregexes), so Vike finds the right+Page.tsx. Dedicated Spanish-only pages (dedicatedEsPages) are left untouched. - It injects
localeandbasePathinto the page context (passed to the client).
{
"routes": {
"/services/": { "en": "/services/", "es": "/es/servicios/" }
},
"dynamicPatterns": [{ "es": "^/es/blog/([^/]+)/?$", "en": "/blog/$1/" }]
}
This is why adding a page also requires adding its route mapping to routes.json
in both languages.
Server entry and the Worker¶
vike build emits a server entry that scripts/build-worker.ts copies alongside
the esbuild-bundled Worker (scripts/patch-server-entry.ts patches the generated
entry first). At runtime src/worker.ts imports Vike's renderPage and invokes it
per request. See Cloudflare Workers and
Build and styling.