Integrating MeshBase with Astro
Build lightning-fast content sites with Astro and MeshBase. Perfect for blogs, documentation, and marketing—zero JavaScript by default, instant loading, flawless SEO.
Why MeshBase + Astro?
🚀Content-First
Astro is built for content sites. MeshBase provides the content. Perfect match for blogs, docs, and marketing sites.
⚡Zero JS by Default
Ship no JavaScript unless needed. MeshBase content loads server-side for instant page loads and perfect SEO.
🏝️Islands Architecture
Add interactive components where needed (React, Vue, Svelte). MeshBase content stays static and fast.
🎯Built for Performance
Astro + MeshBase = some of the fastest possible websites. Pre-rendered HTML, optimized assets, minimal runtime.
What You'll Need
- ✓Astro 3+ project
- ✓A MeshBase account with content types defined
- ✓Your MeshBase API key
Quick Start
Get MeshBase content into your Astro site in minutes.
1. Set up environment variables
Create .env:
MESHBASE_API_URL=https://api.meshbase.io/v1
MESHBASE_API_KEY=your-api-key-here2. Create API helper
Create src/lib/meshbase.ts:
const API_URL = import.meta.env.MESHBASE_API_URL;
const API_KEY = import.meta.env.MESHBASE_API_KEY;
export async function fetchFromMeshBase(endpoint: string) {
const response = await fetch(`${API_URL}${endpoint}`, {
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`Failed to fetch from MeshBase: ${response.status}`);
}
const json = await response.json();
return json.data;
}
// Type-safe helpers
export interface BlogPost {
id: string;
title: string;
excerpt: string;
content: string;
coverImage?: string;
publishedAt: string;
slug: string;
}
export async function getBlogPosts(): Promise<BlogPost[]> {
return fetchFromMeshBase('/blog-posts');
}
export async function getBlogPost(id: string): Promise<BlogPost> {
const data = await fetchFromMeshBase(`/blog-posts/${id}`);
return data;
}3. Fetch in your pages
Create src/pages/blog/index.astro:
---
import { getBlogPosts } from '../../lib/meshbase';
const posts = await getBlogPosts();
---
<html>
<head>
<title>Blog</title>
</head>
<body>
<h1>Blog Posts</h1>
<div class="blog-list">
{posts.map(post => (
<article>
<h2>
<a href={`/blog/${post.slug || post.id}`}>
{post.title}
</a>
</h2>
<p>{post.excerpt}</p>
</article>
))}
</div>
</body>
</html>You're Done!
Your Astro site now fetches MeshBase content at build time. Zero JavaScript sent to the browser—just fast, SEO-perfect HTML!
Dynamic Routes with getStaticPaths
Generate a page for every blog post at build time.
Create src/pages/blog/[id].astro:
---
import { getBlogPosts, getBlogPost } from '../../lib/meshbase';
export async function getStaticPaths() {
const posts = await getBlogPosts();
return posts.map(post => ({
params: { id: post.slug || post.id },
props: { post }
}));
}
const { post } = Astro.props;
---
<html>
<head>
<title>{post.title}</title>
<meta name="description" content={post.excerpt} />
</head>
<body>
<article>
<h1>{post.title}</h1>
{post.coverImage && (
<img src={post.coverImage} alt={post.title} />
)}
<div set:html={post.content} />
</article>
</body>
</html>Pre-rendered at Build Time
Every blog post becomes a static HTML file. No server, no database queries at runtime. Your blog is as fast as it gets!
Using Astro Content Collections
Integrate MeshBase with Astro's Content Collections for type safety and better DX.
Define your collection schema
Create src/content/config.ts:
import { defineCollection, z } from 'astro:content';
const blog = defineCollection({
type: 'data',
schema: z.object({
title: z.string(),
excerpt: z.string(),
content: z.string(),
coverImage: z.string().optional(),
publishedAt: z.string(),
slug: z.string(),
}),
});
export const collections = { blog };Fetch and validate data
---
import { getCollection } from 'astro:content';
import { getBlogPosts } from '../lib/meshbase';
// Fetch from MeshBase and validate against schema
const meshbasePosts = await getBlogPosts();
const posts = meshbasePosts.map(post => ({
id: post.id,
data: post
}));
---Hybrid Rendering (SSR + SSG)
Mix static and server-rendered pages. Perfect for frequently updated content.
Enable SSR mode
Update astro.config.mjs:
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';
export default defineConfig({
output: 'hybrid', // or 'server' for full SSR
adapter: node({
mode: 'standalone'
})
});Mark pages as server-rendered
---
// This page renders on-demand at request time
export const prerender = false;
import { getBlogPosts } from '../../lib/meshbase';
const posts = await getBlogPosts();
---
<html>
<body>
<h1>Latest Posts (always fresh!)</h1>
{posts.map(post => (
<article>
<h2>{post.title}</h2>
</article>
))}
</body>
</html>Best of Both Worlds
Static pages for content that rarely changes. Server-rendered pages for frequently updated content. You decide per-page!
Adding Interactive Islands
MeshBase content stays static. Add React/Vue/Svelte islands for interactive features.
Example: Search component (React)
Install React integration:
npx astro add reactCreate src/components/BlogSearch.tsx:
import { useState } from 'react';
export default function BlogSearch({ posts }) {
const [query, setQuery] = useState('');
const filtered = posts.filter(post =>
post.title.toLowerCase().includes(query.toLowerCase())
);
return (
<div>
<input
type="search"
placeholder="Search posts..."
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
{filtered.map(post => (
<article key={post.id}>
<h3>{post.title}</h3>
</article>
))}
</div>
);
}Use the island in your page
---
import { getBlogPosts } from '../lib/meshbase';
import BlogSearch from '../components/BlogSearch';
const posts = await getBlogPosts();
---
<html>
<body>
<h1>Blog</h1>
{/* Interactive island - only this loads JS */}
<BlogSearch client:load posts={posts} />
</body>
</html>Next Steps
- →Handle images and media
Optimize images with Astro's Image component
- →Explore the full API
Filtering, sorting, pagination, and more
- →Astro Documentation
Learn more about Astro's features