- 前端
【免费下载链接】nextjs-notion-starter-kit
Deploy your own Notion-powered website in minutes with Next.js and Vercel.
本文以 readme.md 为骨架,结合本仓库源码,完整讲解如何用 Notion 作为 CMS、借助 react-notion-x 与 Next.js 在几分钟内搭建并部署一个高性能个人博客/作品集网站。读完本文,你将掌握
site.config.ts全量配置项、Vercel 部署注意事项、URL 路径映射机制、LQIP 预览图与 Redis 缓存、自动社交分享图、自动目录与暗色模式等功能的配置方法,并能理解其背后的源码实现。
一、项目概览与技术栈
nextjs-notion-starter-kit 是一个以Notion 作为 CMS(内容管理系统)的 Next.js 网站起步模板。作者用它驱动了自己的个人博客与作品集站点,核心能力包括:
- 只需几分钟完成初始化,所有配置集中在一个文件(
site.config.ts) - 借助 react-notion-x 提供对 Notion 内容的健壮渲染支持
- 基于 Next.js + TypeScript + React 构建
- 出色的页面加载性能与平滑的图片预览
- 自动生成社交分享图(Social Images)
- 自动生成美观的 URL(Pretty URLs)
- 自动生成文章目录(Table of Contents)
- 完整支持暗色模式
- 通过 CMD+K / CMD+P 快速搜索
- 针对不同设备尺寸的响应式设计
- 针对 Next.js 与 Vercel 做了优化
从 package.json 可以看到当前仓库的技术栈与版本:next(^16.2.0)、react(^19.2.4)、react-notion-x与notion-client/notion-types/notion-utils(均为 ^7.10.0)、lqip-modern(^2.2.1)、fathom-client(^3.4.1)、posthog-js(^1.362.0)、react-tweet(^3.3.0)、rss(^1.2.2)等。构建工具链方面使用了pnpm(packageManager: pnpm@10.29.3,见 package.json 与 pnpm-workspace.yaml)。
注意:README 中建议 Node.js 版本 >= 16,但以当前仓库 package.json 的
engines字段为准,要求Node >= 20,请按此准备本地环境。
二、快速起步:从 clone 到上线
官方建议的启动流程如下(见 readme.md 的 Setup 章节):
- Fork / clone 本仓库
- 修改 site.config.ts 中的若干配置项
- 安装依赖:
npm install(使用 pnpm 则可执行pnpm install) - 本地开发:
npm run dev - 部署到 Vercel:
npm run deploy(等价于vercel deploy) - 核对 Vercel 项目设置(详见下文「Vercel 部署配置」)
相关脚本定义在 package.json 中:dev对应next dev,build对应next build,start对应next start,deploy对应vercel deploy。
2.1 获取根页面 ID(rootNotionPageId)
这是整个项目唯一必需的核心配置。操作步骤:
- 建议在 Notion 中复制官方提供的默认示例页面作为起点(你可以在 Notion 中复制任一公开页面,结构无强制要求);
- 确保你的根 Notion 页面已设置为公开(Public);
- 复制该页面的链接,提取 URL 末尾形如
7875426197cf461698809def95960ebf的一段,这就是页面的Notion ID,填入rootNotionPageId。
在 lib/config.ts 中,rootNotionPageId会经过parsePageId(..., { uuid: false })解析,如果解析失败会直接抛出Config error invalid "rootNotionPageId",所以该值必须是合法 ID。
2.2 获取工作区 ID(rootNotionSpaceId,可选)
如需将站点限制为仅包含单个 Notion 工作区的页面,可设置rootNotionSpaceId。获取方式:在浏览器中打开你站点的任意页面,打开开发者控制台,访问全局变量block(即当前页面的 Notion 数据),输入block.space_id即可打印出工作区 ID。
这个变量在 components/NotionPage.tsx 中注入:客户端渲染时会将pageId、recordMap、block挂到window全局对象上,方便调试。工作区校验则在 lib/acl.ts 中完成——如果页面所属空间与site.rootNotionSpaceId不一致,会返回 404 错误;社交图接口的校验见 pages/api/social-image.tsx。
2.3 内容组织建议
官方建议在首页设置一个 NotionCollection(集合/数据库),用来集中存放所有文章、项目等内容。不过仓库对 Notion 工作区结构没有任何约束,你完全可以按照平时习惯自由组织内容。
三、site.config.ts 全量配置详解
所有配置都集中定义在 site.config.ts,其类型定义位于 lib/site-config.ts。下面按类别完整列出可配置项:
| 配置项 | 类型 | 必填 | 说明 |
|---|---|---|---|
rootNotionPageId | string | ✅ 必填 | 站点根 Notion 页面 ID |
rootNotionSpaceId | string \| null | 可选 | 限制页面所属 Notion 工作区 |
name | string | ✅ 必填 | 站点名称 |
domain | string | ✅ 必填 | 站点域名(生产环境 URL 基址) |
author | string | ✅ 必填 | 作者名称 |
description | string | 可选 | Open Graph 描述,默认'Notion Blog' |
language | string | 可选 | 站点语言,默认'en'(用于 RSS feed) |
twitter/github/linkedin | string | 可选 | 社交账号(页脚与 meta 标签使用) |
newsletter/youtube/zhihu/mastodon | string | 可选 | 更多社交入口;mastodon是完整的 profile URL,会换算为 handle 用于链接验证 |
defaultPageIcon | string \| null | 可选 | 全站默认 Notion 图标,默认null |
defaultPageCover | string \| null | 可选 | 全站默认封面图,默认null |
defaultPageCoverPosition | number | 可选 | 封面图定位,默认0.5 |
isPreviewImageSupportEnabled | boolean | 可选 | 是否启用 LQIP 预览图,默认true |
isTweetEmbedSupportEnabled | boolean | 可选 | 是否启用推文嵌入 |
isRedisEnabled | boolean | 可选 | 是否用 Redis 缓存预览图,默认false |
isSearchEnabled | boolean | 可选 | 是否启用站内搜索,默认true |
includeNotionIdInUrls | boolean | 可选 | URL 中是否包含 Notion ID,默认跟随isDev |
pageUrlOverrides | PageUrlOverridesMap \| null | 可选 | 将指定 Notion 页面 ID 映射到自定义路径 |
pageUrlAdditions | PageUrlOverridesMap \| null | 可选 | 补充路径映射(不覆盖默认路径) |
navigationStyle | 'default' \| 'custom' | 可选 | 导航风格,默认'default'(Notion 原生样式) |
navigationLinks | NavigationLink[] | 可选 | 自定义导航链接,需配合navigationStyle: 'custom' |
其中navigationLinks的每个元素支持title+pageId(站内页面)或title+url(外部链接)两种形式。示例配置已注释在 site.config.ts。
3.1 配置读取与默认值机制
lib/config.ts 是站点级配置的"总装车间",它从site.config.ts读取原始配置并补充默认值:
description默认'Notion Blog'(lib/config.ts)language默认'en'(lib/config.ts)defaultPageCoverPosition默认0.5(lib/config.ts)isPreviewImageSupportEnabled默认false(注意:site.config.ts中显式设为true,因此默认仓库行为是开启预览图的,lib/config.ts)includeNotionIdInUrls默认取isDev的值,即开发环境为true、生产环境为false(lib/config.ts)isSearchEnabled默认true(lib/config.ts)isRedisEnabled除配置项外,还可通过环境变量REDIS_ENABLED开启(lib/config.ts)
此外,lib/get-config-value.ts还提供了一层环境变量覆盖机制:可通过NEXT_PUBLIC_SITE_CONFIG传入 JSON 形式的配置片段,它会与site.config.ts做浅合并(spread)覆盖(lib/get-config-value.ts);getRequiredSiteConfig则会在缺少name/author/domain等必填项时抛出Config error: missing required site config value ...异常。
3.2 路径映射的校验逻辑
pageUrlOverrides/pageUrlAdditions的值在 lib/config.ts 中会经过cleanPageUrlMap校验:
- 页面 ID 必须能被
parsePageId解析,否则抛错Invalid ... page id; - 路径不能为空,且必须以
/开头(写入后内部会去掉开头的/); - 校验通过后会被整理为「路径 → pageId」的映射,同时生成反向映射
inversePageUrlOverrides供页面渲染时查询。
四、Vercel 部署配置(关键!)
官方特别强调:默认情况下社交预览图在 Vercel 上无法正常工作,原因是站点需要登录鉴权。必须从 Vercel 项目设置中关闭鉴权:
Project -> Settings -> Deployment Protection,禁用 Vercel Authentication。
如果你忘了这一步,爬虫(以及任何请求社交图的客户端)在抓取/api/social-image时会收到401 Unauthorized响应,导致分享卡片无法生成。
五、URL 路径机制:开发与生产的差异
项目的 URL 路径在开发与生产环境下默认不同(不过将任一环境的路径粘到另一环境通常也能正常解析):
- 开发环境:使用
/nextjs-notion-blog-d1b5dcf8b9ff425b8aef5ce6f0730202这种「标题 slug + Notion ID」的形式。开发时始终把 Notion 页面 ID 放在 URL 中,便于调试。 - 生产环境:使用
/nextjs-notion-blog这种更简洁的形式,去掉多余的 ID 噪音。
5.1 底层实现
该机制由 lib/map-page-url.ts 实现:mapPageUrl根据includeNotionIdInUrls(默认跟随isDev)决定是否在 slug 中附加 UUID;根页面始终映射为/,其余页面通过getCanonicalPageId(lib/get-canonical-page-id.ts)生成路径——该函数优先返回pageUrlOverrides的反向映射结果,否则回退到 react-notion-x 的getCanonicalPageId默认实现。
在构建阶段,lib/get-site-map.ts 会调用getAllPagesInSpace抓取空间内所有页面,并据此构建canonicalPageMap(slug → pageId 映射)。其中有一个细节:如果页面在 Notion 中显式设置了Public属性且为假,则该页面会被从站点地图中排除(lib/get-site-map.ts)。
5.2 按页面覆盖 slug
你可以通过在 Notion 数据库中为页面添加一个Slug文本属性,来覆盖默认生成的 slug——任何带有Slug属性的页面都会使用该值作为其 URL 路径。
注意:如果工作区内存在多个同名 slug 的页面,构建时会抛出「duplicate URL pathnames」错误(开发模式下会先打印error duplicate canonical page id警告,见 lib/get-site-map.ts)。
5.3 请求解析与 URI 缓存
当浏览器请求/some-slug时,lib/resolve-notion-page.ts 按以下顺序解析:
- 先尝试把
rawPageId直接当作 Notion 页面 ID 解析(parsePageId); - 失败则查询
pageUrlOverrides/pageUrlAdditions映射; - 仍未命中则查询数据库(Redis/内存)中缓存的
uri-to-page-id:${domain}:${environment}:${rawPageId}映射(该缓存当前不设 TTL); - 最后回退到
getSiteMap()的canonicalPageMap查表,命中后会把「URI → pageId」写入缓存以加速后续请求; - 全部未命中则返回 404(注意:404 结果不会被缓存)。
5.4 关于旧链接
官方提醒:如果你计划随时间修改页面标题,需要确保旧链接仍然有效。本项目目前不提供旧链接检测方案,建议借助 Next.js 内置的redirects 支持(在next.config.js中配置)来处理。
六、图片预览(LQIP)与 Redis 缓存
站点使用next/image高效地提供图片服务,并可通过lqip-modern生成预览图片(LQIP,低质量图片占位),实现平滑的图片加载体验。预览图默认开启,但由于生成过程较慢,可通过在 site.config.ts 中设置isPreviewImageSupportEnabled: false关闭。
6.1 源码实现
lib/preview-images.ts 是预览图的核心模块:
- 通过
getPageImageUrls收集页面内所有图片 URL,并追加默认图标/封面; - 以
concurrency: 8的并发度用p-map批量处理; - 每个 URL 先用
ky拉取图片二进制,再交给lqip-modern生成originalWidth、originalHeight、dataURIBase64三个字段组成的PreviewImage; - 使用
p-memoize做进程内记忆化缓存,避免重复计算。
生成后的preview_image映射会挂到 recordMap 上(见 lib/notion.ts),最终由NotionRenderer的previewImages属性消费(components/NotionPage.tsx)。
6.2 用 Redis 缓存加速后续构建
如果希望把生成的预览图缓存下来、加速后续构建,需要先准备一个外部 Redis 数据存储:
- 在 site.config.ts 中设置
isRedisEnabled: true; - 设置环境变量
REDIS_HOST与REDIS_PASSWORD指向你的 Redis 实例。
本地开发可添加.env文件:
REDIS_HOST='TODO' REDIS_PASSWORD='TODO'从源码看,Redis 相关变量还支持更多扩展:REDIS_URL、REDIS_USER(默认'default')、REDIS_NAMESPACE(默认'preview-images')以及REDIS_ENABLED(见 lib/config.ts)。底层使用@keyvhq/core+@keyvhq/redis封装(lib/db.ts):启用 Redis 时以redisUrl作为存储后端,未启用时退化为内存 Keyv 实例。
预览图和 Redis 缓存都是可选功能。如果不想处理它们,直接在站点配置中关闭即可。官方推荐的 Redis 提供商(如 Redis Labs 免费套餐)可以自行评估,不强制。
七、样式定制
所有针对 Notion 内容的自定义 CSS 都位于 styles/notion.css。这些样式主要覆盖 react-notion-x 导出的全局 CSS 类(react-notion-x/styles.css,在 pages/_app.tsx 中全局引入)。
从仓库源码可以看到一些典型的定制手法(styles/notion.css):
.notion { --notion-max-width: 720px; --notion-header-height: 54px; } .index-page { --notion-max-width: 900px; }即:普通文章页最大宽度 720px,首页(根页面)放宽到 900px。
每个 Notion block 都有唯一的类名,因此可以精确地针对单个块做定制,例如:
.notion-block-260baa77f1e1428b97fb14ac99c7c385 { display: none; }全局样式导入顺序见 pages/_app.tsx:依次为 KaTeX(数学公式)、Prism(代码高亮)、react-notion-x 核心样式、站点全局样式、Notion 样式覆盖、Prism 主题覆盖。
八、暗色模式
项目完整支持暗色模式,可通过页脚(Footer)中的太阳/月亮图标切换。实现细节:
- lib/use-dark-mode.ts 基于
@fisch0920/use-dark-mode,使用classNameDark: 'dark-mode'; - 切换按钮在 components/NotionPageHeader.tsx(页头)与 components/Footer.tsx(页脚)均有实现;
- 当处于暗色模式时,
NotionPage会给 body 添加dark-mode类(components/NotionPage.tsx),并同步更新<meta name="theme-color">(components/PageHead.tsx)。
九、自动社交分享图(Social Images)
所有 Open Graph 与社交 meta 标签都由 Notion 内容自动生成,让社交分享默认看起来专业。社交图通过Vercel OG Image Generation自动生成,默认的 React 模板位于 pages/api/social-image.tsx,可以按需调整。
9.1 生成机制
- 接口以 Edge Runtime 运行(
export const runtime = 'edge',pages/api/social-image.tsx); - 通过
?id=<pageId>查询参数指定页面(缺省时使用rootNotionPageId); - 使用
next/og的ImageResponse渲染1200×630的分享图(pages/api/social-image.tsx),字体为仓库内置的 Inter(lib/fonts/inter-semibold.ts); - 图片元素取自页面属性
Social Image→ 页面封面 → 站点默认封面;作者头像取自页面图标 → 默认图标;作者取自Author属性 → 站点author(pages/api/social-image.tsx); - 对于 Unsplash 图片还会自动附加
w=1200&fit=max参数以获取合适尺寸(pages/api/social-image.tsx)。
9.2 前端 meta 输出
components/PageHead.tsx 输出完整的 meta 集合:og:type、og:site_name、og:description、twitter:card(有图时为summary_large_image)、twitter:creator、canonical URL、RSS alternate 链接等;对于博客文章(isBlogPost)还会注入BlogPosting类型的 JSON-LD 结构化数据,强化 SEO(components/PageHead.tsx)。
社交图 URL 由 lib/get-social-image-url.ts 构造为/api/social-image?id=<pageId>;RSS feed 中也会将社交图作为每条文章的enclosure(图片附件)输出(pages/feed.tsx)。
十、自动目录(Table of Contents)
默认情况下,每篇博客文章页都会在桌面端以aside形式展示目录:
- 使用scrollspy(滚动侦测)逻辑:用户滚动文档时自动高亮当前章节,并支持点击跳转;
- 如果页面标题数量少于
minTableOfContentsItems(默认3),目录自动隐藏; - 目录在首页(index page)以及浏览器窗口过小(移动端)时也会隐藏。
相关代码:目录开关与阈值在 components/NotionPage.tsx(showTableOfContents = !!isBlogPost、minTableOfContentsItems = 3),传入NotionRenderer后由 react-notion-x 渲染;侧栏组件为 components/PageAside.tsx。
10.1 侧栏的差异化展示
components/PageAside.tsx 的展示策略很有意思:仅对博客文章(isBlogPost)展示「推文操作区」(Like / Retweet 快捷按钮,见 components/PageActions.tsx,底层是 Twitter/X 的 Web Intents);其他页面展示社交链接(components/PageSocial.tsx)。其中推文的嵌入数据由 lib/get-tweets.ts 预先抓取并缓存(含 Redis 缓存与p-memoize双层缓存),渲染时通过react-tweet展示(components/NotionPage.tsx)。
十一、响应式设计
所有页面都针对常见设备尺寸做了响应式适配(见 styles/notion.css 中的媒体查询与components/styles.module.css)。桌面端展示目录侧栏,移动端自动收起;导航栏在窄屏下可横向滚动。
十二、站点分析(Analytics)
分析功能是可选特性,按需开启:
12.1 Fathom Analytics
Fathom 提供轻量级的 Google Analytics 替代方案。只需添加环境变量NEXT_PUBLIC_FATHOM_ID即可启用——仅在生产环境生效。
从源码看,lib/config.ts 中fathomId在isDev时为undefined,并带有excludedDomains: ['localhost', 'localhost:3000']配置;pages/_app.tsx 中执行Fathom.load,并在每次路由切换完成时调用Fathom.trackPageview()。
12.2 PostHog Analytics
PostHog 提供轻量级、开源的 Google Analytics 替代方案。只需添加环境变量NEXT_PUBLIC_POSTHOG_ID即可启用——同样仅在生产环境使用。实现见 lib/config.ts 与 pages/_app.tsx(posthog.init+ 路由切换时posthog.capture('$pageview'))。
十三、环境变量总览
如果需要使用 Redis、分析或其他依赖环境变量的功能,需要将这些变量添加到 Vercel 项目(Project Settings -> Environment Variables)。汇总如下:
| 环境变量 | 作用 | 说明 |
|---|---|---|
REDIS_HOST | Redis 主机地址 | 启用isRedisEnabled后必需 |
REDIS_PASSWORD | Redis 密码 | 启用isRedisEnabled后必需 |
REDIS_URL | 完整 Redis 连接串 | 可选,优先于HOST/PASSWORD拼接 |
REDIS_USER | Redis 用户名 | 可选,默认'default' |
REDIS_NAMESPACE | Redis key 命名空间 | 可选,默认'preview-images' |
REDIS_ENABLED | 强制开启 Redis | 可选,等价于isRedisEnabled: true |
NEXT_PUBLIC_FATHOM_ID | Fathom 站点 ID | 仅生产生效 |
NEXT_PUBLIC_POSTHOG_ID | PostHog 站点 ID | 仅生产生效 |
NEXT_PUBLIC_SITE_CONFIG | JSON 配置覆盖 | 与site.config.ts合并,需合法 JSON |
NOTION_API_BASE_URL | Notion API 基址 | 传入NotionAPI构造(lib/notion-api.ts) |
在 GitHub Actions 中测试 Redis 构建:需要编辑.github/workflows/build.yml(README 提及,当前镜像仓库未包含该目录),为构建步骤添加REDIS_HOST与REDIS_PASSWORD,并同时在 GitHub 仓库的 Settings -> Secrets 中配置对应的 repository secrets。
十四、附带的站点能力:搜索、RSS 与 sitemap
除 README 主线功能外,仓库还内置了若干开箱即用的能力(均可在配置中控制):
- 站内搜索(CMD+K / CMD+P):由
isSearchEnabled控制(默认true)。前端 lib/search-notion.ts 以 10 秒过期缓存 POST 到/api/search-notion(pages/api/search-notion.ts 仅接受 POST,响应带Cache-Control),后端转发给 Notion 搜索接口(lib/notion.ts)。 - RSS Feed:pages/feed.tsx 基于
rss包生成 XML,遍历站点地图中属于根页面 collection 的博客文章,输出标题、链接、描述、日期与社交图附件,缓存 24 小时。 - Sitemap:pages/sitemap.xml.tsx 根据
canonicalPageMap生成sitemap.xml,缓存 8 小时。 - 404 / 错误页:components/Page404.tsx 与 pages/404.tsx、pages/_error.tsx 处理未找到与异常场景,配图位于 public/404.png、public/error.png。
lite模式:URL 带?lite=true时渲染精简版页面,用于 oEmbed 嵌入场景(components/NotionPage.tsx)。- 动态导入:代码高亮(Prism 支持 30+ 语言)、Collection、公式(KaTeX)、PDF、Modal、Tweet 均为按需动态加载(components/NotionPage.tsx)。
十五、许可证与贡献
本项目采用MIT许可证(见 license),作者为 Travis Fischer。贡献者请参阅 contributing.md。
小结:nextjs-notion-starter-kit 用一份site.config.ts收敛了绝大多数配置,配合 Notion 的公开页面与集合组织内容,即可快速搭建个人博客/作品集。理解 lib/config.ts 的默认值策略、lib/map-page-url.ts 的 URL 映射、lib/preview-images.ts 的 LQIP 流水线以及 pages/api/social-image.tsx 的 OG 图渲染,能让你在自定义站点时做到心中有数、按需取用。
- 前端
【免费下载链接】nextjs-notion-starter-kit
Deploy your own Notion-powered website in minutes with Next.js and Vercel.
相关推荐
WSABuilds PowerShell脚本Install.ps1源码解析:Windows 11安卓子系统自动化安装指南
WSABuilds PowerShell脚本Install.ps1源码解析:Windows 11安卓子系统自动化安装指南 WSABuilds项目中的PowerS
开发工具10分钟上线Notion网站:Next.js Notion Starter Kit全流程指南
10分钟上线Notion网站:Next.js Notion Starter Kit全流程指南 你是否曾想拥有一个个人网站却被技术门槛劝退?还在为内容管理系统(C
前端如何用 Next.js Notion Starter Kit 快速搭建现代网站:完整指南
如何用 Next.js Notion Starter Kit 快速搭建现代网站:完整指南 Next.js Notion Starter Kit 是一个功能强大的
前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考