Use generateStaticParamsForPosts from @vlozi/blog/next to pre-render every published post at build time. This is the fastest possible way to serve your blog — no server-side rendering, no cold starts.
Installation
npm install @vlozi/blogSet up the client
// lib/vlozi.ts
import { VloziClient } from "@vlozi/blog";
export const vlozi = new VloziClient({
apiKey: process.env.VLOZI_API_KEY ?? "",
baseUrl: process.env.VLOZI_BASE_URL ?? "",
});Generate static paths
// app/blog/[slug]/page.tsx
import { generateStaticParamsForPosts } from "@vlozi/blog/next";
import { vlozi } from "@/lib/vlozi";
export async function generateStaticParams() {
return generateStaticParamsForPosts(vlozi);
// Returns: [{ slug: "my-first-post" }, { slug: "another-post" }, ...]
}This function paginates through your posts in 100-post batches and collects every slug. next build pre-renders one page per slug — the page fetches happen once at build time, not at request time.
Options
generateStaticParamsForPosts(client, {
batchSize?: number, // posts per page request; default 100
maxPosts?: number, // hard cap; default unlimited
})NOTE
For blogs with more than ~1,000 posts, consider setting maxPosts to cap build time, or rely on ISR to cover the long tail. See ISR →.
Generate metadata
// app/blog/[slug]/page.tsx
import { generateMetadataForPost } from "@vlozi/blog/next";
import { vlozi } from "@/lib/vlozi";
export async function generateMetadata({ params }: { params: { slug: string } }) {
return generateMetadataForPost(vlozi, params.slug, {
baseUrl: process.env.NEXT_PUBLIC_SITE_URL, // e.g. "https://mysite.com"
});
}This returns a complete Next.js Metadata object with:
titlefrompost.seoTitle(fallback:post.title)descriptionfrompost.seoDescription(fallback:post.excerpt)openGraph— typearticle, title, description, image (with width/height), publishedTime, tags as keywordstwittercard metadata mirroring OpenGraph
Options
generateMetadataForPost(client, slug, {
baseUrl?: string, // canonical URL base for OpenGraph; default: ""
imageWidth?: number, // default: 1200
imageHeight?: number, // default: 630
})Returns {} (empty metadata) when the post is not found — Next.js handles 404 gracefully.
Full page example
// app/blog/[slug]/page.tsx
import { generateStaticParamsForPosts, generateMetadataForPost } from "@vlozi/blog/next";
import { BlogContent } from "@vlozi/blog/react";
import { vlozi } from "@/lib/vlozi";
import { notFound } from "next/navigation";
import Image from "next/image";
export async function generateStaticParams() {
return generateStaticParamsForPosts(vlozi);
}
export async function generateMetadata({ params }: { params: { slug: string } }) {
return generateMetadataForPost(vlozi, params.slug, {
baseUrl: process.env.NEXT_PUBLIC_SITE_URL,
});
}
export default async function BlogPostPage({ params }: { params: { slug: string } }) {
const post = await vlozi.blog.get(params.slug).catch(() => null);
if (!post) notFound();
return (
<article className="max-w-3xl mx-auto py-12 px-4">
{post.featuredImageUrl && (
<Image
src={post.featuredImageUrl}
alt={post.title}
width={1200}
height={630}
className="rounded-lg mb-8 w-full object-cover"
priority
/>
)}
<h1 className="text-4xl font-bold mb-4">{post.title}</h1>
<div className="flex items-center gap-4 text-sm text-muted-foreground mb-8">
<time dateTime={post.publishedAt}>
{new Date(post.publishedAt).toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
})}
</time>
{post.category && (
<span className="bg-primary/10 text-primary px-2 py-0.5 rounded-full">
{post.category.name}
</span>
)}
</div>
{post.content && <BlogContent html={post.content} />}
</article>
);
}Blog list page (server-rendered)
// app/blog/page.tsx
import { ServerBlogList } from "@vlozi/blog/server";
import { vlozi } from "@/lib/vlozi";
export default function BlogPage() {
return (
<div className="container py-10">
<h1 className="text-4xl font-bold mb-8">Blog</h1>
<ServerBlogList client={vlozi} limit={12} />
</div>
);
}ServerBlogList is an async React Server Component — it fetches data server-side with zero client JS. For interactive features (search, filter, infinite scroll), use the client-side BlogList with a VloziProvider instead.
How it compares to the client-side approach
| Static generation | Client-side (BlogList) |
|
|---|---|---|
| SEO | Best — HTML in source | Good — requires crawling JS |
| TTFB | Instant (CDN-cached) | Fast (API call on mount) |
| Build time | Scales with post count | None |
| Dynamic filtering | Needs ISR + API | Real-time |
| Recommended for | Post detail pages | Blog list with search/filter |