tinacms
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTinaCMS
TinaCMS
Git-backed headless CMS with visual editing for content-heavy sites.
Last Updated: 2026-01-21
Versions: tinacms@3.3.1, @tinacms/cli@2.1.1
基于Git的无头CMS,为内容密集型网站提供可视化编辑功能。
最后更新时间:2026-01-21
版本:tinacms@3.3.1, @tinacms/cli@2.1.1
Quick Start
快速开始
Package Manager Recommendation:
- Recommended: pnpm (required for TinaCMS >2.7.3)
- Alternative: npm or yarn (may have module resolution issues in newer versions)
bash
undefined包管理器推荐:
- 推荐:pnpm(TinaCMS >2.7.3版本要求使用)
- 替代方案:npm或yarn(新版本中可能存在模块解析问题)
bash
undefinedInstall pnpm (if needed)
安装pnpm(如有需要)
npm install -g pnpm
npm install -g pnpm
Initialize TinaCMS
初始化TinaCMS
npx @tinacms/cli@latest init
npx @tinacms/cli@latest init
Install dependencies with pnpm
使用pnpm安装依赖
pnpm install
pnpm install
Update package.json scripts
更新package.json中的脚本
{
"dev": "tinacms dev -c "next dev"",
"build": "tinacms build && next build"
}
{
"dev": "tinacms dev -c "next dev"",
"build": "tinacms build && next build"
}
Set environment variables
设置环境变量
NEXT_PUBLIC_TINA_CLIENT_ID=your_client_id
TINA_TOKEN=your_read_only_token
NEXT_PUBLIC_TINA_CLIENT_ID=your_client_id
TINA_TOKEN=your_read_only_token
Start dev server
启动开发服务器
pnpm run dev
pnpm run dev
Access admin interface
访问管理界面
**Version Locking (Recommended):**
Pin exact versions to prevent breaking changes from automatic CLI/UI updates:
```json
{
"dependencies": {
"tinacms": "3.3.1", // NOT "^3.3.1"
"@tinacms/cli": "2.1.1"
}
}Why: TinaCMS UI assets are served from CDN and may update before your local CLI, causing incompatibilities.
Source: GitHub Issue #5838
**版本锁定(推荐):**
固定精确版本,防止CLI/UI自动更新导致的破坏性变更:
```json
{
"dependencies": {
"tinacms": "3.3.1", // 不要使用"^3.3.1"
"@tinacms/cli": "2.1.1"
}
}原因:TinaCMS UI资源从CDN加载,可能会先于本地CLI更新,导致兼容性问题。
Next.js Integration
Next.js集成
useTina Hook (enables visual editing):
tsx
import { useTina } from 'tinacms/dist/react'
import { client } from '../../tina/__generated__/client'
export default function BlogPost(props) {
const { data } = useTina({
query: props.query,
variables: props.variables,
data: props.data
})
return <article><h1>{data.post.title}</h1></article>
}
export async function getStaticProps({ params }) {
const response = await client.queries.post({
relativePath: `${params.slug}.md`
})
return {
props: {
data: response.data,
query: response.query,
variables: response.variables
}
}
}App Router: Admin route at
Pages Router: Admin route at
app/admin/[[...index]]/page.tsxpages/admin/[[...index]].tsxuseTina Hook(启用可视化编辑):
tsx
import { useTina } from 'tinacms/dist/react'
import { client } from '../../tina/__generated__/client'
export default function BlogPost(props) {
const { data } = useTina({
query: props.query,
variables: props.variables,
data: props.data
})
return <article><h1>{data.post.title}</h1></article>
}
export async function getStaticProps({ params }) {
const response = await client.queries.post({
relativePath: `${params.slug}.md`
})
return {
props: {
data: response.data,
query: response.query,
variables: response.variables
}
}
}App Router:管理路由位于
Pages Router:管理路由位于
app/admin/[[...index]]/page.tsxpages/admin/[[...index]].tsxSchema Configuration
Schema配置
tina/config.ts structure:
typescript
import { defineConfig } from 'tinacms'
export default defineConfig({
branch: process.env.GITHUB_BRANCH || 'main',
clientId: process.env.NEXT_PUBLIC_TINA_CLIENT_ID,
token: process.env.TINA_TOKEN,
build: {
outputFolder: 'admin',
publicFolder: 'public',
},
schema: {
collections: [/* ... */],
},
})Collection Example (Blog Post):
typescript
{
name: 'post', // Alphanumeric + underscores only
label: 'Blog Posts',
path: 'content/posts', // No trailing slash
format: 'mdx',
fields: [
{
type: 'string',
name: 'title',
label: 'Title',
isTitle: true,
required: true
},
{
type: 'rich-text',
name: 'body',
label: 'Body',
isBody: true
}
]
}Field Types: , , , , , , ,
stringrich-textnumberdatetimebooleanimagereferenceobjectReference Field Note: When a reference field references multiple collection types with shared field names, ensure the field types match. Conflicting types (e.g., vs ) cause GraphQL schema errors.
bio: stringbio: rich-texttypescript
// Example: Reference field referencing multiple collections
{
type: 'reference',
name: 'contributor',
collections: ['author', 'editor'] // Ensure shared fields have same type
}Source: Community-sourced
tina/config.ts结构:
typescript
import { defineConfig } from 'tinacms'
export default defineConfig({
branch: process.env.GITHUB_BRANCH || 'main',
clientId: process.env.NEXT_PUBLIC_TINA_CLIENT_ID,
token: process.env.TINA_TOKEN,
build: {
outputFolder: 'admin',
publicFolder: 'public',
},
schema: {
collections: [/* ... */],
},
})集合示例(博客文章):
typescript
{
name: 'post', // 仅允许字母、数字和下划线
label: 'Blog Posts',
path: 'content/posts', // 不要加末尾斜杠
format: 'mdx',
fields: [
{
type: 'string',
name: 'title',
label: 'Title',
isTitle: true,
required: true
},
{
type: 'rich-text',
name: 'body',
label: 'Body',
isBody: true
}
]
}字段类型:, , , , , , ,
stringrich-textnumberdatetimebooleanimagereferenceobject引用字段注意事项:当引用字段引用多个具有共享字段名的集合类型时,确保字段类型一致。类型冲突(如 vs )会导致GraphQL schema错误。
bio: stringbio: rich-texttypescript
// 示例:引用多个集合的引用字段
{
type: 'reference',
name: 'contributor',
collections: ['author', 'editor'] // 确保共享字段类型一致
}来源:社区整理
Common Errors & Solutions
常见错误与解决方案
1. ❌ ESbuild Compilation Errors
1. ❌ ESbuild编译错误
Error Message:
ERROR: Schema Not Successfully Built
ERROR: Config Not Successfully ExecutedCauses:
- Importing code with custom loaders (webpack, babel plugins, esbuild loaders)
- Importing frontend-only code (uses , DOM APIs, React hooks)
window - Importing entire component libraries instead of specific modules
Solution:
Import only what you need:
typescript
// ❌ Bad - Imports entire component directory
import { HeroComponent } from '../components/'
// ✅ Good - Import specific file
import { HeroComponent } from '../components/blocks/hero'Prevention Tips:
- Keep imports minimal
tina/config.ts - Only import type definitions and simple utilities
- Avoid importing UI components directly
- Create separate files if needed
.schema.ts
Reference: See
references/common-errors.md#esbuild错误信息:
ERROR: Schema Not Successfully Built
ERROR: Config Not Successfully Executed原因:
- 导入了使用自定义加载器的代码(webpack、babel插件、esbuild加载器)
- 导入了仅前端可用的代码(使用、DOM API、React钩子)
window - 导入了整个组件库而非特定模块
解决方案:
仅导入所需内容:
typescript
// ❌ 错误示例 - 导入整个组件目录
import { HeroComponent } from '../components/'
// ✅ 正确示例 - 导入特定文件
import { HeroComponent } from '../components/blocks/hero'预防技巧:
- 尽量减少中的导入
tina/config.ts - 仅导入类型定义和简单工具类
- 避免直接导入UI组件
- 如有需要,创建单独的文件
.schema.ts
参考:查看
references/common-errors.md#esbuild2. ❌ Module Resolution: "Could not resolve 'tinacms'"
2. ❌ 模块解析错误:"Could not resolve 'tinacms'"
Error Message:
Error: Could not resolve "tinacms"Causes:
- Corrupted or incomplete installation
- Version mismatch between dependencies
- Missing peer dependencies
Solution:
bash
undefined错误信息:
Error: Could not resolve "tinacms"原因:
- 安装文件损坏或不完整
- 依赖版本不匹配
- 缺少对等依赖
解决方案:
bash
undefinedClear cache and reinstall
清除缓存并重新安装
rm -rf node_modules package-lock.json
npm install
rm -rf node_modules package-lock.json
npm install
Or with pnpm
或使用pnpm
rm -rf node_modules pnpm-lock.yaml
pnpm install
rm -rf node_modules pnpm-lock.yaml
pnpm install
Or with yarn
或使用yarn
rm -rf node_modules yarn.lock
yarn install
**Prevention:**
- Use lockfiles (`package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`)
- Don't use `--no-optional` or `--omit=optional` flags
- Ensure `react` and `react-dom` are installed (even for non-React frameworks)
---rm -rf node_modules yarn.lock
yarn install
**预防措施:**
- 使用锁文件(`package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`)
- 不要使用`--no-optional`或`--omit=optional`参数
- 确保已安装`react`和`react-dom`(即使是非React框架)
---3. ❌ Field Naming Constraints
3. ❌ 字段命名限制
Error Message:
Field name contains invalid charactersCause:
- TinaCMS field names can only contain: letters, numbers, underscores
- Hyphens, spaces, special characters are NOT allowed
Solution:
typescript
// ❌ Bad - Uses hyphens
{
name: 'hero-image',
label: 'Hero Image',
type: 'image'
}
// ❌ Bad - Uses spaces
{
name: 'hero image',
label: 'Hero Image',
type: 'image'
}
// ✅ Good - Uses underscores
{
name: 'hero_image',
label: 'Hero Image',
type: 'image'
}
// ✅ Good - CamelCase also works
{
name: 'heroImage',
label: 'Hero Image',
type: 'image'
}Note: This is a breaking change from Forestry.io migration
错误信息:
Field name contains invalid characters原因:
- TinaCMS字段名称仅允许包含:字母、数字、下划线
- 不允许使用连字符、空格、特殊字符
解决方案:
typescript
// ❌ 错误示例 - 使用连字符
{
name: 'hero-image',
label: 'Hero Image',
type: 'image'
}
// ❌ 错误示例 - 使用空格
{
name: 'hero image',
label: 'Hero Image',
type: 'image'
}
// ✅ 正确示例 - 使用下划线
{
name: 'hero_image',
label: 'Hero Image',
type: 'image'
}
// ✅ 正确示例 - 使用驼峰命名
{
name: 'heroImage',
label: 'Hero Image',
type: 'image'
}注意:这是从Forestry.io迁移时的一个破坏性变更
4. ❌ Docker Binding Issues
4. ❌ Docker绑定问题
Error:
- TinaCMS admin not accessible from outside Docker container
Cause:
- TinaCMS binds to (localhost only) by default
127.0.0.1 - Docker containers need binding to accept external connections
0.0.0.0
Solution:
bash
undefined错误:
- TinaCMS管理界面无法从Docker容器外部访问
原因:
- TinaCMS默认绑定到(仅本地访问)
127.0.0.1 - Docker容器需要绑定到以接受外部连接
0.0.0.0
解决方案:
bash
// 确保框架开发服务器监听所有接口
tinacms dev -c "next dev --hostname 0.0.0.0"
tinacms dev -c "vite --host 0.0.0.0"
tinacms dev -c "astro dev --host 0.0.0.0"Docker Compose示例:
yaml
services:
app:
build: .
ports:
- "3000:3000"
command: npm run dev // 对应执行:tinacms dev -c "next dev --hostname 0.0.0.0"Ensure framework dev server listens on all interfaces
5. ❌ 缺少_template
键错误
_templatetinacms dev -c "next dev --hostname 0.0.0.0"
tinacms dev -c "vite --host 0.0.0.0"
tinacms dev -c "astro dev --host 0.0.0.0"
**Docker Compose Example:**
```yaml
services:
app:
build: .
ports:
- "3000:3000"
command: npm run dev # Which runs: tinacms dev -c "next dev --hostname 0.0.0.0"错误信息:
GetCollection failed: Unable to fetch
template name was not provided原因:
- 集合使用了数组(多schema)
templates - 文档的frontmatter中缺少字段
_template - 从迁移到
templates但未更新文档fields
解决方案:
选项1:使用替代(单模板推荐)
fieldstypescript
{
name: 'post',
path: 'content/posts',
fields: [/* ... */] // 不需要_template
}选项2:确保frontmatter中存在
_templateyaml
---
_template: article // ← 使用templates数组时必填
title: My Post
---迁移脚本(从templates转换为fields时使用):
bash
undefined5. ❌ Missing _template
Key Error
_template移除content/posts/所有文件中的_template字段
Error Message:
GetCollection failed: Unable to fetch
template name was not providedCause:
- Collection uses array (multiple schemas)
templates - Document missing field in frontmatter
_template - Migrating from to
templatesand documents not updatedfields
Solution:
Option 1: Use instead (recommended for single template)
fieldstypescript
{
name: 'post',
path: 'content/posts',
fields: [/* ... */] // No _template needed
}Option 2: Ensure exists in frontmatter
_templateyaml
---
_template: article # ← Required when using templates array
title: My Post
---Migration Script (if converting from templates to fields):
bash
undefinedfind content/posts -name "*.md" -exec sed -i '/_template:/d' {} +
---Remove _template from all files in content/posts/
6. ❌ 路径不匹配问题
find content/posts -name "*.md" -exec sed -i '/_template:/d' {} +
---错误:
- 文件未显示在Tina管理界面中
- 保存时出现“文件未找到”错误
- GraphQL查询返回空结果
原因:
- 集合配置中的与实际文件目录不匹配
path - 相对路径与绝对路径混淆
- 末尾斜杠问题
解决方案:
typescript
// 文件实际位置:content/posts/hello.md
// ✅ 正确配置
{
name: 'post',
path: 'content/posts', // 与文件位置匹配
fields: [/* ... */]
}
// ❌ 错误配置 - 缺少'content/'
{
name: 'post',
path: 'posts', // 无法找到文件
fields: [/* ... */]
}
// ❌ 错误配置 - 末尾有斜杠
{
name: 'post',
path: 'content/posts/', // 可能导致问题
fields: [/* ... */]
}调试方法:
- 运行检查路径
npx @tinacms/cli@latest audit - 验证文件是否存在于指定目录
- 检查文件扩展名是否与字段匹配
format
6. ❌ Path Mismatch Issues
7. ❌ 构建脚本顺序问题
Error:
- Files not appearing in Tina admin
- "File not found" errors when saving
- GraphQL queries return empty results
Cause:
- in collection config doesn't match actual file directory
path - Relative vs absolute path confusion
- Trailing slash issues
Solution:
typescript
// Files located at: content/posts/hello.md
// ✅ Correct
{
name: 'post',
path: 'content/posts', // Matches file location
fields: [/* ... */]
}
// ❌ Wrong - Missing 'content/'
{
name: 'post',
path: 'posts', // Files won't be found
fields: [/* ... */]
}
// ❌ Wrong - Trailing slash
{
name: 'post',
path: 'content/posts/', // May cause issues
fields: [/* ... */]
}Debugging:
- Run to check paths
npx @tinacms/cli@latest audit - Verify files exist in specified directory
- Check file extensions match field
format
错误信息:
ERROR: Cannot find module '../tina/__generated__/client'
ERROR: Property 'queries' does not exist on type '{}'原因:
- 框架构建在之前执行
tinacms build - Tina类型在TypeScript编译前未生成
- CI/CD流水线顺序错误
解决方案:
json
{
"scripts": {
"build": "tinacms build && next build" // ✅ 先执行Tina构建
// 错误示例:"build": "next build && tinacms build" // ❌ 顺序错误
}
}CI/CD示例(GitHub Actions):
yaml
- name: Build
run: |
npx @tinacms/cli@latest build // 先生成类型
npm run build // 再构建框架重要性:
- 会在
tinacms build目录生成TypeScript类型tina/__generated__/ - 框架构建需要这些类型才能成功编译
- 顺序错误会导致类型错误
7. ❌ Build Script Ordering Problems
8. ❌ TinaCMS资源加载失败
Error Message:
ERROR: Cannot find module '../tina/__generated__/client'
ERROR: Property 'queries' does not exist on type '{}'Cause:
- Framework build running before
tinacms build - Tina types not generated before TypeScript compilation
- CI/CD pipeline incorrect order
Solution:
json
{
"scripts": {
"build": "tinacms build && next build" // ✅ Tina FIRST
// NOT: "build": "next build && tinacms build" // ❌ Wrong order
}
}CI/CD Example (GitHub Actions):
yaml
- name: Build
run: |
npx @tinacms/cli@latest build # Generate types first
npm run build # Then build frameworkWhy This Matters:
- generates TypeScript types in
tinacms buildtina/__generated__/ - Framework build needs these types to compile successfully
- Running in wrong order causes type errors
错误信息:
Failed to load resource: net::ERR_CONNECTION_REFUSED
http://localhost:4001/...原因:
- 将开发环境的推送到生产环境(从localhost加载资源)
admin/index.html - 网站部署在子目录但未配置
basePath
解决方案:
生产部署注意:
json
{
"scripts": {
"build": "tinacms build && next build" // ✅ 始终使用build命令
// 错误示例:"build": "tinacms dev" // ❌ 生产环境绝不能用dev
}
}子目录部署注意:
⚠️ 子路径部署限制:TinaCMS部署到子路径时(如而非example.com/cms/admin),资源加载存在已知问题。即使配置了example.com/admin也可能出现问题。basePath解决方法:将TinaCMS管理界面部署在根路径(),或使用反向代理重写规则。/admin来源:社区整理
typescript
// tina/config.ts
export default defineConfig({
build: {
outputFolder: 'admin',
publicFolder: 'public',
basePath: 'your-subdirectory' // ← 子路径下可能存在资源加载问题
}
})CI/CD修复:
yaml
undefined8. ❌ Failed Loading TinaCMS Assets
GitHub Actions / Vercel / Netlify
Error Message:
Failed to load resource: net::ERR_CONNECTION_REFUSED
http://localhost:4001/...Causes:
- Pushed development to production (loads assets from localhost)
admin/index.html - Site served on subdirectory but not configured
basePath
Solution:
For Production Deploys:
json
{
"scripts": {
"build": "tinacms build && next build" // ✅ Always build
// NOT: "build": "tinacms dev" // ❌ Never dev in production
}
}For Subdirectory Deployments:
⚠️ Sub-path Deployment Limitation: TinaCMS has known issues loading assets correctly when deployed to a sub-path (e.g.,instead ofexample.com/cms/admin). This is a limitation even withexample.com/adminconfiguration.basePathWorkaround: Deploy TinaCMS admin at root path () or use reverse proxy rewrite rules./adminSource: Community-sourced
typescript
// tina/config.ts
export default defineConfig({
build: {
outputFolder: 'admin',
publicFolder: 'public',
basePath: 'your-subdirectory' // ← May have asset loading issues on sub-paths
}
})CI/CD Fix:
yaml
undefined- run: npx @tinacms/cli@latest build // 始终使用build,不要用dev
---GitHub Actions / Vercel / Netlify
9. ❌ 引用字段503服务不可用
- run: npx @tinacms/cli@latest build # Always use build, not dev
---错误:
- 引用字段下拉框超时并显示503错误
- 加载引用字段时管理界面无响应
原因:
- 被引用的集合中条目过多(数百或数千条)
- 引用字段目前不支持分页
解决方案:
选项1:拆分集合
typescript
// 不要使用一个庞大的"authors"集合
// 按活跃状态或字母顺序拆分
{
name: 'active_author',
label: 'Active Authors',
path: 'content/authors/active',
fields: [/* ... */]
}
{
name: 'archived_author',
label: 'Archived Authors',
path: 'content/authors/archived',
fields: [/* ... */]
}选项2:使用带验证的字符串字段
typescript
// 替代引用字段
{
type: 'string',
name: 'authorId',
label: 'Author ID',
ui: {
component: 'select',
options: ['author-1', 'author-2', 'author-3'] // 精选列表
}
}选项3:自定义字段组件(高级)
- 在自定义组件中实现分页
- 查看TinaCMS文档:https://tina.io/docs/extending-tina/custom-field-components/
9. ❌ Reference Field 503 Service Unavailable
10. ❌ 媒体管理器上传超时(幽灵上传)
Error:
- Reference field dropdown times out with 503 error
- Admin interface becomes unresponsive when loading reference field
Cause:
- Too many items in referenced collection (100s or 1000s)
- No pagination support for reference fields currently
Solutions:
Option 1: Split collections
typescript
// Instead of one huge "authors" collection
// Split by active status or alphabetically
{
name: 'active_author',
label: 'Active Authors',
path: 'content/authors/active',
fields: [/* ... */]
}
{
name: 'archived_author',
label: 'Archived Authors',
path: 'content/authors/archived',
fields: [/* ... */]
}Option 2: Use string field with validation
typescript
// Instead of reference
{
type: 'string',
name: 'authorId',
label: 'Author ID',
ui: {
component: 'select',
options: ['author-1', 'author-2', 'author-3'] // Curated list
}
}Option 3: Custom field component (advanced)
- Implement pagination in custom component
- See TinaCMS docs: https://tina.io/docs/extending-tina/custom-field-components/
错误信息:
Upload failed
Error uploading image原因:
- 媒体管理器显示错误,但图片已在后台成功上传
- UI超时未反映实际上传状态
- 删除操作也会出现类似问题(显示错误但删除已成功)
解决方案:
如果上传显示错误:
- 等待5-10秒
- 关闭并重新打开媒体管理器
- 重试前检查图片是否已上传
- 避免重复上传
状态:已知问题(高优先级)
来源:GitHub Issue #6325
10. ❌ Media Manager Upload Timeouts (Ghost Uploads)
部署选项
—
TinaCloud(托管服务)- 推荐
Error Message:
Upload failed
Error uploading imageCause:
- Media Manager shows error but image uploads successfully in background
- UI timeout doesn't reflect actual upload status
- Similar issue occurs with deletion (error shown but deletion succeeds)
Solution:
If upload shows error:
- Wait 5-10 seconds
- Close and reopen Media Manager
- Check if image already uploaded before retrying
- Avoid duplicate upload attempts
Status: Known issue (high priority)
Source: GitHub Issue #6325
设置步骤:
- 在https://app.tina.io注册账号
- 获取Client ID和只读Token
- 设置环境变量:,
NEXT_PUBLIC_TINA_CLIENT_IDTINA_TOKEN - 部署到Vercel/Netlify/Cloudflare Pages
优点:零配置,免费额度(每月10000次请求)
Deployment Options
Node.js自托管
TinaCloud (Managed) - Recommended
—
Setup:
- Sign up at https://app.tina.io
- Get Client ID and Read Only Token
- Set env vars: ,
NEXT_PUBLIC_TINA_CLIENT_IDTINA_TOKEN - Deploy to Vercel/Netlify/Cloudflare Pages
Pros: Zero config, free tier (10k requests/month)
⚠️ 边缘运行时限制:自托管TinaCMS无法在边缘运行时环境(Cloudflare Workers、Vercel Edge Functions)中使用,因为和@tinacms/datalayer依赖Node.js API。边缘部署请使用TinaCloud(托管服务)。@tinacms/graphql来源:GitHub Issue #4363(标记为"wontfix")
⚠️ 自托管示例可能过时:TinaCMS仓库中的官方自托管示例已被团队确认“相当过时”。请始终参考最新文档,不要仅依赖示例仓库。
仅适用于Node.js环境(非边缘运行时):
bash
pnpm install @tinacms/datalayer tinacms-authjs
npx @tinacms/cli@latest init backend示例(Node.js服务器,非Workers):
typescript
import { TinaNodeBackend, LocalBackendAuthProvider } from '@tinacms/datalayer'
import { AuthJsBackendAuthProvider, TinaAuthJSOptions } from 'tinacms-authjs'
import databaseClient from '../../tina/__generated__/databaseClient'
const isLocal = process.env.TINA_PUBLIC_IS_LOCAL === 'true'
// 仅在Node.js运行时可用,边缘运行时不支持
const handler = TinaNodeBackend({
authProvider: isLocal
? LocalBackendAuthProvider()
: AuthJsBackendAuthProvider({
authOptions: TinaAuthJSOptions({
databaseClient,
secret: process.env.NEXTAUTH_SECRET,
}),
}),
databaseClient,
})优点:完全可控,自托管
缺点:需要Node.js运行时(无法使用边缘计算)
Self-Hosted on Node.js
—
⚠️ Edge Runtime Limitation: Self-hosted TinaCMS does NOT work in Edge Runtime environments (Cloudflare Workers, Vercel Edge Functions) due to Node.js dependencies inand@tinacms/datalayer. Use TinaCloud (managed service) for edge deployments.@tinacms/graphqlSource: GitHub Issue #4363 (labeled "wontfix")
⚠️ Self-Hosted Examples May Be Outdated: Official self-hosted examples in the TinaCMS repository are acknowledged by the team as "quite out of date". Always cross-reference with latest documentation instead of relying solely on example repos.Source: GitHub Issue #6365
For Node.js environments only (not edge runtime):
bash
pnpm install @tinacms/datalayer tinacms-authjs
npx @tinacms/cli@latest init backendExample (Node.js server, not Workers):
typescript
import { TinaNodeBackend, LocalBackendAuthProvider } from '@tinacms/datalayer'
import { AuthJsBackendAuthProvider, TinaAuthJSOptions } from 'tinacms-authjs'
import databaseClient from '../../tina/__generated__/databaseClient'
const isLocal = process.env.TINA_PUBLIC_IS_LOCAL === 'true'
// This ONLY works in Node.js runtime, NOT edge runtime
const handler = TinaNodeBackend({
authProvider: isLocal
? LocalBackendAuthProvider()
: AuthJsBackendAuthProvider({
authOptions: TinaAuthJSOptions({
databaseClient,
secret: process.env.NEXTAUTH_SECRET,
}),
}),
databaseClient,
})Pros: Full control, self-hosted
Cons: Requires Node.js runtime (cannot use edge computing)
—