tinacms
TinaCMSは、Gitで管理されたコンテンツを使い、Next.jsと連携して直感的なビジュアル編集環境を構築できるオープンソースCMSで、開発者がコンテンツスキーマを定義し、編集者がリアルタイムで変更を確認できる環境を構築するSkill。
📜 元の英語説明(参考)
Expert guidance for TinaCMS, the open-source headless CMS that stores content in Git (Markdown/MDX/JSON) and provides visual editing capabilities. Helps developers set up TinaCMS with Next.js, define content schemas, and build visual editing experiences where editors can see changes in real time.
🇯🇵 日本人クリエイター向け解説
TinaCMSは、Gitで管理されたコンテンツを使い、Next.jsと連携して直感的なビジュアル編集環境を構築できるオープンソースCMSで、開発者がコンテンツスキーマを定義し、編集者がリアルタイムで変更を確認できる環境を構築するSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o tinacms.zip https://jpskill.com/download/15482.zip && unzip -o tinacms.zip && rm tinacms.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/15482.zip -OutFile "$d\tinacms.zip"; Expand-Archive "$d\tinacms.zip" -DestinationPath $d -Force; ri "$d\tinacms.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
tinacms.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
tinacmsフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
TinaCMS — GitバックのビジュアルCMS
概要
TinaCMSは、コンテンツをGit(Markdown/MDX/JSON)に保存し、ビジュアル編集機能を提供するオープンソースのヘッドレスCMSです。開発者がNext.jsでTinaCMSをセットアップし、コンテンツスキーマを定義し、エディターが変更をリアルタイムで確認できるビジュアル編集エクスペリエンスを構築するのに役立ちます。
手順
スキーマ定義
型安全なスキーマでコンテンツ構造を定義します。
// tina/config.ts — コンテンツスキーマを使用したTinaCMS構成
import { defineConfig } from "tinacms";
export default defineConfig({
branch: process.env.NEXT_PUBLIC_TINA_BRANCH || "main",
clientId: process.env.NEXT_PUBLIC_TINA_CLIENT_ID!,
token: process.env.TINA_TOKEN!,
build: {
outputFolder: "admin", // /adminで提供される管理UI
publicFolder: "public",
},
media: {
tina: {
mediaRoot: "uploads", // アップロードされた画像を/public/uploadsに保存
publicFolder: "public",
},
},
schema: {
collections: [
{
name: "post",
label: "ブログ記事",
path: "content/posts", // このディレクトリにMarkdownファイルとして保存
format: "mdx", // mdx | md | json
fields: [
{
type: "string",
name: "title",
label: "タイトル",
isTitle: true, // CMSでの表示名として使用
required: true,
},
{
type: "string",
name: "description",
label: "説明",
ui: {
component: "textarea", // 複数行のテキスト入力
},
},
{
type: "datetime",
name: "publishedAt",
label: "公開日",
required: true,
},
{
type: "image",
name: "heroImage",
label: "ヒーロー画像",
},
{
type: "string",
name: "category",
label: "カテゴリー",
options: ["engineering", "product", "culture", "tutorial"],
},
{
type: "object",
name: "author",
label: "著者",
fields: [
{ type: "string", name: "name", label: "名前", required: true },
{ type: "image", name: "avatar", label: "アバター" },
{ type: "string", name: "role", label: "役割" },
],
},
{
type: "rich-text",
name: "body",
label: "コンテンツ",
isBody: true, // MDXの本文コンテンツにマッピング
templates: [
// ビジュアルエディターで利用可能なカスタムMDXコンポーネント
{
name: "Callout",
label: "吹き出しボックス",
fields: [
{
type: "string",
name: "type",
label: "種類",
options: ["info", "warning", "tip", "danger"],
},
{
type: "rich-text",
name: "children",
label: "コンテンツ",
},
],
},
{
name: "CodeBlock",
label: "コードブロック",
fields: [
{ type: "string", name: "language", label: "言語" },
{ type: "string", name: "code", label: "コード", ui: { component: "textarea" } },
],
},
],
},
],
},
// ページコレクション — マーケティングページ、ランディングページ用
{
name: "page",
label: "ページ",
path: "content/pages",
format: "mdx",
fields: [
{ type: "string", name: "title", label: "タイトル", isTitle: true, required: true },
{
type: "object",
name: "seo",
label: "SEO",
fields: [
{ type: "string", name: "metaTitle", label: "メタタイトル" },
{ type: "string", name: "metaDescription", label: "メタディスクリプション" },
{ type: "image", name: "ogImage", label: "OG画像" },
],
},
{
type: "object",
name: "blocks",
label: "ページブロック",
list: true, // 繰り返し可能なブロック — ビジュアルページビルダー
templates: [
{
name: "hero",
label: "ヒーローセクション",
fields: [
{ type: "string", name: "heading", label: "見出し" },
{ type: "string", name: "subheading", label: "サブ見出し" },
{ type: "image", name: "backgroundImage", label: "背景" },
{ type: "string", name: "ctaText", label: "CTAボタンテキスト" },
{ type: "string", name: "ctaLink", label: "CTAリンク" },
],
},
{
name: "features",
label: "機能グリッド",
fields: [
{ type: "string", name: "heading", label: "セクション見出し" },
{
type: "object",
name: "items",
label: "機能アイテム",
list: true,
fields: [
{ type: "string", name: "title", label: "タイトル" },
{ type: "string", name: "description", label: "説明" },
{ type: "image", name: "icon", label: "アイコン" },
],
},
],
},
],
},
],
},
],
},
});
Next.jsでのビジュアル編集
ライブビジュアル編集でコンテンツをレンダリングします。
// app/posts/[slug]/page.tsx — ビジュアル編集機能付きブログ記事ページ
import { client } from "@/tina/__generated__/clien
(原文はここで切り詰められています) 📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
TinaCMS — Git-Backed Visual CMS
Overview
TinaCMS, the open-source headless CMS that stores content in Git (Markdown/MDX/JSON) and provides visual editing capabilities. Helps developers set up TinaCMS with Next.js, define content schemas, and build visual editing experiences where editors can see changes in real time.
Instructions
Schema Definition
Define your content structure in a type-safe schema:
// tina/config.ts — TinaCMS configuration with content schemas
import { defineConfig } from "tinacms";
export default defineConfig({
branch: process.env.NEXT_PUBLIC_TINA_BRANCH || "main",
clientId: process.env.NEXT_PUBLIC_TINA_CLIENT_ID!,
token: process.env.TINA_TOKEN!,
build: {
outputFolder: "admin", // Admin UI served at /admin
publicFolder: "public",
},
media: {
tina: {
mediaRoot: "uploads", // Store uploaded images in /public/uploads
publicFolder: "public",
},
},
schema: {
collections: [
{
name: "post",
label: "Blog Posts",
path: "content/posts", // Store as Markdown files in this directory
format: "mdx", // mdx | md | json
fields: [
{
type: "string",
name: "title",
label: "Title",
isTitle: true, // Used as the display name in the CMS
required: true,
},
{
type: "string",
name: "description",
label: "Description",
ui: {
component: "textarea", // Multi-line text input
},
},
{
type: "datetime",
name: "publishedAt",
label: "Published Date",
required: true,
},
{
type: "image",
name: "heroImage",
label: "Hero Image",
},
{
type: "string",
name: "category",
label: "Category",
options: ["engineering", "product", "culture", "tutorial"],
},
{
type: "object",
name: "author",
label: "Author",
fields: [
{ type: "string", name: "name", label: "Name", required: true },
{ type: "image", name: "avatar", label: "Avatar" },
{ type: "string", name: "role", label: "Role" },
],
},
{
type: "rich-text",
name: "body",
label: "Content",
isBody: true, // Maps to the MDX body content
templates: [
// Custom MDX components available in the visual editor
{
name: "Callout",
label: "Callout Box",
fields: [
{
type: "string",
name: "type",
label: "Type",
options: ["info", "warning", "tip", "danger"],
},
{
type: "rich-text",
name: "children",
label: "Content",
},
],
},
{
name: "CodeBlock",
label: "Code Block",
fields: [
{ type: "string", name: "language", label: "Language" },
{ type: "string", name: "code", label: "Code", ui: { component: "textarea" } },
],
},
],
},
],
},
// Page collection — for marketing pages, landing pages
{
name: "page",
label: "Pages",
path: "content/pages",
format: "mdx",
fields: [
{ type: "string", name: "title", label: "Title", isTitle: true, required: true },
{
type: "object",
name: "seo",
label: "SEO",
fields: [
{ type: "string", name: "metaTitle", label: "Meta Title" },
{ type: "string", name: "metaDescription", label: "Meta Description" },
{ type: "image", name: "ogImage", label: "OG Image" },
],
},
{
type: "object",
name: "blocks",
label: "Page Blocks",
list: true, // Repeatable blocks — visual page builder
templates: [
{
name: "hero",
label: "Hero Section",
fields: [
{ type: "string", name: "heading", label: "Heading" },
{ type: "string", name: "subheading", label: "Subheading" },
{ type: "image", name: "backgroundImage", label: "Background" },
{ type: "string", name: "ctaText", label: "CTA Button Text" },
{ type: "string", name: "ctaLink", label: "CTA Link" },
],
},
{
name: "features",
label: "Features Grid",
fields: [
{ type: "string", name: "heading", label: "Section Heading" },
{
type: "object",
name: "items",
label: "Feature Items",
list: true,
fields: [
{ type: "string", name: "title", label: "Title" },
{ type: "string", name: "description", label: "Description" },
{ type: "image", name: "icon", label: "Icon" },
],
},
],
},
],
},
],
},
],
},
});
Visual Editing in Next.js
Render content with live visual editing:
// app/posts/[slug]/page.tsx — Blog post page with visual editing
import { client } from "@/tina/__generated__/client";
import { useTina } from "tinacms/dist/react";
import { TinaMarkdown } from "tinacms/dist/rich-text";
// Components for custom MDX blocks
const components = {
Callout: ({ type, children }: any) => (
<div className={`callout callout-${type}`}>
{type === "tip" && "💡"}
{type === "warning" && "⚠️"}
{type === "danger" && "🚨"}
{type === "info" && "ℹ️"}
<TinaMarkdown content={children} />
</div>
),
CodeBlock: ({ language, code }: any) => (
<pre><code className={`language-${language}`}>{code}</code></pre>
),
};
// Server component: fetch data at build/request time
export default async function PostPage({ params }: { params: { slug: string } }) {
const { data, query, variables } = await client.queries.post({
relativePath: `${params.slug}.mdx`,
});
return <PostClient data={data} query={query} variables={variables} />;
}
// Client component: enables visual editing when in Tina admin
function PostClient({ data, query, variables }: any) {
// useTina enables real-time editing — changes appear instantly
const { data: tinaData } = useTina({ query, variables, data });
const post = tinaData.post;
return (
<article>
{post.heroImage && <img src={post.heroImage} alt={post.title} />}
<h1>{post.title}</h1>
<p>{post.description}</p>
<div className="author">
{post.author?.avatar && <img src={post.author.avatar} alt="" />}
<span>{post.author?.name}</span>
<time>{new Date(post.publishedAt).toLocaleDateString()}</time>
</div>
{/* TinaMarkdown renders rich-text with custom components */}
<TinaMarkdown content={post.body} components={components} />
</article>
);
}
// Generate static paths for all posts
export async function generateStaticParams() {
const posts = await client.queries.postConnection();
return posts.data.postConnection.edges?.map((edge) => ({
slug: edge?.node?._sys.filename,
})) ?? [];
}
Querying Content
Use the auto-generated GraphQL client:
// src/lib/content.ts — Query content using Tina's generated client
import { client } from "@/tina/__generated__/client";
// Get all blog posts (sorted by date)
async function getAllPosts() {
const response = await client.queries.postConnection({
sort: "publishedAt",
last: 100, // Get latest 100 posts
});
return response.data.postConnection.edges?.map((edge) => ({
slug: edge?.node?._sys.filename,
title: edge?.node?.title,
description: edge?.node?.description,
publishedAt: edge?.node?.publishedAt,
category: edge?.node?.category,
author: edge?.node?.author,
})) ?? [];
}
// Get posts filtered by category
async function getPostsByCategory(category: string) {
const response = await client.queries.postConnection({
filter: {
category: { eq: category },
},
});
return response.data.postConnection.edges?.map((e) => e?.node) ?? [];
}
// Get a single post by filename
async function getPost(slug: string) {
const response = await client.queries.post({
relativePath: `${slug}.mdx`,
});
return response.data.post;
}
Installation
# Add to an existing Next.js project
npx @tinacms/cli@latest init
# This creates:
# - tina/config.ts (schema configuration)
# - tina/__generated__/ (auto-generated client and types)
# Run development server with visual editing
npx tinacms dev -c "next dev"
# Build for production
npx tinacms build && next build
Examples
Example 1: Setting up Tinacms with a custom configuration
User request:
I just installed Tinacms. Help me configure it for my TypeScript + React workflow with my preferred keybindings.
The agent creates the configuration file with TypeScript-aware settings, configures relevant plugins/extensions for React development, sets up keyboard shortcuts matching the user's preferences, and verifies the setup works correctly.
Example 2: Extending Tinacms with custom functionality
User request:
I want to add a custom visual editing in next.js to Tinacms. How do I build one?
The agent scaffolds the extension/plugin project, implements the core functionality following Tinacms's API patterns, adds configuration options, and provides testing instructions to verify it works end-to-end.
Guidelines
- Git is your database — Content lives in Markdown/JSON files in your repo; every edit creates a git commit
- Use MDX for rich content — MDX lets editors use custom React components (callouts, embeds, interactive elements)
- Define templates for blocks — Page builder patterns (hero, features, CTA) give editors flexibility without code
- Type-safe queries — Tina generates TypeScript types from your schema; use the generated client, not raw GraphQL
- Visual editing in dev — Run
tinacms devlocally for real-time editing preview; editors see changes as they type - Branch-based workflow — Editors can work on branches; content changes go through PR review like code
- Media in Git LFS — For repos with many images, use Git LFS to keep the repo size manageable
- Self-host for control — Tina Cloud handles auth and Git; self-host the backend for full control over the editing API