How to Use TinaCMS with Astro: Complete Guide
Step-by-step guide to integrating TinaCMS with your Astro website.
TinaCMS is a Git-backed headless CMS that gives you visual editing on your actual site while storing all content as Markdown or MDX files in your repository. The key differentiator here is that your content lives in Git, not in a third-party database. Edits made through the visual editor create real commits in your repo. For developers who want version-controlled content with a friendly editing interface, Tina is a compelling option.
With Astro, TinaCMS provides a GraphQL API layer over your local Markdown/MDX files. You define your content schema in code, and Tina generates a type-safe API plus an admin UI that editors can use to modify content without touching files directly.
Prerequisites
- Node.js 18+
- An Astro project (
npm create astro@latest) - A GitHub repository for your project (Tina commits directly to your repo)
- A Tina Cloud account for the hosted editing experience (free tier available, paid from $29/mo)
Installation
Initialize TinaCMS in your existing Astro project:
npx @tinacms/cli@latest init
This command scaffolds the configuration and adds the necessary dependencies. It creates a tina/ directory with your schema configuration.
If you prefer manual setup:
npm install tinacms @tinacms/cli
Configuration
The init command creates tina/config.ts. Define your content schema here:
// tina/config.ts
import { defineConfig } from "tinacms";
export default defineConfig({
branch: process.env.TINA_BRANCH || "main",
clientId: process.env.TINA_CLIENT_ID || "",
token: process.env.TINA_TOKEN || "",
build: {
outputFolder: "admin",
publicFolder: "public",
},
media: {
tina: {
mediaRoot: "blog-images",
publicFolder: "public",
},
},
schema: {
collections: [
{
name: "post",
label: "Blog Posts",
path: "src/content/posts",
format: "mdx",
fields: [
{
type: "string",
name: "title",
label: "Title",
isTitle: true,
required: true,
},
{
type: "string",
name: "description",
label: "Description",
},
{
type: "datetime",
name: "publishDate",
label: "Publish Date",
},
{
type: "image",
name: "heroImage",
label: "Hero Image",
},
{
type: "string",
name: "tags",
label: "Tags",
list: true,
},
{
type: "rich-text",
name: "body",
label: "Body",
isBody: true,
},
],
},
],
},
});
Add your Tina Cloud credentials to .env:
TINA_CLIENT_ID=your-client-id
TINA_TOKEN=your-read-only-token
TINA_BRANCH=main
Get these from the Tina Cloud dashboard after connecting your GitHub repository.
Basic Usage
TinaCMS provides a GraphQL client that reads your content. Use it to fetch posts in your Astro pages:
---
// src/pages/blog/index.astro
import { client } from "../../tina/__generated__/client";
import BaseLayout from "../../layouts/BaseLayout.astro";
const postsResponse = await client.queries.postConnection({
sort: "publishDate",
last: 50,
});
const posts = postsResponse.data.postConnection.edges || [];
---
<BaseLayout title="Blog">
<h1>Blog</h1>
{posts.map(({ node: post }) => (
<article>
<a href={`/blog/${post._sys.filename}`}>
<h2>{post.title}</h2>
<p>{post.description}</p>
</a>
</article>
))}
</BaseLayout>
For individual post pages:
---
// src/pages/blog/[slug].astro
import { client } from "../../tina/__generated__/client";
import { TinaMarkdown } from "tinacms/dist/rich-text";
import BaseLayout from "../../layouts/BaseLayout.astro";
export async function getStaticPaths() {
const postsResponse = await client.queries.postConnection();
const posts = postsResponse.data.postConnection.edges || [];
return posts.map(({ node }) => ({
params: { slug: node._sys.filename },
}));
}
const { slug } = Astro.params;
const postResponse = await client.queries.post({
relativePath: `${slug}.mdx`,
});
const post = postResponse.data.post;
---
<BaseLayout title={post.title}>
<article>
<h1>{post.title}</h1>
{post.heroImage && (
<img src={post.heroImage} alt={post.title} />
)}
<div set:html={post.body} />
</article>
</BaseLayout>
Run the dev server with Tina's admin panel:
npx tinacms dev -c "astro dev"
This starts both Astro's dev server and the Tina admin at /admin. Editors can now create and modify content visually.
Production Tips
Use Tina's local mode during development. Run
npx tinacms devfor local development, which reads directly from your filesystem without needing Tina Cloud. This is faster and works offline.Set up branch-based editing. Tina supports editing on different Git branches. Point your staging environment at a
draftbranch so editors can preview changes without affecting production content.Define custom components for rich text. If your MDX uses custom components, register them in Tina's rich-text field configuration. This lets editors insert components like callouts, code blocks, or embeds through the visual editor.
Keep your generated client up to date. Run
npx tinacms buildafter schema changes to regenerate the GraphQL client. The generated types ensure your queries stay in sync with your content model.Use Tina's media manager. Configure the media field to upload images to your repo's public folder. This keeps all assets version-controlled alongside your content.
Alternatives to Consider
- Keystatic if you want a similar Git-based CMS with an even simpler setup and an official Astro integration.
- Prismic if you need a hosted CMS with more advanced slice-based content modeling and scheduled publishing.
- Decap CMS (formerly Netlify CMS) if you want a free, open-source Git-based CMS with a simpler feature set.
Wrapping Up
TinaCMS gives you the best of both worlds: a visual editing experience for content creators and Git-backed content for developers. The schema-as-code approach means your content model is versioned and reviewable in pull requests. For Astro sites where you want to keep content in your repository while still providing a proper CMS interface, Tina is one of the strongest options available. The free tier handles small teams, and the editing experience is smooth enough that non-technical collaborators can contribute content without learning Markdown.
Related Articles
How to Use Algolia with Astro: Complete Guide
Step-by-step guide to integrating Algolia with your Astro website.
How to Integrate Auth0 with Astro: Complete Guide
Step-by-step guide to integrating Auth0 with your Astro website. Setup, configuration, and best practices.
How to Use AWS Amplify with Astro: Complete Guide
Step-by-step guide to integrating AWS Amplify with your Astro website.