Tina CMS: Visual Editing for Static Sites and Next.js
Tina CMS is an open-source, Git-backed content management system with a distinguishing feature that sets it apart from most headless options: inline visual editing. Where tools like Decap CMS present editors with a form interface that is separate from the rendered site, Tina renders the actual page alongside the editing controls — editors see changes in context as they type.
This matters for publishing teams where the gap between the form and the final output causes friction. The inline editing model removes that gap.
What Tina Is
Tina is built around a few core ideas:
Git-backed content. Content lives in your repository as Markdown or MDX files. Tina reads from and writes to Git, so content is version-controlled, diffable, and owned by you in the same way your code is. There is no proprietary content database.
Schema-defined content. You define your content model in a Tina configuration file (tina/config.ts). This schema drives both the editing interface and the TypeScript types Tina generates for your front end — content is typed end to end.
Visual editing. For React-based front ends (Next.js, Remix), Tina can render the live site in an editing context where each piece of content is directly clickable and editable. For Hugo and other non-React generators, Tina provides a form-based interface similar to Decap CMS.
Tina Cloud. The managed hosting option for Tina’s backend (content API and media management). Self-hosting Tina’s backend is also possible but more involved.
Setup with Next.js
Initialize Tina in an existing Next.js project:
npx @tinacms/cli@latest init
This creates a tina/ directory with a config.ts file and installs the required dependencies.
Define your content schema in tina/config.ts:
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",
},
media: {
tina: {
mediaRoot: "images",
publicFolder: "public",
},
},
schema: {
collections: [
{
name: "post",
label: "Posts",
path: "content/posts",
format: "mdx",
fields: [
{ type: "string", name: "title", label: "Title", isTitle: true, required: true },
{ type: "datetime", name: "date", label: "Date" },
{ type: "boolean", name: "draft", label: "Draft" },
{ type: "string", name: "excerpt", label: "Excerpt", ui: { component: "textarea" } },
{ type: "image", name: "featured_image", label: "Featured Image" },
{ type: "string", name: "tags", label: "Tags", list: true },
{ type: "rich-text", name: "body", label: "Body", isBody: true },
],
},
],
},
});
Visual Editing in Next.js Pages
For inline editing to work, wrap your page component with Tina’s useTina hook, which subscribes the component to real-time content updates from the Tina sidebar:
import { useTina } from "tinacms/dist/react";
import { client } from "../tina/__generated__/client";
export default function Post({ data, query, variables }) {
const { data: tinaData } = useTina({ query, variables, data });
return (
<article>
<h1>{tinaData.post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: tinaData.post.body }} />
</article>
);
}
export async function getStaticProps({ params }) {
const { data, query, variables } = await client.queries.post({
relativePath: `${params.slug}.mdx`,
});
return { props: { data, query, variables } };
}
When editors open /admin and navigate to the post, Tina renders the live page on the right with an editing panel on the left. Typing in the title field updates the rendered title in real time. The visual editing experience is the closest thing to WYSIWYG available in a Git-backed static site workflow.
Setup with Hugo
Tina does not provide visual editing for Hugo (since Hugo templates are not React components), but it does provide a form-based editorial interface similar to Decap CMS. The setup is simpler than the Next.js integration:
npx @tinacms/cli@latest init
Configure the schema in tina/config.ts to match your Hugo content structure — same field definitions, pointed at your content/posts/ directory. Tina generates a /admin interface that editors use to create and edit Markdown files, which are committed to Git and trigger Hugo builds.
The primary advantage of Tina over Decap CMS for Hugo is the content schema definition in TypeScript (more explicit and maintainable than Decap’s YAML config), the media management interface, and Tina Cloud’s content API for querying content outside the build process.
Content Querying
Tina generates a typed GraphQL client from your schema. Query content in build scripts or API routes:
import { client } from "../tina/__generated__/client";
// Get all posts
const postsResponse = await client.queries.postConnection();
const posts = postsResponse.data.postConnection.edges.map(e => e.node);
// Get a single post
const postResponse = await client.queries.post({
relativePath: "my-post.mdx"
});
const post = postResponse.data.post;
The generated types mean your editor autocompletes field names and TypeScript catches schema mismatches at build time.
Tina Cloud vs Self-Hosted
Tina Cloud provides the content API, user authentication, and media management as a hosted service. The free tier covers one user and basic usage — sufficient for solo publishers. Paid plans scale by team size.
Self-hosting the Tina backend requires running a separate service alongside your front end. The open-source tinacms package includes the tools to do this, and the documentation covers deployment on Railway, Render, and similar platforms.
When Tina Is the Right Choice
Tina is best suited for Next.js projects where visual editing is a priority for non-technical editors, or for teams who want typed content schemas and a Git-backed workflow with a more polished editorial interface than Decap CMS provides.
For Hugo sites with technical editors comfortable with Markdown, Decap CMS or direct file editing is less overhead. For Hugo sites with non-technical editors who need a GUI, Tina is a meaningful upgrade over Decap. For Next.js sites where editors should be able to see their changes in context, Tina is the best current option in the Git-backed CMS category.