Official starter templates
Jumpstart your next project using our official open-source starter repositories:
Editorial Blog & Knowledge Base
Complete dynamic blog with category archives, live search, on-demand ISR revalidation, and dark mode.
npx create-schma@latest --template nextjsClient Portal & SPA
Lightweight client-rendered Single Page Application querying SCHMA Delivery APIs via React Query.
npx create-schma@latest --template viteScaffold in seconds
Run the interactive scaffolding tool in your terminal to clone and configure any starter:
# Create a new Next.js 15 App Router project with SCHMA pre-configured
npx create-schma@latest my-blog --template nextjs
cd my-blog
npm run devRecipe: Dynamic Route with Server Components
This example demonstrates how to fetch and render a published entry dynamically in Next.js App Router:
// app/posts/[slug]/page.tsx
import { notFound } from "next/navigation";
import { schma } from "@/lib/schma";
interface Post {
title: string;
slug: string;
content: string;
}
export default async function BlogPostPage({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const { entries } = await schma.delivery.listEntries<Post>({
type: "post",
limit: 1,
});
const post = entries.find((p) => p.data.slug === slug);
if (!post) notFound();
return (
<article className="max-w-2xl mx-auto py-12">
<h1 className="text-4xl font-bold">{post.data.title}</h1>
<div className="prose mt-6">{post.data.content}</div>
</article>
);
}Recipe: On-Demand Revalidation via Webhooks
Keep your static pages lightning fast by serving them from edge cache and invalidating cache tags only when editors publish changes:
// app/api/revalidate/route.ts (Next.js On-Demand Revalidation)
import { revalidateTag } from "next/cache";
import { NextRequest, NextResponse } from "next/server";
import crypto from "crypto";
export async function POST(req: NextRequest) {
const rawBody = await req.text();
const signature = req.headers.get("x-schma-signature");
const secret = process.env.SCHMA_WEBHOOK_SECRET;
if (!signature || !secret) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
// Verify HMAC-SHA256 signature
const hmac = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(hmac))) {
return NextResponse.json({ error: "Invalid signature" }, { status: 403 });
}
const payload = JSON.parse(rawBody);
if (payload.event === "entry.published") {
revalidateTag("content");
}
return NextResponse.json({ revalidated: true });
}Read the complete webhook revalidation guide in our Knowledge Base →