Skip to Content
Getting started

Getting started

@next-md-blog/core is organized around collections (the collection API, introduced in v1.2). A collection owns one content surface (blog, glossary, docs, changelog, …) with its own folder, URL segment, schema type, and RSS feed. A single shared SiteConfig holds site-wide concerns (name, URL, Organization JSON-LD).

Install

npm install @next-md-blog/core # (CLI for scaffolding routes and SEO files) npx @next-md-blog/cli

Define your site + collections

next-md-blog.config.ts
import { defineSite, defineCollection } from '@next-md-blog/core'; export const site = defineSite({ siteName: 'Acme', siteUrl: process.env.NEXT_PUBLIC_SITE_URL ?? 'https://acme.example', defaultAuthor: 'Acme Team', defaultLang: 'en', }); // One collection per content surface. Coexist freely. export const blog = defineCollection({ id: 'blog', contentDir: 'content/blog', pathSegment: 'blog', // → /[lang]/blog/[slug] site, }); export const glossary = defineCollection({ id: 'glossary', contentDir: 'content/glossary', pathSegment: 'glossary', schemaType: 'DefinedTerm', // built-in glossary schema rss: false, site, });

Write a post

content/blog/welcome.md
--- title: Welcome description: First post. date: 2026-01-01 tags: [intro] --- Hello world.

Render it (App Router)

app/blog/[slug]/page.tsx
import { notFound } from 'next/navigation'; import { MarkdownContent } from '@next-md-blog/core'; import { blog } from '@/next-md-blog.config'; export async function generateStaticParams() { const posts = await blog.getAll(); return posts.map((post) => ({ slug: post.slug })); } export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }) { const { slug } = await params; const post = await blog.getOne(slug); if (!post) return { title: 'Not found' }; return blog.metadata(post); } export default async function Page({ params }: { params: Promise<{ slug: string }> }) { const { slug } = await params; const post = await blog.getOne(slug); if (!post) notFound(); return <MarkdownContent content={post.content} />; }

That’s the minimum. The collection encapsulates contentDir, config, locale handling, and schema generation — no more threading options through every call site.

Site-wide files (app/sitemap.ts, robots.ts, feed.xml)

app/sitemap.ts
import { composeSitemap } from '@next-md-blog/core'; import { blog, glossary, site } from '@/next-md-blog.config'; const LOCALES = [site.defaultLang ?? 'en'] as const; export default async function sitemap() { return composeSitemap({ collections: [blog, glossary], locales: LOCALES, }); }
app/robots.ts
import { getRobots } from '@next-md-blog/core/next'; import { site } from '@/next-md-blog.config'; export default () => getRobots(site);
app/feed.xml/route.ts
import { blog } from '@/next-md-blog.config'; export async function GET() { return blog.rssResponse(); }

Next steps

Last updated on