P8: Astro with (Tina) CMS

Setting up TinaCMS with Astro

This guide will walk you through integrating TinaCMS with your Astro project, enabling you to add a visual content editor to your static site.

Prerequisites

  • Node.js (v14 or higher)

  • An existing Astro project or create a new one

  • Basic familiarity with Astro and markdown/MDX

Step 1: Create or Navigate to Your Astro Project

If you don't have an existing Astro project, create one:

# Create a new project
npm create astro@latest my-tina-site

# Navigate to the project directory
cd my-tina-site

Step 2: Install TinaCMS Dependencies

Install the required TinaCMS packages:

npm install tinacms @tinacms/cli @astrojs/mdx

Step 3: Configure Astro for MDX

Add MDX support to your Astro configuration. Create or modify astro.config.mjs:

import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';

export default defineConfig({
  integrations: [mdx()],
});

Step 4: Initialize TinaCMS

Initialize TinaCMS in your project:

npx @tinacms/cli init

This will create several configuration files:

  • tina/config.{ts,js}: Main TinaCMS configuration

  • .env: Environment variables

  • .tina/__generated__: Generated TypeScript types

Step 5: Configure TinaCMS

Modify tina/config.ts to define your content schema:

import { defineConfig } from 'tinacms';

export default defineConfig({
  branch: process.env.TINA_BRANCH || '',
  clientId: process.env.TINA_CLIENT_ID || '',
  token: process.env.TINA_TOKEN || '',
  
  build: {
    outputFolder: 'admin',
    publicFolder: 'public',
  },
  
  media: {
    tina: {
      mediaRoot: '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: 'datetime',
            name: 'date',
            label: 'Date',
            required: true,
          },
          {
            type: 'rich-text',
            name: 'body',
            label: 'Body',
            isBody: true,
          },
        ],
      },
    ],
  },
});

Step 6: Set Up Content Source

Create a directory for your content:

mkdir -p src/content/posts

Step 7: Add Environment Variables

Create a .env file in your project root:

# Local development
TINA_PUBLIC_CLIENT_ID=<your-client-id>
TINA_TOKEN=<your-token>

# Production
TINA_PUBLIC_IS_LOCAL=false
TINA_PUBLIC_SEARCH_TOKEN=<your-search-token>

Step 8: Update Package Scripts

Add TinaCMS scripts to your package.json:

{
  "scripts": {
    "dev": "tinacms dev -c \"astro dev\"",
    "build": "tinacms build && astro build"
  }
}

Step 9: Create a Content Component

Create a new component to display your content. Create src/components/Post.astro:

---
const { content } = Astro.props;
---

<article>
  <h1>{content.title}</h1>
  <time datetime={content.date}>
    {new Date(content.date).toLocaleDateString()}
  </time>
  <div class="content">
    <content.body />
  </div>
</article>

Step 10: Create a Page to Display Content

Create a page to display your content. Create or modify src/pages/[slug].astro:

---
import { getCollection } from 'astro:content';
import Post from '../components/Post.astro';
import Layout from '../layouts/Layout.astro';

export async function getStaticPaths() {
  const posts = await getCollection('post');
  return posts.map(post => ({
    params: { slug: post.slug },
    props: { post },
  }));
}

const { post } = Astro.props;
const { Content } = await post.render();
---

<Layout>
  <Post content={post.data}>
    <Content />
  </Post>
</Layout>

Step 11: Start the Development Server

Run the development server with TinaCMS:

npm run dev

Visit http://localhost:4321/admin to access the TinaCMS admin interface.

Additional Configuration

Authentication

For production, you'll need to set up authentication. TinaCMS supports various authentication providers:

  1. GitHub (recommended for open-source projects)

  2. Custom authentication

  3. Clerk, Auth0, or other third-party providers

Add authentication configuration to your tina/config.ts:

export default defineConfig({
  // ... other config
  auth: {
    providers: [
      {
        name: 'github',
        oauth: {
          clientId: process.env.GITHUB_CLIENT_ID || '',
          clientSecret: process.env.GITHUB_CLIENT_SECRET || '',
        },
      },
    ],
  },
});

Media Management

TinaCMS supports various media handlers. Configure media management in your tina/config.ts:

export default defineConfig({
  // ... other config
  media: {
    tina: {
      mediaRoot: 'public/images',
      publicFolder: 'public',
    },
  },
});

Custom Fields

You can add custom fields to your content schema:

fields: [
  {
    type: 'object',
    name: 'metadata',
    label: 'Metadata',
    fields: [
      {
        type: 'string',
        name: 'description',
        label: 'Description',
      },
      {
        type: 'image',
        name: 'thumbnail',
        label: 'Thumbnail',
      },
    ],
  },
]

Troubleshooting

Common issues and solutions:

  1. Content not updating:

    • Clear the .tina/__generated__ directory

    • Restart the development server

  2. Type errors:

    • Run npm run tina-gql:generate to regenerate types

    • Check for mismatches between schema and content

  3. Build errors:

    • Ensure all environment variables are set

    • Check for missing dependencies

    • Verify content paths match your configuration

Best Practices

  1. Version Control:

    • Commit .tina/config.ts

    • Ignore .env and .tina/__generated__

  2. Content Organization:

    • Use meaningful collection names

    • Organize content in logical directories

    • Use consistent naming conventions

  3. Performance:

    • Optimize images before upload

    • Use appropriate field types

    • Implement proper caching strategies

Resources

Last updated