Combining Singleton + Collection for Listing Pages
Some pages need both curated intro content (text, blocks, SEO) and a dynamic list of collection items. This combo pattern gives editors full control over the page structure while keeping entries maintainable.
The Pattern
- Singleton stores the listing page content: title, intro text, SEO fields, optional blocks
- Collection stores the repeatable items
Example: Services Listing
The services page uses this pattern. The /services page reads from the pages singleton (intro content) and the services collection (item cards).
---
import BaseLayout from '../layouts/BaseLayout.astro';
import { reader } from '../lib/reader';
// Singleton for page content
const page = await reader.collections.pages.read('services');
// Collection for items
const services = await reader.collections.services.all();
Implementing Your Own
Create a singleton for the listing page:
// singletons/projects-listing.ts
export const projectsListing = singleton({
label: 'Projects',
path: 'content/projects-listing',
schema: {
introTitle: fields.text({ label: 'Intro Title' }),
introText: fields.text({ label: 'Intro Text', multiline: true }),
...seoFields,
},
});
Create a collection for the items:
// collections/projects.ts
export const projects = collection({
label: 'Projects',
slugField: 'title',
path: 'content/projects/*',
schema: {
title: fields.slug({ name: { label: 'Title' } }),
description: fields.text({ label: 'Description', multiline: true }),
url: fields.url({ label: 'URL' }),
},
});
Then the Astro page combines both reads. The singleton content wraps the listing, and collection items are rendered in a grid.
Benefits
- Editors edit intro text without touching code
- Items remain individually manageable
- SEO fields work per listing page
- No hardcoded content