Publishing Notion Content

How Notion content gets from the CMS into production.

Mental model

Source of truth for deploys = git commits, not live Notion calls.

Artifact In git? How it is updated
public/search-index.json Yes npm run publish:content (local)
public/.notion-cache/ Yes npm run publish:content (local)
public/components/ Yes (local/next dev cache) npm run publish:content (local)
Component seeds (Export/Preview) No β€” Netlify Blobs publish:content β†’ components:seeds:upload
public/_redirects, netlify/redirect-data.json No Regenerated on every npm run build (Netlify prebuild)

Netlify never persists .notion-cache between builds. Every deploy uses whatever is in the repo at that commit. Runtime component TSX is not served from git/out/ β€” publish:content uploads new seeds to Blobs so Export works after deploy.


Full pipeline

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  LOCAL PUBLISH  (when Notion content changes)                β”‚
β”‚                                                              β”‚
β”‚  npm run publish:content                                     β”‚
β”‚    1. build:search                                           β”‚
β”‚    2. build:metadata          (incremental, no --force)      β”‚
β”‚    3. build:search            (re-index after metadata)      β”‚
β”‚    4. build:content --refresh-shell + homepage gallery merge β”‚
β”‚    5. build:artifacts         (metadata + components)        β”‚
β”‚    6. validate:artifacts                                     β”‚
β”‚    7. components:seeds:upload (new Blobs seeds; skip exists) β”‚
β”‚                                                              β”‚
β”‚  git add -f public/search-index.json public/.notion-cache/   β”‚
β”‚            public/components/                                β”‚
β”‚  git commit -m "chore: publish notion content"               β”‚
β”‚  git push origin main                                        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β”‚
                           β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  NETLIFY DEPLOY  (automatic on every push)                   β”‚
β”‚                                                              β”‚
β”‚  prebuild β†’ redirects, edge-data, mcp-discovery, skills      β”‚
β”‚  next build β†’ static export to /out (strips components/code) β”‚
β”‚  Export/Preview reads Blobs via Components API               β”‚
β”‚  ~3 min, zero Notion API calls                               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

One command to publish

npm run publish:content
git add -f public/search-index.json public/.notion-cache/ public/components/
git commit -m "chore: publish notion content"
git push origin main

Requires .env.local with NOTION_API_KEY, NOTION_TOKEN, and GEMINI_API_KEY, plus Netlify CLI auth for seed upload (netlify login + netlify link, or NETLIFY_AUTH_TOKEN + NETLIFY_SITE_ID).

Netlify deploys automatically. No cache clear needed. New pattern seeds are already in Blobs from step 7 β€” no separate upload.


Secondary commands (low-level)

Use these only when you need a targeted run. Normal publishing should use publish:content.

Command Purpose
npm run build:search Rebuild public/search-index.json only
npm run build:content Incremental Notion page cache
npm run build:content -- --refresh-shell Refresh homepage + menuGroup DB views
npm run build:content -- --force Overwrite all .notion-cache files
npm run build:metadata AI descriptions + Notion sync (incremental)
npm run build:components Component TSX exports (incremental)
npm run build:artifacts Metadata + components orchestrator
npm run validate:artifacts Write artifacts report only
npm run components:seeds:upload Ship local TSX seeds to Netlify Blobs (also step 7 of publish:content)

Force flags (--force) are for recovery or full rebuilds β€” not part of the default publish flow.

Escape hatch: PATTERN_PUBLISH_SKIP_SEEDS_UPLOAD=1 npm run publish:content skips Blobs upload (debug only β€” new patterns will not Export on deploy).


--refresh-shell explained

build:content skips existing cache files by default. New pattern pages get their own cache file, but homepage and category database views can stay stale.

publish:content always passes --refresh-shell, which re-fetches:

  • Homepage (siteConfig.notionPageId)
  • Menu group DBs: ux-patterns, ui-patterns, all-patterns

It also merges the patterns gallery collection_query from the all-patterns shell into the homepage cache (Notion’s homepage fetch omits that data).


Env var reference (content builds)

Var Effect Default
NOTION_DEEP_COVER_SCAN Absent = content-body image scan on On
NOTION_COVER_DEBUG Verbose cover/image logs Off
NOTION_FORCE_IMAGE_SCAN Re-scan Notion body images (ignore cached images[]) Off
NOTION_API_DEBUG Verbose Notion request logs Off
NOTION_FAIL_FAST Fail on first Notion error Follows CI env
GEMINI_API_KEY Required for metadata/components generation β€”