🚀Framework Integration

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-here

2. 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 react

Create 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