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:
GitHub (recommended for open-source projects)
Custom authentication
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:
Content not updating:
Clear the
.tina/__generated__
directoryRestart the development server
Type errors:
Run
npm run tina-gql:generate
to regenerate typesCheck for mismatches between schema and content
Build errors:
Ensure all environment variables are set
Check for missing dependencies
Verify content paths match your configuration
Best Practices
Version Control:
Commit
.tina/config.ts
Ignore
.env
and.tina/__generated__
Content Organization:
Use meaningful collection names
Organize content in logical directories
Use consistent naming conventions
Performance:
Optimize images before upload
Use appropriate field types
Implement proper caching strategies
Resources
Last updated