Quick Start

Learn how to add a complete, Druid blog to your Next.js app in minutes using the Druid SDK.

Prerequisites
Before you begin, make sure your Next.js project has the following dependencies installed.
Next.js ^15.5.0App Router with TypeScript support
React ^19.1.1Latest React with concurrent features
Tailwind CSS ^4.1.12For styling and responsive design
shadcn/uiUI component library built on Radix UI
1Install the SDK
Add the Druid SDK to your project.
Terminal
pnpm install @druid-sh/sdk
2Configure CSS Styles
Add the Druid SDK styles and your preferred Highlight.js theme.
src/app/globals.css
@tailwind base;@tailwind components;@tailwind utilities;/* Import Druid SDK styles */@import "../../node_modules/@druid-sh/sdk/dist";/* Your preferred highlight.js theme */@import "highlight.js/styles/github.css";/* Dark theme variant */.dark {  @import "highlight.js/styles/github-dark.css";}/* Alternative themes you can use:@import "highlight.js/styles/atom-one-light.css";@import "highlight.js/styles/vs2015.css";@import "highlight.js/styles/monokai.css";*/
3Configure the Client
Create a client instance.
src/lib/client.ts
import { DruidClient } from "@druid-sh/sdk";if (!process.env.NEXT_PUBLIC_DRUID_API_KEY) {  throw new Error("NEXT_PUBLIC_DRUID_API_KEY environment variable not set");}export const druid = new DruidClient({  apiKey: process.env.NEXT_PUBLIC_DRUID_API_KEY});
4Create the Blog List Page
This page renders a paginated list of all blog posts at /blog.
src/app/blog/page.tsx
import { druid } from "@/lib/client";import { BlogList, generateBlogListMetadata } from "@druid-sh/sdk";import type { Metadata } from "next";export const metadata: Metadata = generateBlogListMetadata(  `Blog - ${druid.siteName}`);interface BlogHomeProps {  params: Promise<{ page?: string }>;}export const dynamic = "force-static";export const revalidate = 60;export default async function BlogHome({ params }: BlogHomeProps) {  const { page } = await params;  const data = await druid.getPosts(parseInt(page || "1"));  return <BlogList data={data} />;}
5Create the Individual Post Page
This page displays a single post at /blog/post/[slug] with SEO metadata.
src/app/blog/post/[slug]/page.tsx
import { druid } from "@/lib/client";import { BlogPost, generateBlogPostMetadata } from "@druid-sh/sdk";import type { Metadata } from "next";import { notFound } from "next/navigation";interface BlogPostPageProps {  params: Promise<{ slug: string }>;}export async function generateMetadata({  params,}: BlogPostPageProps): Promise<Metadata> {  const { slug } = await params;  const postData = await druid.getPost(slug);  if (!postData.post) {    return {      title: "Post Not Found",    };  }  return generateBlogPostMetadata(    postData.post,    `${postData.post.title} - ${druid.siteName}`  );}export async function generateStaticParams() {  const slugs = await druid.getSlugs();  return slugs;}export const revalidate = 60;export default async function BlogPostPage({ params }: BlogPostPageProps) {  const { slug } = await params;  const data = await druid.getPost(slug);  if (!data.post) {    notFound();  }  return <BlogPost data={data} />;}
6Create the Tag List Page
This page lists posts under a specific tag at /blog/tag/[tag].
src/app/blog/tag/[tag]/page.tsx
import { druid } from "@/lib/client";import { BlogList, generateBlogListMetadata } from "@druid-sh/sdk";import { Metadata } from "next";import { notFound } from "next/navigation";interface BlogTagHomeProps {  params: Promise<{ tag: string }>;}export async function generateMetadata({  params,}: BlogTagHomeProps): Promise<Metadata> {  const { tag } = await params;  const tagData = await druid.getTag(tag);  return generateBlogListMetadata(`${tagData.name} - Blog - ${druid.siteName}`);}export async function generateStaticParams() {  const tags = await druid.getTags();  return tags.map((tag) => ({    tag: tag.slug,  }));}export const revalidate = 60;export default async function BlogTagHome({ params }: BlogTagHomeProps) {  const { tag } = await params;  const data = await druid.getPostsByTag(tag, 1);  if (data.posts.length === 0) {    return notFound();  }  return <BlogList data={data} />;}
7Create the Tag Pages
This page lists posts under a specific tag and page at /blog/tag/[tag]/page/[page].
src/app/blog/tag/[tag]/page/[page]/page.tsx
import { druid } from "@/lib/client";import { BlogList, generateBlogListMetadata } from "@druid-sh/sdk";import type { Metadata } from "next";export const metadata: Metadata = generateBlogListMetadata(  `Blog - ${druid.siteName}`);interface BlogTagPageProps {  params: Promise<{ tag: string; page?: string }>;}export async function generateStaticParams() {  const data = await druid.getTagPages();  return data;}export const revalidate = 60;export default async function BlogTagPage({ params }: BlogTagPageProps) {  const { tag, page } = await params;  const data = await druid.getPostsByTag(tag, parseInt(page || "1"));  return <BlogList data={data} />;}
8Create the Blog Pages
This page lists posts under a specific page at /blog/page/[page].
src/app/blog/tag/[tag]/page/[page]/page.tsx
import { druid } from "@/lib/client";import { BlogList, generateBlogListMetadata } from "@druid-sh/sdk";import type { Metadata } from "next";export const metadata: Metadata = generateBlogListMetadata(  `Blog - ${druid.siteName}`);interface BlogPageProps {  params: Promise<{ page?: string }>;}export async function generateStaticParams() {  const data = await druid.getPages();  return data;}export const revalidate = 60;export default async function BlogPage({ params }: BlogPageProps) {  const { page } = await params;  const data = await druid.getPosts(parseInt(page || "1"));  return <BlogList data={data} />;}

🎉 You're Live!

With these pages in place, your blog is fully functional at /blog. You can now publish posts from your dashboard, and the SDK handles fetching, routing, rendering, and SEO for you.

🍪 We use cookies for functional and analytical purposes.