Advanced

AI integration

Ready-made prompts for Cursor, Copilot, Claude, and ChatGPT to scaffold your blog.

Use these ready-made prompts with any AI coding assistant (Cursor, Copilot, Claude, ChatGPT) to integrate the Blog SDK into your website in seconds — with a design that matches your existing site.

How it works

  1. Copy a prompt below.
  2. Replace {{API_KEY}} with your API key from Settings → API Keys (the gateway URL is already set).
  3. Paste into your AI coding assistant within your project.
  4. The AI will analyze your codebase first, then generate blog pages that match your existing design.

TIP

Each prompt includes a "Analyze My Codebase First" step that instructs the AI to discover your design system, component library, layout patterns, and color scheme before writing any code. The generated blog pages look native to your app — same fonts, same colors, same spacing, same components.

Creates a complete blog with search, category/tag navigation, post grid with pagination, and SEO-optimized detail pages. The AI will use your app's existing components and design tokens.

## Task
 
Integrate the **Vlozi Blog SDK** (`@vlozi/blog`) into my React/Next.js application. Create a complete blog section with a list page, post detail page, category/tag navigation, and search functionality.
 
## STEP 1 — Analyze My Codebase First (MANDATORY)
 
Before writing ANY code, you MUST analyze my existing codebase and list your findings. Do not skip this step.
 
1. **Design system:** Check tailwind.config, globals.css, theme files, or CSS variables. List my color palette, fonts, border-radius tokens, and spacing scale.
2. **Component library:** Check if I use shadcn/ui, MUI, Chakra, Ant Design, or custom components. Look in /components/ui/ or /components/. List the exact components I have (Button, Input, Card, Badge, Skeleton, etc.).
3. **Layout pattern:** Check my existing pages. Identify the shared layout (header, footer, sidebar, container width, padding). The blog pages MUST use the exact same layout.
4. **Typography:** Note my font family, heading sizes, and text/muted colors.
5. **Routing:** Check if I use Next.js App Router or Pages Router.
6. **Dark mode:** Check if I have dark mode and how it's implemented.
 
**Output your findings before writing code** so I can verify you understood my codebase.
 
## STEP 2 — The four hard rules (violating any of these breaks the integration)
 
These are the most common ways to break the integration. Each rule prevents a real customer-reported failure mode.
 
### Rule 1 — Import `@vlozi/blog/styles.css` FIRST
 
```tsx
// app/layout.tsx
import "@vlozi/blog/styles.css";   // ← MUST be first
import "./globals.css";             // ← my own styles second

Importing your globals.css first means your global resets (Tailwind preflight, custom * { margin: 0 }, shadcn's text-base cascading) clobber the SDK's .vlz-content h1/h2/h3 defaults. Symptom: post bodies render as flat unformatted text — no heading sizes, no list markers, no spacing. This is the single most common integration failure.

Rule 2 — DO NOT add prose classes to <BlogContent>

The SDK ships its own prose baseline. Tailwind Typography's prose classes fight the SDK's. ❌ <article className="prose prose-lg">. ✅ <article>.

Rule 3 — DO NOT style bare <pre> or <iframe> globally

Mermaid is portal-mounted into the original <pre>. Scope your code-block styling: .vlz-content pre:not([data-vlz-hydrated]) { background: ... }.

Rule 4 — Use slug as the React key, not id

The Post type has NO id field. Always key={post.slug}.

STEP 3 — Design Rules

  • DO NOT use the SDK's pre-built BlogCard/BlogList components if I have my own card/list patterns. Instead, use the hooks (usePosts, usePost) and build the UI with MY existing components.
  • DO reuse my existing Button, Input, Badge, Card, Skeleton components for the blog UI.
  • DO match my existing page layout — same container width, same header/footer, same sidebar pattern if any.
  • DO match my color scheme by mapping my design tokens onto the SDK's five --vlz-* knobs under .vlz-content: --vlz-accent, --vlz-muted-fg, --vlz-border, --vlz-surface, --vlz-surface-hover.
  • DO match my typography, spacing, and border-radius exactly.
  • If I have dark mode, the blog pages must support it using the same mechanism. For mermaid diagrams, set mermaidTheme on <VloziProvider config={{ ... }}> to match (function form for dynamic theme like next-themes).
  • The blog must look like it was built by the same team that built the rest of my app.

SDK Installation

npm install @vlozi/blog

Peer dependency: react >= 18. No other dependencies needed.

Required Environment Variables

Add to .env.local:

NEXT_PUBLIC_VLOZI_API_KEY={{API_KEY}}
NEXT_PUBLIC_VLOZI_BASE_URL=https://api.vlozi.app

Complete TypeScript Types

interface VloziConfig {
  apiKey: string;
  baseUrl: string;
}
 
interface Post {
  title: string;
  slug: string;                  // canonical React key — Post type has NO id field
  excerpt: string;
  content?: string;              // HTML string — ONLY on single post (blog.get / usePost)
  publishedAt: string;           // ISO date string
  seoTitle?: string;
  seoDescription?: string;
  featuredImageUrl?: string | null;
  category?: Category | null;
  tags?: Tag[];
  author?: Author | null;        // Attribution, when configured
  readingTime?: number;          // Estimated minutes
}
 
interface Category {
  name: string;
  slug: string;
  postCount?: number;
}
 
interface Tag {
  name: string;
  slug: string;
  postCount?: number;
}
 
interface Author {
  name: string;
  slug?: string;         // URL-safe identifier (for /authors/:slug pages)
  avatarUrl?: string;    // Absolute URL to 1:1 avatar
  bio?: string;
  url?: string;          // Website or profile URL
}
 
interface ListParams {
  page?: number;                                  // default: 1
  limit?: number;                                 // default: 10, max: 100
  category?: string | string[];                   // slug, or array for OR-match
  tag?: string | string[];                        // slug, or array for OR-match
  search?: string;                                // case-insensitive title+excerpt search
  sort?: "publishedAt" | "title" | "createdAt";   // default: "publishedAt"
  order?: "asc" | "desc";                         // default: "desc"
}
 
interface PaginatedResponse<T> {
  data: T[];
  meta: { page: number; limit: number; total: number; totalPages: number; };
}
 
interface ArchiveGroup {
  year: number;
  month: number;       // 1–12
  monthName: string;   // "January", etc.
  posts: Post[];
  count: number;
}
 
interface ArchiveResult {
  groups: ArchiveGroup[]; // sorted newest first
  totalPosts: number;
}
 
interface NeighborsResult {
  previous: Post | null; // older adjacent post
  next:     Post | null; // newer adjacent post
}

SDK Exports

Core (from @vlozi/blog)

  • new VloziClient({ apiKey, baseUrl }) — creates the API client
  • All TypeScript types: Post, Category, Tag, Author, ListParams, PaginatedResponse, ArchiveGroup, ArchiveResult, NeighborsResult, VloziConfig

Client Methods (for server components or direct usage)

client.blog.list(params?: ListParams) → Promise<PaginatedResponse<Post>>
client.blog.get(slug: string) → Promise<Post>  // includes full HTML content
client.blog.categories.list() → Promise<{ data: Category[] }>
client.blog.tags.list() → Promise<{ data: Tag[] }>
client.blog.archive(options?: { maxPosts?: number }) → Promise<ArchiveResult>
client.blog.related(slug: string, options?: { limit?: number; pool?: number }) → Promise<Post[]>
client.blog.neighbors(slug: string, options?: { pool?: number }) → Promise<NeighborsResult>

React (from @vlozi/blog/react)

  • <VloziProvider client={client}> — React context provider (wraps blog pages)
  • useVlozi() — access the client instance directly (advanced: prefetch, imperative calls)

Hooks (must be inside VloziProvider)

usePosts(params?: ListParams) → { data, loading, error, refetch, page, totalPages, hasNextPage, hasPrevPage }
usePost(slug: string) → { data: Post | null, loading, error }
useCategories() → { data: Category[] | null, loading, error, refetch }
useTags() → { data: Tag[] | null, loading, error, refetch }
useArchive(options?: { maxPosts?: number }) → { data: ArchiveResult | null, loading, error, refetch }
useRelatedPosts(slug: string, options?: { limit?: number; pool?: number }) → { data: Post[] | null, loading, error }
useNeighbors(slug: string, options?: { pool?: number }) → { data: NeighborsResult | null, loading, error }

Pre-Built Components (use ONLY if you don't have your own components)

BlogList — Paginated post grid

Props: limit?, columns? (1|2|3|4), variant? ("grid"|"list"), category?, tag?, showPagination?, renderItem?, renderLoading?, renderEmpty?, renderError?, className?
⚠️ BlogList does NOT accept a search prop. For search, use usePosts hook directly.

BlogInfiniteList — Paginated post grid with IntersectionObserver-based auto-load

Props: limit?, columns? (1|2|3|4), category?, tag?, search?, sort?, order?, rootMargin? (default "400px"), prefetchOnHover?, imageComponent?, renderItem?, renderLoading?, renderEmpty?, renderError?, className?

BlogCard — Individual post card

Props: post (required), variant? ("default"|"featured"|"compact"), onClick?, renderMeta?, footer?, className?

BlogPost — Full article renderer

Props: slug (required), showFeaturedImage?, renderTitle?, className?

BlogCategoryNav — Category navigation

Props: variant? ("sidebar"|"tabs"|"pills"), activeCategory?, onSelect? (receives slug or null), showCounts?, className?

BlogTagNav — Tag navigation

Props: variant? ("pills"|"cloud"), activeTag?, onSelect? (receives slug or null), showCounts?, className?

BlogArchive — All posts grouped by year/month

Props: variant? ("grouped"|"flat"), maxPosts? (default 1000), postHref?, renderGroup?, renderLoading?, renderEmpty?, renderError?, className?

RelatedPosts — "You might also like…" block under a post (fail-silent by default)

Props: slug (required), limit? (default 3), pool? (default 100), columns?, heading?, renderItem?, imageComponent?, prefetchOnHover?, renderLoading?, renderError?, renderEmpty?, className?

PrevNextNav — Previous/next navigation for a single post

Props: slug (required), pool? (default 500), postHref?, renderLink?, renderLoading?, hideWhenEmpty? (default true), className?

Content Styles

Import @vlozi/blog/styles.css in your blog layout for styled HTML content (code blocks, tables, blockquotes, callouts, etc). Uses CSS custom properties so it adapts to your theme.

Files to Create

1. Blog Provider (client component)

"use client";
import { VloziClient } from "@vlozi/blog";
import { VloziProvider } from "@vlozi/blog/react";
import { useMemo, ReactNode } from "react";
 
export function BlogProvider({ children }: { children: ReactNode }) {
  const client = useMemo(() => new VloziClient({
    apiKey: process.env.NEXT_PUBLIC_VLOZI_API_KEY!,
    baseUrl: process.env.NEXT_PUBLIC_VLOZI_BASE_URL!,
  }), []);
  return <VloziProvider client={client}>{children}</VloziProvider>;
}

2. Blog Layout (wraps all blog pages)

  • Create a layout.tsx that wraps children with the BlogProvider
  • Use the SAME header/footer/nav as the rest of the app
  • Import @vlozi/blog/styles.css here

3. Blog List Page (/blog) — "use client"

  • Search bar (debounced 300ms) — use my app's Input component
  • Category nav — use BlogCategoryNav or my app's own tab/pill component
  • Tag cloud — use BlogTagNav or my app's Badge component
  • Post grid with pagination
  • Loading state using my app's Skeleton component
  • IMPORTANT: Since BlogList does NOT support search, use the usePosts hook directly:
    const { data, loading, error, totalPages, hasNextPage, hasPrevPage } = usePosts({
      page, limit: 12, category, tag, search: debouncedSearch, sort: "publishedAt", order: "desc"
    });
    Then render the grid manually with data.data.map(post => ...).

4. Post Detail Page (/blog/[slug])

  • SEO metadata via generateMetadata using server-side VloziClient
  • Full article via <BlogPost slug={slug} showFeaturedImage /> or via usePost hook with custom UI
  • Category badge + tag pills using my app's Badge component
  • Breadcrumbs if my app uses them elsewhere
  • Must render inside BlogProvider

CRITICAL RULES

  1. All hooks and components MUST be inside <VloziProvider> — they throw if used outside
  2. Provider must be "use client" — it uses React Context
  3. post.content is HTML — render with dangerouslySetInnerHTML (use DOMPurify in production)
  4. Import @vlozi/blog/styles.css for styled content
  5. Environment vars must be NEXT_PUBLIC_ prefixed for client components
  6. Debounce search input (300ms); pass undefined not empty string to the search param
  7. onSelect in BlogCategoryNav/BlogTagNav receives null for "All" — convert: (slug) => setState(slug ?? undefined)
  8. content field only exists on single post (blog.get / usePost), NOT in list responses
  9. BlogList does NOT support search — use usePosts hook directly for search functionality

Now analyze my codebase first (output your findings), then implement the blog integration that looks native to my app.

 
## Minimal blog (2 pages)
 
Creates just a list page and detail page — no search or filtering. The AI will still match your existing design.
 
```md
Install @vlozi/blog and create:
1. /blog page — Post grid with pagination
2. /blog/[slug] page — Full post with SEO via generateMetadata
 
## STEP 1 — Analyze my codebase first (MANDATORY, output findings before coding):
- Layout: Find my header, footer, container width, and page padding. Blog MUST use the same layout.
- Components: Find my component library (shadcn/ui? MUI? custom?). Use MY Card, Button, Badge, Skeleton — don't create new ones.
- Theme: Find my colors, fonts, spacing, border-radius. Match them exactly.
- Dark mode: If I have it, support it.
 
## STEP 2 — Setup:

npm install @vlozi/blog

 
Env vars (.env.local):
NEXT_PUBLIC_VLOZI_API_KEY={{API_KEY}}
NEXT_PUBLIC_VLOZI_BASE_URL=https://api.vlozi.app
 
Provider (client component):
```tsx
const client = useMemo(() => new VloziClient({
  apiKey: process.env.NEXT_PUBLIC_VLOZI_API_KEY!,
  baseUrl: process.env.NEXT_PUBLIC_VLOZI_BASE_URL!,
}), []);

Wrap blog pages with . Import @vlozi/blog/styles.css in the blog layout.

STEP 3 — Build:

  • Use usePosts(params) hook for the list page and usePost(slug) for the detail page.
  • Build UI with MY existing components — don't use the SDK's pre-built BlogCard/BlogList unless I have no own components.
  • post.content (HTML) only exists on usePost, not in list. Render with dangerouslySetInnerHTML.
  • For SEO: use generateMetadata with a server-side VloziClient instance.
  • All hooks must be inside VloziProvider and in "use client" components.
 
## Blog widget (embed on any page)
 
Adds a "Latest Posts" section to an existing page. The AI will match your page's existing card and section styles.
 
```md
Add a "Latest Blog Posts" section to my homepage using @vlozi/blog.
 
## STEP 1 — Analyze my homepage first (MANDATORY, output findings before coding):
- Find the card style, section headings, spacing, typography, and colors already used on this page
- Find my existing Card, Badge, and Button components — use them, don't create new ones
- Find my container width and grid patterns
 
## STEP 2 — Setup:
```bash
npm install @vlozi/blog

Env vars (.env.local): NEXT_PUBLIC_VLOZI_API_KEY={{API_KEY}} NEXT_PUBLIC_VLOZI_BASE_URL=https://api.vlozi.app

STEP 3 — Build:

Wrap the section with where client is:

const client = useMemo(() => new VloziClient({
  apiKey: process.env.NEXT_PUBLIC_VLOZI_API_KEY!,
  baseUrl: process.env.NEXT_PUBLIC_VLOZI_BASE_URL!,
}), []);

Show 3 most recent posts in a responsive horizontal row using the usePosts hook:

const { data, loading } = usePosts({ limit: 3, sort: "publishedAt", order: "desc" });

Each card: featured image, category badge, title, excerpt (2-line clamp), date, reading time. Link each card to /blog/{post.slug}. Add a "View All Posts →" link to /blog. Must be "use client" since hooks require VloziProvider context.

 
## Customize your prompt
 
Append any of these to the prompts above to add specific features:
 
- **Custom cards:** `Don't use BlogCard. Build custom cards using usePosts hook and my existing Card component.`
- **SSG:** `Generate all blog pages statically using generateStaticParams.`
- **Dark mode:** `Ensure blog pages work in both light and dark mode using the same theme toggle as the rest of my app.`
- **Headless:** `Use only hooks (usePosts, usePost, useCategories, useTags). Build all UI from scratch using my component library.`
- **Category pages:** `Also create /blog/category/[slug] pages that filter posts by category.`
- **Raw API (non-React):** `Don't use the React SDK. Use raw fetch calls to /blog/public/posts, /blog/public/categories, /blog/public/tags with Authorization: Bearer header.`
- **Animations:** `Add smooth page transitions and card hover animations matching the style used elsewhere in my app.`
- **Breadcrumbs:** `Add breadcrumb navigation (Home → Blog → Post Title) using my app's existing breadcrumb component.`
 
## Troubleshooting
 
If the AI generates code with errors, paste this follow-up:
 
```md
Fix the Vlozi integration:
- "useVlozi must be used within a VloziProvider" → Wrap with <VloziProvider>
- "apiKey/baseUrl is required" → new VloziClient({ apiKey: process.env.NEXT_PUBLIC_VLOZI_API_KEY!, baseUrl: process.env.NEXT_PUBLIC_VLOZI_BASE_URL! })
- Content not styled → import "@vlozi/blog/styles.css"
- post.content undefined → content only exists on usePost/blog.get, not in list responses
- onSelect receives null → convert: (slug) => setState(slug ?? undefined)
- Search fires too fast → debounce 300ms, pass undefined not ""
- Search not working with BlogList → BlogList does NOT accept a search prop. Use the usePosts hook directly with { search: debouncedSearch } and render the grid manually
- Blog doesn't match my design → Analyze my tailwind.config / globals.css / components/ folder and use the SAME colors, fonts, spacing, border-radius, and components as the rest of my app
Blog · AdvancedEdit on GitHub