Next.js

ISR and cache revalidation

Keep statically-generated post pages fresh with Next.js Incremental Static Regeneration.

Statically generated pages become stale when you publish or edit a post. Next.js Incremental Static Regeneration (ISR) lets you regenerate individual pages on-demand — without a full rebuild.

ISR tags

The SDK ships two ISR tag helpers:

import {
  VLOZI_BLOG_TAG,   // "vlozi-blog" — tags the entire blog (list + every post)
  vloziPostTag,     // (slug) => "vlozi-post:<slug>" — tags a single post
} from "@vlozi/blog/next";

Wire them into your fetch() calls (or pass them to the VloziClient constructor) so Next.js knows which cache entries to invalidate when you revalidate.

Option A — Set tags on the VloziClient

This is the simplest approach. Every fetch the client makes inherits the tags automatically:

// lib/vlozi.ts
import { VloziClient } from "@vlozi/blog";
import { VLOZI_BLOG_TAG, vloziPostTag } from "@vlozi/blog/next";
 
// Module-scope client for server-side use
export const vlozi = new VloziClient({
  apiKey: process.env.VLOZI_API_KEY ?? "",
  baseUrl: process.env.VLOZI_BASE_URL ?? "",
  nextTags: [VLOZI_BLOG_TAG],  // tag every fetch this client makes
});

For post detail pages, you can add a per-post tag at fetch time:

// app/blog/[slug]/page.tsx
const post = await vlozi.blog.get(params.slug, {
  nextTags: [vloziPostTag(params.slug)],  // in addition to VLOZI_BLOG_TAG
});

Option B — Set tags per-fetch in RSC

import { unstable_cache } from "next/cache";
import { VLOZI_BLOG_TAG, vloziPostTag } from "@vlozi/blog/next";
 
const getPost = unstable_cache(
  (slug: string) => vlozi.blog.get(slug),
  ["blog-post"],
  { tags: [VLOZI_BLOG_TAG, vloziPostTag("__slug__")] }
);

Revalidating

revalidateVloziBlog

Programmatically trigger revalidation from anywhere (API route, server action, webhook handler):

import { revalidateVloziBlog } from "@vlozi/blog/next";
 
// Revalidate everything (list + all posts)
await revalidateVloziBlog();
 
// Revalidate just the list (not individual post pages)
await revalidateVloziBlog({ shallow: true });
 
// Revalidate specific posts by slug
await revalidateVloziBlog({ slugs: ["my-post-slug", "another-post"] });

Under the hood, this calls Next.js revalidateTag() using a dynamic import — the SDK never bundles next/cache into client code.

From a Route Handler (webhook)

Create a route handler that Vlozi (or your own code) can call after publishing:

// app/api/revalidate-blog/route.ts
import { NextResponse } from "next/server";
import { revalidateVloziBlog } from "@vlozi/blog/next";
 
export async function POST(request: Request) {
  const { slug } = await request.json().catch(() => ({}));
 
  if (slug) {
    await revalidateVloziBlog({ slugs: [slug] });
  } else {
    await revalidateVloziBlog();
  }
 
  return NextResponse.json({ revalidated: true });
}

Then after publishing a post in the Vlozi Dashboard, call this endpoint:

curl -X POST https://yoursite.com/api/revalidate-blog \
  -H "Content-Type: application/json" \
  -d '{"slug": "my-new-post"}'

NOTE

A first-party webhook integration (where Vlozi calls your revalidation endpoint automatically on publish) is on the SDK roadmap. Until it ships, the curl/manual approach above is the recommended pattern.

Setting revalidate on pages

If you use time-based revalidation instead of (or in addition to) on-demand revalidation, export revalidate from your page:

// app/blog/[slug]/page.tsx
 
// Re-generate each post at most once per hour
export const revalidate = 3600;
 
// Or: always serve the cached version but regenerate in background
export const revalidate = "force-cache";

For most blogs, on-demand revalidation is better — pages only rebuild when content actually changes, not on a fixed schedule.

Complete setup example

// lib/vlozi.ts — server-side client with ISR tags
import { VloziClient } from "@vlozi/blog";
import { VLOZI_BLOG_TAG } from "@vlozi/blog/next";
 
export const vlozi = new VloziClient({
  apiKey: process.env.VLOZI_API_KEY ?? "",
  baseUrl: process.env.VLOZI_BASE_URL ?? "",
  nextTags: [VLOZI_BLOG_TAG],
});
// app/blog/[slug]/page.tsx — static + per-post ISR tag
import { generateStaticParamsForPosts, generateMetadataForPost, vloziPostTag } from "@vlozi/blog/next";
import { BlogContent } from "@vlozi/blog/react";
import { vlozi } from "@/lib/vlozi";
import { notFound } from "next/navigation";
 
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 } }) {
  // Per-post ISR tag so revalidateVloziBlog({ slugs: [slug] }) targets only this page
  const post = await vlozi.blog.get(params.slug, {
    nextTags: [vloziPostTag(params.slug)],
  }).catch(() => null);
 
  if (!post) notFound();
 
  return (
    <article>
      <h1>{post.title}</h1>
      {post.content && <BlogContent html={post.content} />}
    </article>
  );
}
// app/api/revalidate-blog/route.ts — on-demand revalidation endpoint
import { NextResponse } from "next/server";
import { revalidateVloziBlog } from "@vlozi/blog/next";
 
export async function POST(request: Request) {
  const { slug } = await request.json().catch(() => ({}));
  await slug ? revalidateVloziBlog({ slugs: [slug] }) : revalidateVloziBlog();
  return NextResponse.json({ revalidated: true });
}

What each tag covers

Tag Revalidates
VLOZI_BLOG_TAG ("vlozi-blog") Everything — blog list pages + all post detail pages
vloziPostTag(slug) ("vlozi-post:<slug>") Only the single post detail page for that slug
Both tags on a detail page Revalidates individually (via slug) or globally (via VLOZI_BLOG_TAG)

Use VLOZI_BLOG_TAG alone for simple setups. Add vloziPostTag per-post when you want surgical revalidation (e.g., editing one post without revalidating all post pages).

Blog · Next.jsEdit on GitHub