@founderhq/next-blog
v1.0.5
Published
Next.js blog SEO helpers, editable templates, and installer for FounderHQ-compatible Payload blogs
Readme
@founderhq/next-blog
Next.js App Router SEO helpers, editable blog templates, and installer for FounderHQ-compatible Payload blogs.
Install
npx @founderhq/next-blog@latest initMinimum Requirements
For the automatic Next.js installer path:
- Next.js App Router project with
app/orsrc/app/ - Next.js
>=16.2.6 <17.0.0when using Payload3.85.x - Node.js
^18.20.2 || >=20.9.0 - React and React DOM
>=18 - TypeScript project config that can resolve generated path aliases
- One supported Payload database adapter: Postgres, local SQLite, or Turso/libSQL
The CLI can also generate a framework-neutral Payload setup for non-Next projects, but public blog routes, metadata, RSS, robots, and sitemap wiring must then be implemented manually in that framework.
Payload must be able to reach its own REST API at runtime. In local development,
generated blog helpers default to http://localhost:${PORT || 3000} unless
PAYLOAD_PUBLIC_SERVER_URL is set. In production, set NEXT_PUBLIC_SITE_URL
to the public site origin, and set PAYLOAD_PUBLIC_SERVER_URL only when the
Payload API is served from a different origin.
The installer is conservative:
- dry-run preview first, then asks whether to apply
- supports
--dry-runfor preview-only runs - supports
--yesfor non-interactive apply after plan generation - supports
--env-mode write|printto choose runtime env handling - refuses dirty git by default
- prints a diff preview before writing
- uses dedicated Payload env vars
- asks for database and site env values
- supports Next App Router automatically and non-Next projects with a framework-neutral manual integration plan
- always writes safe examples to
.env.exampleon apply - lets you either write runtime env values to
.envand.env.local, or print them for copy-paste into your own env setup - installs only the selected Payload database and storage adapters
- asks before running migrations and validates env first
- aborts with manual instructions for unsupported project shapes, existing Payload configs, unsupported Next config filenames, and generated file path collisions
- copies editable blog UI into your app instead of forcing package-owned visual components
Supported v1 databases:
- Postgres through
@payloadcms/db-postgres - local SQLite through
@payloadcms/db-sqlite - Turso/libSQL through
@payloadcms/db-sqlite
MongoDB is intentionally manual/advanced in v1.
Supported installer storage choices:
- local
- S3
- R2
- Vercel Blob
- Azure Blob
- Google Cloud Storage
- UploadThing
- custom/manual
The custom/manual choice generates a no-op founderhq-blog.storage.ts so
migrations and the admin app can boot before the customer adds their adapter.
Edit that file before production uploads.
Payload uses dedicated env vars and never reuses an app DATABASE_URL
silently:
PAYLOAD_DATABASE_URLPAYLOAD_DATABASE_AUTH_TOKENPAYLOAD_SECRETNEXT_PUBLIC_SITE_NAMENEXT_PUBLIC_SITE_URLNEXT_PUBLIC_BLOG_ROUTE_PREFIXPAYLOAD_MEDIA_CDN_URLoptional; set only when uploaded media should be served from a CDN/custom asset domain
The v1 installer requires a non-root blog route prefix such as /blog or
/insights. Root-mounted blogs can be wired manually after install if a client
needs the blog to own /.
Install model
The package uses a hybrid model:
@founderhq/payload-cms-kitowns Payload collections, fields, blocks, hooks, and rich text behavior.@founderhq/next-blog/serverowns portable server helpers for fetching, normalization, metadata, JSON-LD, RSS, sitemaps, redirects, and search.npx @founderhq/next-blog initcopies editable UI templates into the target project, similar to the shadcn open-code model.
Fresh installs generate files like:
FOUNDERHQ_BLOG_INTEGRATION.mdpayload.config.tswith Payload admin users, API-key support, and/payload-apisrc/components/blog/blog-components.tsxsrc/components/blog/blog.csssrc/app/blog/page.tsxsrc/app/blog/page/[page]/page.tsxsrc/app/blog/[slug]/page.tsxsrc/app/blog/search/page.tsxsrc/app/blog/authors/page.tsxsrc/app/blog/authors/[slug]/page.tsxsrc/app/blog/authors/[slug]/page/[page]/page.tsxsrc/app/blog/categories/page.tsxsrc/app/blog/categories/[slug]/page.tsxsrc/app/blog/categories/[slug]/page/[page]/page.tsxsrc/app/blog/tags/page.tsxsrc/app/blog/tags/[slug]/page.tsxsrc/app/blog/tags/[slug]/page/[page]/page.tsxsrc/app/blog/rss.xml/route.tssrc/app/sitemaps/blog-posts.xml/route.tssrc/app/sitemaps/blog-authors.xml/route.tssrc/app/sitemaps/blog-categories.xml/route.tssrc/app/sitemaps/blog-tags.xml/route.tssrc/app/sitemap.xml/route.tswhen no root sitemap existssrc/app/robots.tswhen no robots route/file exists
Customers should edit the generated component and CSS files to match their
site. The default CSS ships with FounderHQ dark/orange tokens on
.fhq-blog-scope, plus a ready .fhq-blog-scope[data-theme="light"] token set
and [data-theme="light"] .fhq-blog-scope inheritance. Cards and controls read
semantic fill, border, table, focus, media, and shadow tokens, so most rebrands
should stay inside the token block. The default font tokens use system stacks;
load brand fonts in the host app and override the font tokens when needed. The
package remains the maintained source for CMS and SEO primitives.
If a project already has payload.config.*, the v1 installer refuses to
overwrite it. Add @founderhq/payload-cms-kit manually through
founderHQBlogPlugin() or the collection/global factories so existing Payload
collections, plugins, access rules, and storage settings stay intact.
For non-Next projects, the CLI does not generate public framework routes. It
creates payload.config.ts, storage config, framework-neutral blog data
helpers, env/scripts, and FOUNDERHQ_BLOG_INTEGRATION.md with the exact route,
RSS, robots, and sitemap contract to implement in that framework.
Sitemaps
Fresh installs create this structure:
/sitemap.xmlas a sitemap index/sitemaps/blog-posts.xmlfor blog index pagination and article URLs/sitemaps/blog-authors.xmlfor the author directory, author pages, and author archive pagination/sitemaps/blog-categories.xmlfor the category directory, category pages, and category archive pagination/sitemaps/blog-tags.xmlfor the tag directory, tag pages, and tag archive pagination
If the target app already has a simple app/sitemap.ts, the installer patches
that metadata sitemap to include indexable blog URLs directly, because Next
metadata sitemaps emit URL sets rather than sitemap indexes. If the target has
a static public/sitemap.xml that is already a sitemap index, the installer
adds the blog child sitemap links. If the existing sitemap is a custom route
handler or a static URL set that cannot be patched safely, the installer still
creates /sitemaps/blog-*.xml routes and prints manual merge instructions.
The generated blog sitemap excludes posts marked noindex and posts with an
external canonical URL.
Other SEO Routes
Fresh installs generate a robots.ts route pointing crawlers at /sitemap.xml.
If a project already has static public/robots.txt, the installer appends a
missing Sitemap: line. Existing app robots metadata routes are not force
patched; the installer warns if one does not appear to include a sitemap.
Generated blog routes include canonical metadata for the blog index, noindex
metadata for search results, JSON-LD for articles, RSS, manual redirect support,
previous-slug redirects from Payload slug history, and canonical metadata for
author, category, tag, and paginated archive routes.
Runtime helpers
import { buildArticleJsonLd, buildRssXml } from "@founderhq/next-blog/server";The server helpers normalize Payload REST documents into renderable articles, including stored Lexical HTML, heading IDs, TOC items, JSON-LD, RSS, sitemap entries, robots config, redirects, and adapter-neutral search.
The package still exports @founderhq/next-blog/components and
@founderhq/next-blog/styles.css for compatibility, but new installs use local
editable templates.
Optional Media CDN
Set PAYLOAD_MEDIA_CDN_URL to a CDN or custom asset origin, for example
https://cdn.example.com or https://cdn.example.com/assets, when uploaded
media should be served away from the storage adapter's default public URL.
Leave it blank to use Payload/storage URLs unchanged. The same optional value
is used for featured images, author avatars, SEO images, JSON-LD, and inline
rich-text uploads.
