The Next.js side of @spfn/pages: a validated site model becomes a
deployable site through a single optional catch-all route. Self-deploy carries a ~4-file
scaffold and runs anywhere Next.js or static output runs; SPFN hosting reuses the same
layouts and helpers, resolving the tenant per request.
The whole route surface is app/[[...slug]]/page.tsx —
createSitePages returns generateStaticParams,
generateMetadata, and the page component, and the content repo needs nothing
more than that ~4-file scaffold. Deploy it to Vercel, Cloudflare Pages, or your own
server; static export (output: 'export') is fully supported. The
multi-tenant hosted edge runs the same layouts and helpers with no scaffold in the repo
at all — registering the repo link is enough.
Two things in the site model can't be rendered by Next.js — raw HTML
escape-hatch pages and site/public/ assets — so spfn-pages sync
materializes them into public/ before the build and records every output in
public/.spfn-pages-sync.json. The next run deletes recorded outputs it no
longer produces — a moved landing, a removed asset — so stale pages never ghost into the
static export, while files sync never wrote are left alone. The content itself comes from
FsContentSource('.'), which reads the checked-out repo directly: builds see
exactly the commit they run on, no GitHub API calls, no rate limits.
One factory returns generateStaticParams, generateMetadata, Page, getSite.
The whole route surface as one optional app/[[...slug]]/page.tsx.
SiteShell, LandingLayout, DocLayout, PostLayout, PostsIndexLayout.
DocLayout navigates site.sections — aria-current, navTitle labels, sticky aside.
Raw HTML pages and site/public assets into public/, manifest-tracked. Also programmatic as syncSite.
Baseline stylesheet — every value overridable via --sf-* tokens.
site.favicon as the icon link, site.ogImage as default og:image, url: as metadataBase.
output: 'export' fully supported — Cloudflare Pages, Vercel, your own server.