New Features in Astro 5 - Content Management Evolves with Content Layer

2025.12.09

Astro 5 Overview

Astro 5, with its new Content Layer API, now enables unified management of content from any source. Additionally, with official support for Server Islands, both performance and flexibility have improved.

Content Layer API

Fetching Content from Various Sources

// src/content.config.ts
import { defineCollection, z } from 'astro:content';
import { glob, file } from 'astro/loaders';

// Local files
const blog = defineCollection({
  loader: glob({ pattern: '**/*.md', base: './src/content/blog' }),
  schema: z.object({
    title: z.string(),
    date: z.date(),
  }),
});

// Fetch from API
const products = defineCollection({
  loader: async () => {
    const response = await fetch('https://api.example.com/products');
    return response.json();
  },
  schema: z.object({
    id: z.string(),
    name: z.string(),
    price: z.number(),
  }),
});

// Fetch from CMS
const articles = defineCollection({
  loader: contentfulLoader({
    spaceId: process.env.CONTENTFUL_SPACE_ID,
    contentType: 'article',
  }),
  schema: z.object({
    title: z.string(),
    body: z.string(),
  }),
});

export const collections = { blog, products, articles };

Unified Query API

---
import { getCollection, getEntry } from 'astro:content';

// Get all blog posts
const posts = await getCollection('blog');

// Get a specific product
const product = await getEntry('products', 'item-123');

// Filtering
const publishedPosts = await getCollection('blog', ({ data }) => {
  return data.draft !== true;
});
---

Server Islands

You can place dynamic server components within static pages.

---
// src/pages/index.astro
import UserGreeting from '../components/UserGreeting.astro';
import RecentPosts from '../components/RecentPosts.astro';
---

<html>
  <body>
    <h1>Welcome</h1>

    <!-- Static content -->
    <p>About this site...</p>

    <!-- Server Island: Rendered on server per request -->
    <UserGreeting server:defer>
      <p slot="fallback">Loading...</p>
    </UserGreeting>

    <!-- Another Server Island -->
    <RecentPosts server:defer />
  </body>
</html>
---
// src/components/UserGreeting.astro
const user = await getUser(Astro.cookies.get('session'));
---

<div>
  {user ? (
    <p>Hello, {user.name}!</p>
  ) : (
    <a href="/login">Login</a>
  )}
</div>

Performance Improvements

Build Time Reduction

Astro 4 vs 5 (1000 page site):
- Build time: 35% faster
- Memory usage: 30% reduction

Vite 6 Integration

// astro.config.mjs
export default defineConfig({
  vite: {
    // Vite 6 new features available
    environments: {
      ssr: {
        // SSR-specific configuration
      }
    }
  }
});

Improved Type Safety

env Schema

// src/env.d.ts
/// <reference types="astro/client" />

interface ImportMetaEnv {
  readonly DATABASE_URL: string;
  readonly API_KEY: string;
}

// astro.config.mjs
export default defineConfig({
  env: {
    schema: {
      DATABASE_URL: envField.string({ context: 'server', access: 'secret' }),
      API_KEY: envField.string({ context: 'server', access: 'secret' }),
      PUBLIC_SITE_URL: envField.string({ context: 'client', access: 'public' }),
    }
  }
});

New Adapters

Cloudflare

import cloudflare from '@astrojs/cloudflare';

export default defineConfig({
  output: 'server',
  adapter: cloudflare({
    imageService: 'cloudflare',
    platformProxy: {
      enabled: true
    }
  })
});

Migration Guide

# Upgrade
npx @astrojs/upgrade

# Or manually
npm install astro@latest

Major Breaking Changes

ChangeAction Required
Content Collections API changeMigrate to content.config.ts
Some default value changesSet explicitly
Node.js 18.17.1+ requiredUpgrade Node.js

Summary

Astro 5, with its Content Layer API, enables unified content management regardless of where the content comes from. Combined with Server Islands, you can add dynamic features while maintaining the speed of static sites.

← Back to list