Next.js

Goodfellow pages and the admin panel in a Next.js app, exported as static files, and how it compares with goodfellow build.

@goodfellow-cms/next puts a Goodfellow site in a Next.js app (App Router, Next.js 16), exported as static files so the same free hosts serve it. The Starter for Next.js has it all set up.

  • withGoodfellow() in next.config.ts exports static files with a folder per page, serves the site from basePath (or GOODFELLOW_BASE), and in next dev runs the admin panel's local backend, which saves to the files on disk. The local backend is never part of a build.
  • goodfellowPages(config) gives the routes their parts: Layout for app/(site)/layout.tsx, which shows the header and footer once around every page, so they stay as they are when moving between pages (menus mark the current page in the browser, since a layout doesn't know it, and on a site in several languages the layout has the header and footer in each language and the browser shows the page's); Page, generateStaticParams and generateMetadata for app/(site)/[[...path]]/page.tsx; NotFound for app/not-found.tsx, with the header and footer of its own, since Next.js shows it outside the layout; sitemap and robots, and demoContent for app/admin/demo-content.json/route.ts, which serves a demo's content.
  • <GoodfellowAdmin site={site.admin()} /> from @goodfellow-cms/next/admin is the admin panel, at app/admin/page.tsx, a Server Component, so the admin panel's page knows the site's blocks only by their fields. The blocks themselves run in the editor, <GoodfellowEditor config={config} /> from @goodfellow-cms/next/editor in a Client Component at app/admin/editor/page.tsx, which the admin panel shows in a sandboxed frame (see the editor's sandbox). An app/admin/page.tsx from before the editor's frames, a Client Component passing config, loads every block's code itself, so the admin panel only says how to update it there. goodfellow-next update replaces it with the starter's, and adds app/admin/editor/page.tsx, unless someone has changed it; otherwise copy the starter's two files. In next dev, the editor's frames come from a bundle the local backend serves, rebuilt as the site's code changes, since Next.js won't serve its development files to sandboxed frames.
  • Pages render as Server Components. Links between the site's pages use next/link, so moving between pages doesn't reload, and images use next/image with their sizes filled in. Images are served as they are unless the site sets a custom image loader, since a static export can't resize them on request.
  • Blocks can render Client Components with any React hooks, and they run in the browser. They can read the site with useSite().
  • After next build, the starter's build script runs goodfellow-next finish out, which gives the editor's page its Content Security Policy, writes the calendar files of collections of events, collections' feeds, the content API, the pages at redirects' old addresses and _redirects for Cloudflare Pages, a copy of the site's Google Fonts, which the exported pages then load in place of Google's, plugins' admin frames and what their build steps write, the search index if a page has a Search block, and goodfellow-build.json for the nightly check, with the dates the pages next build rendered say what they show changes on, and lists what automated accessibility checks find on the exported pages. On a site with an admin address, it then starts every exported page with the script that sends it away from there, and writes Cloudflare Pages' header rules; withGoodfellow() adds Vercel's. In next dev, calendar files and feeds come from the local backend, and there's no content API. On Windows, it also moves the files Next.js uses to load pages ahead to where browsers look for them, since Next.js writes them into folders there. Keep it in your build script.

Compared with goodfellow build

Interactive blocks work with both, but goodfellow build runs each Client Component as a separate island, which can't share React context, can't change the content it's given, takes only plain props, and starts again on every page.

Next.js suits developers who want client-side navigation, their own Next.js pages beside the site's, or Next.js's ecosystem. It costs more: each page loads Next.js's JavaScript and carries its content again as data, builds take longer, and there are more dependencies to keep up to date. Most of Next.js's server features don't apply, because Goodfellow sites are static, unless the app is a full-stack site with a server of its own. For a content site with a few interactive parts at most, goodfellow build is lighter and simpler.