Examples & Starter Templates

Reference architectures, starter codebases, and production patterns to accelerate your development with SCHMA.

Official starter templates

Jumpstart your next project using our official open-source starter repositories:

Next.js 15 App RouterTypeScript

Editorial Blog & Knowledge Base

Complete dynamic blog with category archives, live search, on-demand ISR revalidation, and dark mode.

npx create-schma@latest --template nextjs
Vite + ReactTypeScript

Client Portal & SPA

Lightweight client-rendered Single Page Application querying SCHMA Delivery APIs via React Query.

npx create-schma@latest --template vite

Scaffold in seconds

Run the interactive scaffolding tool in your terminal to clone and configure any starter:

Terminal
# 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 dev

Recipe: 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
// 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
// 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 →