Separating Content from Metadata: The Sidecar Pattern in Astro 5
Astro Content Management Architecture SSG

Separating Content from Metadata: The Sidecar Pattern in Astro 5

8 min read
By Development Team

When building content-heavy sites, we often face a challenge: how do we integrate pure Markdown documentation from external repositories while maintaining our own metadata requirements? The traditional frontmatter approach modifies the original Markdown files, making it difficult to sync external content sources without touching them.

Enter the sidecar metadata pattern - a clean architectural solution enabled by Astro 5’s powerful Content Layer API.

The Problem: Frontmatter Friction

Traditional Astro content collections use YAML frontmatter embedded in Markdown files:

---
title: "My Article"
publicationDate: 2024-01-25
tags: ["Astro", "SSG"]
---

# My Article Content

This works beautifully for content you control, but creates friction when:

  • Syncing external repositories: Documentation from other projects needs to stay pristine
  • Multi-site publishing: The same Markdown serves different sites with different metadata
  • Git submodules: External content sources shouldn’t require modification
  • Content portability: Pure Markdown is more universally compatible

The Solution: Sidecar Metadata Files

The sidecar pattern separates content from metadata by using companion files:

content/
  blog/
    my-article.md           # Pure Markdown, no frontmatter
    my-article.meta.yaml    # All metadata in a separate file

This approach offers several advantages:

  • Pristine source files: Original Markdown remains untouched
  • Clear separation of concerns: Content vs. site-specific metadata
  • Flexible metadata storage: YAML, JSON, or any format you prefer
  • Easy external syncing: Pull docs from other repos as-is

Astro 5’s Content Layer API

Astro 5 introduced a stable Content Layer API that makes custom loaders straightforward. According to the official documentation, loaders are “pluggable functions that fetch and transform data from any source.”

Key Features for Our Use Case

  1. Custom Loaders: Build loaders that combine multiple data sources
  2. Incremental Updates: Efficient change detection with content digests
  3. Type Safety: Full Zod schema validation
  4. Performance: 5x faster builds for Markdown content, 25-50% less memory usage

Implementation Plan

Phase 1: Custom Sidecar Loader

Create a loader that:

  • Scans for .md files in a directory
  • Looks for companion .meta.yaml files
  • Merges them into Astro’s content layer
  • Falls back to frontmatter if no sidecar exists (hybrid approach)

Key Components:

// src/lib/loaders/sidecar-loader.ts
export function sidecarLoader(options: {
  directory: string;
  schema: ZodSchema;
  fallbackToFrontmatter?: boolean;
}): Loader {
  return {
    name: 'sidecar-loader',
    load: async ({ store, parseData, generateDigest }) => {
      // Implementation here
    },
    schema: options.schema,
  };
}

Phase 2: File Structure

Organize content to support both patterns:

src/content/
  blog/
    # New sidecar pattern
    external-doc.md          # Pure Markdown from external repo
    external-doc.meta.yaml   # Local metadata

    # Traditional pattern (still supported)
    local-article.md         # Markdown with frontmatter

Phase 3: Collection Configuration

Update content config to use the custom loader:

// src/content/config.ts
import { sidecarLoader } from '../lib/loaders/sidecar-loader';

const blog = defineCollection({
  loader: sidecarLoader({
    directory: './src/content/blog',
    schema: z.object({
      title: z.string(),
      description: z.string(),
      publicationDate: z.coerce.date(),
      tags: z.array(z.string()),
      // ... rest of schema
    }),
    fallbackToFrontmatter: true, // Hybrid support
  }),
});

Phase 4: Sync Workflow

Create automation for external content:

# scripts/sync-external-docs.sh
#!/bin/bash

# Pull external documentation
git submodule update --remote external/docs

# Generate metadata for new files
node scripts/generate-metadata.js

# Build site
npm run build

Technical Implementation Details

Change Detection

The loader uses Astro’s digest system for efficient incremental builds:

const digest = generateDigest({
  markdown: markdownContent,
  metadata: metadataContent,
  lastModified: stats.mtime,
});

store.set({ id, data, digest, filePath: mdPath });

Error Handling

Graceful fallbacks when metadata files are missing:

let metadata = {};
try {
  const metaContent = await fs.readFile(metaPath, 'utf-8');
  metadata = parseYaml(metaContent);
} catch {
  logger.warn(`No metadata file for ${mdPath}, checking frontmatter`);
  if (options.fallbackToFrontmatter) {
    metadata = extractFrontmatter(markdownContent);
  }
}

Type Safety

Full TypeScript support through Zod schemas:

const data = await parseData({
  id,
  data: { ...metadata, body: markdown }
});
// data is now fully typed based on your schema

Real-World Use Cases

1. Documentation Aggregation

Pull docs from multiple repositories:

content/docs/
  astro-official.md              # From Astro's repo
  astro-official.meta.yaml       # Local customization
  react-docs.md                  # From React's repo
  react-docs.meta.yaml           # Local customization

2. Multi-Site Publishing

Same content, different metadata per site:

# Site A's metadata
title: "Getting Started Guide"
audience: "beginners"

# Site B's metadata
title: "Quick Start Tutorial"
audience: "developers"

3. Git Submodules

Include external repos without modification:

git submodule add https://github.com/example/docs.git content/external
# Add local metadata files alongside

Performance Considerations

Build Performance

Astro 5’s Content Layer delivers significant improvements:

  • 5x faster for Markdown pages on content-heavy sites
  • 2x faster for MDX processing
  • 25-50% less memory usage

Incremental Updates

The digest system ensures only changed content rebuilds:

const existingEntry = store.get(id);
if (existingEntry?.digest === newDigest) {
  return; // Skip unchanged content
}

Migration Strategy

Gradual Adoption

Start with hybrid support:

  1. Phase 1: Implement loader with fallback to frontmatter
  2. Phase 2: Migrate high-value content to sidecar pattern
  3. Phase 3: Move external sources to pure Markdown
  4. Phase 4: Optional - remove frontmatter support entirely

Backward Compatibility

The hybrid approach ensures no breaking changes:

fallbackToFrontmatter: true // Supports both patterns

Resources & References

Official Documentation

Community Examples

Technical Background

Next Steps

To implement this pattern in your Antler site:

  1. Create the loader: Build src/lib/loaders/sidecar-loader.ts
  2. Update config: Modify src/content/config.ts to use the loader
  3. Test with sample: Create a test blog post using the sidecar pattern
  4. Add external source: Set up a git submodule or sync script
  5. Document workflow: Create team guidelines for metadata management

Benefits Summary

Pure Markdown sources - No modification of external content ✅ Type-safe metadata - Full Zod validation and TypeScript support ✅ Flexible metadata - Different formats for different needs ✅ Performance gains - Leverage Astro 5’s optimized build system ✅ Backward compatible - Support both patterns during migration ✅ Clear separation - Content vs. site-specific concerns

Conclusion

The sidecar metadata pattern, enabled by Astro 5’s Content Layer API, provides a elegant solution for managing content from diverse sources. Whether you’re aggregating documentation, publishing to multiple sites, or simply want cleaner separation of concerns, this pattern offers flexibility without sacrificing the developer experience or type safety that makes Astro great.

By keeping content pristine and metadata separate, we can build more maintainable, portable, and collaborative content systems. The pattern respects the original sources while giving us the rich metadata capabilities modern sites require.


This feature is planned for a future enhancement to Antler. Have thoughts on the implementation? Contributions and feedback are welcome!