feat: Notion blog with granular cache and stable slugs#61
Merged
adrienlupo merged 12 commits intomainfrom Mar 5, 2026
Merged
Conversation
Replace Lorem ipsum placeholder articles with real content fetched from the Notion database. Articles are cached using Next.js 16 "use cache" directive and revalidated on-demand via the existing webhook endpoint. - Add @notionhq/client SDK and lib/notion.ts with cached data functions - Add NotionRenderer server component for rendering Notion blocks - Update blog listing, article page, and homepage preview - Extract author from Notion "Author" people property - Show 2-line article preview (skipping metadata blocks) - Wrap BlogPreview in Suspense for async server component - Update TypeScript and @types/react for async component support - Delete hardcoded app/blog/data.ts
The "" || props.alt expression is always props.alt since "" is falsy. TypeScript 5.x strict mode catches this — simplified to just props.alt.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
- Catch errors in generateStaticParams so transient Notion failures don't crash the build (pages generate on-demand instead) - Increase Notion SDK retry limit from 2 to 3
Add support for a "Slug" property from Notion database to prevent URL breakage when article titles change. Falls back to slugify(title) when the property is empty or missing. Remove image block rendering from NotionRenderer since Notion-hosted file URLs expire after ~1h and break cached pages.
- BlogCard: replace 6 individual props with article: ArticleMeta - Deduplicate fetchArticleBySlug calls with React cache() - Fix missing French accents in not-found page - Extract getBlockRichText helper to replace ternary chains - Remove redundant userNameCache Map (already covered by "use cache")
Paginate articles 10 per page using ?page=N query parameter for shareability and SEO. Adds a Pagination server component with prev/next links and page number navigation.
Move article fetching and pagination into a child async component wrapped in <Suspense> so the static shell can be prerendered without blocking on the dynamic searchParams promise.
Drop the preview field from ArticleMeta and the extra Notion API calls that fetched first-paragraph excerpts. Combine date and author on a single line in BlogCard for a cleaner, more compact listing.
- Use Notion page ID as slug instead of slugified title to prevent URL breakage on title changes
- Replace fetchArticleBySlug with fetchArticleById using direct page retrieve (1 API call instead of full DB query)
- Add granular cache tags: "blog-list" for article listing, "blog-article-{id}" per article
- Add cacheLife("max") for aggressive caching invalidated only by webhook
- Parse Notion webhook event types for targeted cache invalidation
- Extract extractPageMeta() and resolveAuthor() shared helpers - Remove slug from ArticleMeta (always equal to id) - Parallelize author + blocks fetch in fetchArticleById - Parallelize searchParams + fetchArticles in blog page - Default webhook case is now a no-op (ignore unknown events) - Remove unnecessary UUID normalization in revalidate route
revalidateTag with "max" does not trigger immediate purge in Next.js
16.1.5. Switching to { expire: 0 } forces instant server + CDN cache
invalidation when Notion webhooks fire.
- Sanitize link hrefs to prevent XSS (reject non-http schemes) - Harden getBlockRichText with null safety instead of unsafe cast - Add image and bookmark block rendering to NotionRenderer - Remove verification_token bypass in revalidate webhook - Use "max" cache profile for revalidateTag calls with lookup table - Remove unused "blog-article" group cache tag - Add runtime guard for NOTION_DATABASE_ID env var - Limit fetchAllBlocks recursion depth to 5 - Fix French month accents (fev -> fev, aou -> aou) - Add error fallback in fetchArticles (return [] on failure) - Extract first paragraph as meta description instead of title - Add ellipsis pagination truncation for large page counts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replace Lorem ipsum placeholder articles with real content fetched from the Notion database. Articles are cached using Next.js 16 "use cache" directive and revalidated on-demand via the existing webhook endpoint.
Changes
Notion integration
lib/notion.tswith cached data functionsapp/blog/data.tsGranular cache and stable slugs (latest)
slugify(title)-- URLs no longer break when titles changefetchArticleById()usesnotion.pages.retrieve()(1 API call) instead of querying the full databaseblog-listfor article listing,blog-article-{id}per article (instead of a single sharedblog-articlestag)cacheLife("max"): aggressive caching invalidated only by webhookpage.content_updated,page.properties_updated,page.created,page.deleted) to invalidate only the relevant cache tags