@bettercms-ai/next

中文文档
0.39.0发布于 2026/11/05TypeScriptESM + CJS近 30 天下载 5106

Next.js的BetterCMS适配器——已打入,缓存-意识内容已读取(getEntry/list Enteries),预览草稿,构建时间内容快照,以及App路由器的llms.txt生成. /块,/形式,/场,/搜索和/视频编辑条目为被框

中文使用文档
文档目录20 节
  1. 安装
  2. 快开始
  3. 环境变量
  4. API 密码
  5. createBetterCMS<Schema>(config)
  6. getEntry<TFields>(slug, opts?) → BetterCMSEntry<TFields> | …
  7. listEntries(model, opts?) → EntryList<Fields>
  8. 缓存重新验证( R)
  9. 转换的页面的值, live ( bcmsPage (中文(简体) )
  10. CMS的图像( next.config (中文(简体) )
  11. 预览草稿
  12. 静态导出( N) output: "export" (中文(简体) )
  13. llms.txt
  14. 表单
  15. 页块( E) <BcmsBlocks> ——表格放入页面构建器
  16. CMS顺序中的章节( : <BcmsSections> (中文(简体) )
  17. 带有国家代码拾取器的电话字段
  18. SEO——每页 <head> 与 generateMetadata
  19. 在发布时保持 SSO 新鲜
  20. 许可证

@ bettercms-ai/ next (英语)

BetterCMS 适配器用于 下一个.js (App路由器). 输入,缓存 - 意识内容读取, 预览草稿,以及 llms.txt ——CMS下的定型地层.

与 @bettercms-ai/codegen: 生成 BetterCMSSchema 从你的 内容模型,然后获得完整类型 getEntry编号 :listEntries 自动完成.

安装

npm install @bettercms-ai/next
# peers: next >= 14, react >= 18

快开始

// lib/cms.ts
import { createBetterCMS } from "@bettercms-ai/next";
import type { BetterCMSSchema } from "./bettercms.generated"; // from @bettercms-ai/codegen

export const cms = createBetterCMS<BetterCMSSchema>({
  workspace: "acme",
  apiKey: process.env.BETTERCMS_API_KEY,
  // baseUrl defaults to https://api.bettercms.ai/api/v1/delivery
  revalidate: 60, // default ISR window (seconds); false = always fresh
});
// app/blog/[slug]/page.tsx
import { cms } from "@/lib/cms";
import { notFound } from "next/navigation";

export default async function Post({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params;
  const entry = await cms.getEntry<{ title: string; body: string }>(slug, {
    revalidate: 300,
    // The exact tags the revalidation webhook sends on publish (see "Caching & revalidation").
    tags: ["bcms:acme:all", `bcms:acme:entry:${slug}`],
  });
  if (!entry) notFound();
  return <article><h1>{entry.fields.title}</h1>{/* ... */}</article>;
}
// app/blog/page.tsx — list, typed by your model registry
const { items, hasNextPage } = await cms.listEntries("blog", { page: 1, perPage: 20 });

环境变量

createBetterCMS, (中文(简体) ), createDraftModeRoute, (中文(简体) ), createRevalidateRoute 财务报告和审定财务报表 <VisualEditing> 读取 没有 环境变量。 你传递每个值作为选项, 所以这些例子中的名称 只是一个公约。 套件在两个地方读出:

变量阅读者含义
BETTERCMS_API_KEYbettercms-snapshot 国 际能够读取模型和条目的工程密钥( 需要)
BETTERCMS_WORKSPACEbettercms-snapshot 国 际工作空间弹片( 需要)
BETTERCMS_API_URLbettercms-snapshot 国 际API 基础, 默认 https://api.bettercms.ai/api/v1
BETTERCMS_PREVIEWgetContent()1 读取活字代替 bcms-content.json (审查楼)

复次密语. 该值是项目重新验证的 Webhook 秘密(项目 −) 设置 – 调整 。 存储为 BCMS_REVALIDATE_SECRET数字 : createRevalidateRoute() 读取 当没有时 secret 通过。 没有两者之一,该路线接受未经签名的请求.

更好的CMS生成的部署工作流程 (连通回波). 重案组的秘密是 BCMS_API_KEY (只给内容-牵引步骤,从不给构建) 工作空间是repo 变量 BCMS_WORKSPACE_SLUG。建设步骤得到 PUBLIC_BCMS_API_URL, (中文(简体) ), PUBLIC_BCMS_WORKSPACE, (中文(简体) ), PUBLIC_BCMS_PROJECT_ID 财务报告和审定财务报表 PUBLIC_SITE_URL此软件包为零 他们为你。 让他们进去 createBetterCMS 你自己来 除非 你用一个 NEXT_PUBLIC_ 前缀.

API 密码

createBetterCMS<Schema>(config)

  • workspace ——工作空间弹(需要)
  • apiKey ——交付 API 密钥(按: Authorization: Bearer(中文(简体) )
  • baseUrl 编号 : previewBaseUrl ——覆盖 API 基础
  • revalidate ——默认的IRS窗口(秒)或 false

getEntry<TFields>(slug, opts?) → BetterCMSEntry<TFields> | null

由内容 swit 单独发布的条目. null 404号公路. 选项 : revalidate, (中文(简体) ), tags, (中文(简体) ), depth (0至2回水合), select (外地预测) 结果是 Partial<TFields>, (中文(简体) ), preview + 键 previewToken。 。 。 .

listEntries(model, opts?) → EntryList<Fields>

发布一个模型的条目,被标注. 掷出 BetterCMSError (CONTENT NOT FOUND) (法语) 对于未知的工作空间/模型;空模型返回空页. 选项 : page, (中文(简体) ), perPage, (中文(简体) ), revalidate, (中文(简体) ), tags, (中文(简体) ), depth, (中文(简体) ), select。 。 。 .

缓存重新验证( R)

读取流经 fetch,下一个补丁到数据缓存中。 过 tags 打电话 revalidateTag(tag) 发表. revalidate: false 与 标记 = 缓存-直到-tag; 无标签 = no-store (总是新鲜的).

重新验证的 Webhook 为每个已公布或未公布的资源发送了两个标记, 财务报告和审定财务报表 createRevalidateRoute 电话 revalidateTag 各页:

  • bcms:{workspace}:all: 工作空间中的一切
  • bcms:{workspace}:{type}:{slug}时 type 实值 entry 或者说 page

标记用这些字符串读取( T)bcms:acme:entry:${slug}, (中文(简体) ), bcms:acme:page:${slug}, (中文(简体) ), bcms:acme:all 页:1 客户端不为您添加, 以及其它形状的标签 绝不作废.

以“ 重新验证” 模式部署的目标( 在其他地方托管的网站) 可能指向同一路线: 它 刷新调用名称的路径和标记, 或整个站点没有命名时的标记 .

转换的页面的值, live (bcmsPage(中文(简体) )

一个网站 @bettercms-ai/convert 转换读取每页的值 bcms-content/<slug>.json。 。 。 . bcmsPage 读同页直播,形状相同,所以帮助者 bcms() 原文不变:

// app/page.tsx (Server Component)
import { bcmsPage } from "@bettercms-ai/next";
import { BcmsPage } from "@bettercms-ai/next/page";
import bcmsHomeSnapshot from "../bcms-content/home.json";

export default async function Page() {
  const bcmsHome = await bcmsPage("home", bcmsHomeSnapshot);
  return <BcmsPage value={bcmsHome}>{/* … */}</BcmsPage>;
}

// a "use client" file under it
import { useBcmsPage } from "@bettercms-ai/next/page";
const page = useBcmsPage();

在“草稿模式”中,它读取预览(/api/v1/preview/<slug>,无斑. 否则它会读到 从送出时已公布行, 缓存到公布名称(bcms:{ws}:all, (中文(简体) ), bcms:{ws}:entry:{slug}, (中文(简体) ), bcms:{ws}:page:{slug}),因此页面保持静态 createRevalidateRoute 刷新它。 当读取失败( 离线建筑) 时, 它会倒回 已发布的内容文件( e)BCMS_CONTENT_FILE),然后到你通过的快照。 客户端组件获得 同一对象通过 useBcmsPage(),因此服务器 HTML 和水合页面显示相同的副本.

CMS的图像(next.config(中文(简体) )

CMS 图像为远程 URL cdn.bettercms.ai,哪个 next/image 拒绝直到 images.remotePatterns 允许它。 环绕配置 :

// next.config.ts
import { withBetterCMS } from "@bettercms-ai/next/config";

export default withBetterCMS(nextConfig);

它增加了媒体主持人(和 NEXT_PUBLIC_BCMS_MEDIA_URL,当网站服务媒体从它的 之后的图案,一次, 并留下其余的配置是.

预览草稿

草稿只来自签名的预览端点,通过 getEntry数字 :

// app/api/draft/route.ts — validates the dashboard's preview token, enables Draft Mode,
// stores the token in a cookie, then redirects
import { createDraftModeRoute } from "@bettercms-ai/next";
export const { GET } = createDraftModeRoute({ apiUrl: "https://api.bettercms.ai" });
import { isDraftEnabled, getPreviewToken } from "@bettercms-ai/next";
const previewToken = (await isDraftEnabled()) ? await getPreviewToken() : null;
const entry = previewToken
  ? await cms.getEntry(slug, { preview: true, previewToken }) // uncached
  : await cms.getEntry(slug, { tags: [`bcms:acme:entry:${slug}`] });

getPage 已经 无选项草稿。它总是返回已公布的页面。 显示草稿 标记范围 getEntry 预览见上文。 对于视觉编辑器画布, 包裹 页面的块 <BcmsDraftBridge> (单位:千美元)@bettercms-ai/next/draft-bridge改为 <BcmsBlocks>编辑将块树草稿推向它 postMessage,所以页面 什么都拿不出来 打开 <BcmsDraftMarker /> 在“草稿模式”的根布局中.

静态导出( N)output: "export"(中文(简体) )

运行 bettercms-snapshot 在此之前 next build,并读取内容 getContent(config)数字 :

{ "scripts": { "build": "bettercms-snapshot && next build" } }

无 bcms-content.json,读取回 活 no-store 获取。 这条路就是这样 动态,以及 output: "export" 悄悄地放下它们,留下一空 out/。 。 。 . getContent 转弯 声入人心 SNAPSHOT_MISSING 构建错误。 设定 BETTERCMS_PREVIEW=1 {\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}读到生活 相反,预览构建.

llms.txt

// app/llms.txt/route.ts
import { llmsTxtRoute } from "@bettercms-ai/next";
export const GET = llmsTxtRoute({ models, title: "Acme", workspace: "acme" });

generateLlmsTxt(models, opts) (纯)和 fetchModels({ apiUrl, apiKey }) 也被导出. 作者控制的值被逃脱,所以内容不能伪造文档结构.

表单

在内容快照中填表飞船( E)bcms-content.json → forms,由建筑物写成 行动。 土生土长的 <BcmsForm> ——真正的输入,没有iframe,你自己的风格. 它处理有条件 showIf 字段、 URL- query 预填充、 蜂蜜壶、 可选 旋转部件、 校验错误和提交 。 没有 API 密钥( 端点是 转弯相接.

// app/contact/page.tsx — a Server Component reads the Action's snapshot...
import { getForm } from "@bettercms-ai/next";
import { ContactForm } from "./contact-form";

export default function ContactPage() {
  const form = getForm("Contact"); // by name or id, from bcms-content.json
  return form ? <ContactForm form={form} /> : null;
}
// ./contact-form.tsx — <BcmsForm> ships from its own "use client" entry.
import { BcmsForm } from "@bettercms-ai/next/form";
import type { DeliveryForm } from "@bettercms-ai/sdk";

export function ContactForm({ form }: { form: DeliveryForm }) {
  return <BcmsForm form={form} turnstileSiteKey={process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY} />;
}

不在反应上? 从任何地方提交框架不可知核心:

import { submitForm } from "@bettercms-ai/sdk";
await submitForm({ formId, data: { email: "[email protected]" }, turnstileToken });

仪表板的复制版嵌入片段仍然是非CMS站点的零积分倒计时.

页块( E)<BcmsBlocks>——表格放入页面构建器

当你在BetterCMS仪表板上建出一个页面并放下一个 窗体块 打开它 表格将它完全排入您放置的位置。 调整页面的块 <BcmsBlocks> 并将表格从 readForms() a 包括: form 块解析其 formId 对付他们,使他们 <BcmsForm> 给你的 其它区块类型 (标题、文字、图像、按钮、空间器、视频、列) 标记可以样式.

🔴 过 components={readComponents()} 我也一样. 以章节形式撰写的页面—— 仪表板的"添加一款"采取器和AI两种产品——都是列表 component 块. A 类 component 未交付定义的块 无,所以一个页面可以 建立绿色并返回时只显示您的版面导航和页脚。 两个读者都从 同样的 bcms-content.json 所以这需要多打一个电话.

// app/[slug]/page.tsx
import { createBetterCMS, readForms, readComponents } from "@bettercms-ai/next";
import { BcmsBlocks } from "@bettercms-ai/next/blocks"; // its own "use client" entry

const cms = createBetterCMS({ workspace: "my-workspace", apiKey: process.env.BETTERCMS_API_KEY! });

export default async function Page({ params }: { params: { slug: string } }) {
  // The page's blocks come from the delivery API (`entry.blocks`); forms from the snapshot.
  const page = await cms.getPage(params.slug);
  const { forms, turnstileSiteKey } = readForms();
  return (
    <BcmsBlocks
      blocks={page.blocks}
      forms={forms}
      // 🔴 NOT optional in practice. A page built out of SECTIONS is a list of `component`
      // blocks, and a `component` block with no matching definition renders NOTHING. Omit
      // this and such a page builds green, returns 200, and shows only your nav and footer.
      components={readComponents()}
      turnstileSiteKey={turnstileSiteKey ?? undefined}
    />
  );
}

CMS顺序中的章节( :<BcmsSections>(中文(简体) )

页面的章节被写在自己的文件中,使其按其CMS页面列表的顺序排列 他们。 服务器组件( 没有客户端 JavaScript), 包含自己的条目 :

import { sectionsFor } from "@bettercms-ai/next";
import { BcmsSections, BcmsSection } from "@bettercms-ai/next/sections";

export default function Home() {
  return (
    <main>
      <BcmsSections {...sectionsFor("home")}>
        <BcmsSection bind="hero">{(root) => <Hero rootProps={root} />}</BcmsSection>
        <BcmsSection bind="code:sec1"><LogoStrip /></BcmsSection>
        <BcmsSection bind="features"><section className="features">…</section></BcmsSection>
      </BcmsSections>
    </main>
  );
}
  • 一个被捆绑在一区块的区块 使它在区块的位置。 一个DOM元素得到 data-bcms-block, (中文(简体) ), data-bcms-style 和块的风格克隆到它。 一个组成部分的孩子是: 给定函数代替: 展开 root 在它制造的元素。 没有函数 a 组件被标记在一个框上: display:contents 街坊没有风格,有一个 display:flow-root 框中,如果样式已经存在,则随行(在弹性或网格母框中,区域是 (然后是一个项目).
  • 编辑器中添加的一节( 一个块,没有绑定)通过 <BcmsBlocks>。 。 。 .
  • 无块名称的一节 : code:<bind> 总是让你写的地方。 一个赤裸的 <bind> 已经 直到列表中指定页面的一栏;然后是已删除的一栏.
  • blocks 无效( 无快照 ) , 使每个孩子都处于源头顺序, 不受影响 .

页面路由器:页面也在浏览器中制作,所以列表必须是页面道具: export const getStaticProps = sectionsStaticProps("home"),则 <BcmsSections {...bcmsSections}> 在页中需要 { bcmsSections }。带有数据的页面 其功能通过: sectionsStaticProps("home", pageStaticProps) (或 sectionsServerSideProps) 调用并添加 bcmsSections 对道具 它回答. npx @bettercms-ai/convert --sections 写这个包裹给你.

带有国家代码拾取器的电话字段

作者启用的电话字段 国家代码拾取器 在建设者中使一个国家 控制在号码旁边 不管访客选什么 球场还是呈递 一个值, (中文(简体) ), e.164(在自己的密钥下)+14155550123)——没有第二把钥匙可以处理.

两个班是你的风格:

类元素
bcms-phone国家控制周围的包装和数字
bcms-phone-country国家控制自己

@bettercms-ai/next 格式化输入的数字。 主题及其电话元数据 (~80KB)生活在一个 单独块, 只有在表单有取取器时才装入 a 包括: 没有一艘飞船 直到那个块到达 田地是平地 tel 投入 里面一样 bcms-phone 包装,所以你的CSS应用前后.

SEO——每页 <head> 与 generateMetadata

getPage() 返回页面 metaTitle, (中文(简体) ), metaDescription有钱人 metaJson (OG / Twitter / canonical / JSON-LD)在仪表板的SEO面板上编辑. 让他们进入 路线 <head> 与 buildMetadata – 网页默认值的每页值层 (同一页面翻页优先 *.bettercms.site 渲染器用途:

// app/[slug]/page.tsx
import type { Metadata } from "next";
import { createBetterCMS, buildMetadata, resolveSeo, type SiteSeoDefaults } from "@bettercms-ai/next";

const cms = createBetterCMS({ workspace: "my-workspace", apiKey: process.env.BETTERCMS_API_KEY! });

// Your project-wide fallbacks (optional). Per-page meta always wins.
const siteDefaults: SiteSeoDefaults = {
  metaDescription: "Selected work, experience, and contact information.",
  ogImage: "https://example.com/og-default.png",
  twitterHandle: "@acme",
};

export async function generateMetadata(
  { params }: { params: { slug: string } },
): Promise<Metadata> {
  const page = await cms.getPage(params.slug);
  if (!page) return {};
  return buildMetadata(page, siteDefaults);
}

buildMetadata 覆盖 <title>、描述、神话、开放图和推特. JSON -LD不是下一个的一部分 Metadata,所以从页面组件中用 resolveSeo(...).jsonLd数字 :

export default async function Page({ params }: { params: { slug: string } }) {
  const page = await cms.getPage(params.slug);
  if (!page) return null;
  const { jsonLd } = resolveSeo(page, siteDefaults);
  return (
    <>
      {jsonLd.map((node, i) => (
        <script
          key={i}
          type="application/ld+json"
          dangerouslySetInnerHTML={{ __html: JSON.stringify(node) }}
        />
      ))}
      {/* …render page.blocks… */}
    </>
  );
}

在发布时保持 SSO 新鲜

阅读内容为:revalidate,默认为60s),所以在窗口后出现 SEO 编辑 e. 对于 即时 发布后刷新, 配置工程的 重新验证 网点 (项目 —— 设置) createRevalidateRoute 处理器,通过 网络隐藏为 secret标记您的读取 bcms:{workspace}:… 标签来源 (原始内容存档于2012-10-10). Caching & revalidation. cms.getPage(slug, { tags: [`bcms:acme:page:${slug}`] }) 这是 页面表单。 没有了网页浏览器,编辑仍然在传播,但是在IRS的间隔上而不是 马上.

许可证

UNLICENSED——内部BetterCMS包.

版本与发布记录

@bettercms-ai/next 共发布 61 个版本,首次发布于 2026/06/30;过去 12 个月发布了 61 个版本。

版本发布日期距今
0.39.0latest2026/11/052 天前
0.38.02026/11/026 天前
0.36.32026/10/2711 天前
0.36.12026/10/2613 天前
0.36.22026/10/2613 天前
0.36.02026/10/2613 天前

关于 @bettercms-ai/next 的常见问题

@bettercms-ai/next 是什么?

Next.js的BetterCMS适配器——已打入,缓存-意识内容已读取(getEntry/list Enteries),预览草稿,构建时间内容快照,以及App路由器的llms.txt生成. /块,/形式,/场,/搜索和/视频编辑条目为被框

如何安装 @bettercms-ai/next?

在项目目录运行 npm install @bettercms-ai/next,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 bettercms-snapshot,可以用 npx bettercms-snapshot 直接运行。

@bettercms-ai/next 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@bettercms-ai/next 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@bettercms-ai/next 可以商用吗?

@bettercms-ai/next 使用 未声明 许可。package.json 没有声明许可证。没有许可证的代码默认保留全部权利,商用前请联系作者或查看仓库中的 LICENSE 文件。以上为概括,不构成法律意见,请以许可原文为准。

@bettercms-ai/next 的下载量有多少?

根据 npm 官方下载统计,@bettercms-ai/next 在 2026/10/08 – 2026/11/06 这 30 天内被下载 5,106 次,日均约 170 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@bettercms-ai/next 还在维护吗?

最新版本 0.39.0 发布于 2026/11/05(2 天前);至今共发布 61 个版本,其中过去 12 个月发布了 61 个;首次发布于 2026/06/30。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。