Creating a New Collection
Creating a New Collection
This guide outlines how to create a new collection that matches the Posts collection exactly. We’ll use the creation of a “Showcase” collection as an example.
1. Define Types
First, add your collection types in src/types/content.ts
:
// Add the base type
export type Showcase = BaseContent;
// Add the entry type
export type ShowcaseEntry = ContentEntry<Showcase>;
// Add to CollectionEntry type
export type CollectionEntry<T extends keyof typeof COLLECTIONS> = T extends 'authors'
? AuthorEntry
: T extends 'posts'
? PostEntry
: T extends 'showcase' // Add this line
? ShowcaseEntry // Add this line
: never;
// Add to COLLECTIONS const
export const COLLECTIONS = {
authors: 'authors',
posts: 'posts',
showcase: 'showcase', // Add this line
} as const;
2. Configure Collection
Add your collection configuration in src/content/config.ts
:
const showcase = defineCollection({
type: "content",
schema: baseContentSchema satisfies z.ZodType<BaseContent>,
});
export const collections = {
authors,
posts,
showcase, // Add this line
} as const;
3. Create Collection Component
Create src/components/display/Showcase.astro
that matches Post.astro
exactly:
---
import { formatDate } from "@/lib/utils/date";
const { showcase } = Astro.props;
---
<li class="py-1">
<a href={`/showcase/${showcase.slug}`} data-astro-reload class="inline-flex w-full items-center justify-between gap-4 text-foreground hover:underline">
<span>{showcase.data.title}</span>
<span class="shrink-0 tabular-nums text-right text-muted-foreground">
{formatDate(showcase.data.publicationDate)}
</span>
</a>
</li>
4. Create Index Page
Create src/pages/showcase/index.astro
that matches posts/index.astro
exactly:
---
import { getCollection } from "astro:content";
import BaseLayout from "@/layouts/BaseLayout.astro";
import Showcase from "@/components/display/Showcase.astro";
import OptimizedImage from "@/components/media/OptimizedImage.astro";
import Divider from "@/components/ui/Divider.astro";
import type { SeoMeta } from "@/types/seo";
const showcaseEntries = await getCollection("showcase");
const sortedShowcaseEntries = showcaseEntries.sort(
(a, b) => b.data.publicationDate.valueOf() - a.data.publicationDate.valueOf(),
);
const pages = await getCollection("pages");
const page = pages.find(page => page.id === "showcase.md");
if (!page) {
throw new Error("showcase.md page not found");
}
const { Content } = await page.render();
const metadata: SeoMeta = {
title: page.data.title,
description: page.data.description,
ogImage: page.data.heroImage,
canonicalUrl: new URL("/showcase", Astro.site).toString(),
indexPage: true,
type: 'website',
locale: 'en',
publishedTime: undefined,
modifiedTime: undefined,
authors: undefined,
tags: undefined
};
---
<BaseLayout metadata={metadata}>
{
page.data.heroImage && (
<OptimizedImage
src={page.data.heroImage}
alt={page.data.heroImageAlt ?? ""}
variant="hero"
/>
)
}
<h1 class="text-4xl font-bold tracking-tight mb-8">{page.data.title}</h1>
<div class="prose dark:prose-invert mb-12">
<Content />
</div>
<Divider class="mb-12" />
<h2 class="text-2xl font-semibold tracking-tight mb-6">Showcase</h2>
<ul class="grid list-none gap-1 p-0">
{sortedShowcaseEntries.map((showcase) => <Showcase showcase={showcase} />)}
</ul>
</BaseLayout>
5. Create Slug Page
Create src/pages/showcase/[...slug].astro
that matches posts/[...slug].astro
exactly:
---
import { type CollectionEntry, getCollection, getEntry } from "astro:content";
import BaseLayout from "@/layouts/BaseLayout.astro";
import { formatDate } from "@/lib/utils";
import OptimizedImage from "@/components/media/OptimizedImage.astro";
import type { SeoArticleMeta } from "@/types/seo";
import Badge from "@/components/ui/Badge.astro";
interface Props {
entry: CollectionEntry<"showcase">;
}
export async function getStaticPaths() {
const showcaseEntries = await getCollection("showcase");
return showcaseEntries.map((entry) => ({
params: { slug: entry.slug },
props: { entry },
}));
}
const { entry } = Astro.props;
const { Content } = await entry.render();
const author = entry.data.author ? await getEntry('authors', entry.data.author) : null;
const metadata: SeoArticleMeta = {
title: entry.data.title,
description: entry.data.description,
ogImage: entry.data.heroImage ?? undefined,
canonicalUrl: new URL(`/showcase/${entry.slug}`, Astro.site).toString(),
indexPage: entry.data.indexPage ?? true,
type: 'article',
publishedTime: entry.data.publicationDate.toISOString(),
authors: author ? [author.data.name] : [],
tags: entry.data.tags ?? [],
locale: 'en',
modifiedTime: undefined
};
---
<BaseLayout metadata={metadata}>
{
entry.data.heroImage && (
<OptimizedImage
src={entry.data.heroImage}
alt={entry.data.heroImageAlt ?? ""}
variant="hero"
/>
)
}
<article class="prose dark:prose-invert">
<h1 class="text-4xl font-bold tracking-tight mb-8 not-prose">{entry.data.title}</h1>
<div class="flex items-center text-sm text-muted-foreground mb-8 not-prose">
<div>{formatDate(entry.data.publicationDate)}</div>
{author && (
<>
<div class="mx-3">|</div>
<div>{author.data.name}</div>
</>
)}
</div>
<Content />
{entry.data.tags && entry.data.tags.length > 0 && (
<div class="flex flex-wrap gap-2 mt-8">
{entry.data.tags.map(tagName => (
<Badge variant="secondary">
{tagName}
</Badge>
))}
</div>
)}
</article>
</BaseLayout>
6. Create Collection Page
collections
documentation
guide