@autoblogwriter/sdk
v3.1.8
Published
Official AutoBlogWriter SDK for fetching posts, building sitemaps, rendering helpers, and revalidation utilities.
Readme
@autoblogwriter/sdk
Official TypeScript SDK for AutoBlogWriter. It keeps server-side integration tiny, adds helpers for Next.js App Router and Vite/React apps, and ships revalidation utilities that respect AutoBlogWriter's API contract.
Installation
npm install @autoblogwriter/sdkRequires Node.js 18+ and native
fetch. Bundles ship as ESM, CJS, and types viatsup.
v3 Migration (Breaking Changes)
Version 3.0.0 removes legacy BlogAuto* symbols and legacy webhook/env identifiers. There are no compatibility aliases in v3.
API Rename Map
| Old (removed) | New |
| --- | --- |
| createBlogAutoClient | createAutoBlogWriterClient |
| BlogAutoClient | AutoBlogWriterClient |
| BlogAutoError | AutoBlogWriterError |
| createBlogAutoFromEnv | createAutoBlogWriterFromEnv |
| BlogAutoEnvConfig | AutoBlogWriterEnvConfig |
| BlogAutoClientConfig | AutoBlogWriterClientConfig |
| BlogAutoRevalidatePayload | AutoBlogWriterRevalidatePayload |
Wire/Config Identifier Changes
| Old (removed) | New |
| --- | --- |
| BLOGAUTO_API_KEY | AUTOBLOGWRITER_API_KEY |
| BLOGAUTO_WORKSPACE_SLUG | AUTOBLOGWRITER_WORKSPACE_SLUG |
| BLOGAUTO_API_URL | AUTOBLOGWRITER_API_URL |
| BLOGAUTO_WORKSPACE_ID | AUTOBLOGWRITER_WORKSPACE_ID |
| BLOGAUTO_REVALIDATE_SECRET | AUTOBLOGWRITER_REVALIDATE_SECRET |
| BLOGAUTO_DEBUG | AUTOBLOGWRITER_DEBUG |
| x-autoblogwriter-signature | x-autoblogwriter-signature |
| autoblogwriter:{workspaceSlug}:... | autoblogwriter:{workspaceSlug}:... |
Entry Points
// Core client, types, and utilities
import { createAutoBlogWriterClient } from "@autoblogwriter/sdk";
// Next.js App Router helpers (server-side)
import { fetchBlogPosts, fetchBlogPost } from "@autoblogwriter/sdk/next";
// Framework-agnostic helpers (Remix, Astro, Express, …)
import { fetchPosts, fetchPost } from "@autoblogwriter/sdk/helpers";
// React components
import { BlogFaq, BlogPost, BlogPostList, Markdown, Branding, AutoBlogPost, AutoBlogPostList } from "@autoblogwriter/sdk/react";
// Webhook signature verification (server-side)
import { verifyWebhookSignature } from "@autoblogwriter/sdk/revalidate";
// Default styles
import "@autoblogwriter/sdk/styles.css";| Entry Point | Environment | Description |
| --- | --- | --- |
| @autoblogwriter/sdk | Server | Core client, markdown renderer, sitemap/robots builders |
| @autoblogwriter/sdk/next | Server (Next.js) | fetchBlogPosts, fetchBlogPost, generatePostMetadata, generateBlogSitemap, generateBlogRobots, createEnvRevalidateHandler — graceful on errors |
| @autoblogwriter/sdk/helpers | Server | fetchPosts, fetchPost, buildBlogSitemap, buildBlogRobots — framework-agnostic, always graceful |
| @autoblogwriter/sdk/react | Client or Server | <BlogPost>, <BlogPostList>, <BlogFaq>, <Markdown>, <Branding> components; <AutoBlogPost>, <AutoBlogPostList> async Server Components |
| @autoblogwriter/sdk/revalidate | Server | createRevalidateRouteHandler, verifyWebhookSignature |
| @autoblogwriter/sdk/styles.css | Client | Default dark-theme styles for all components |
Quick Start (Next.js App Router)
- Install the package:
npm install @autoblogwriter/sdk- Configure environment variables:
AUTOBLOGWRITER_API_KEY=ba_pk_your_api_key
AUTOBLOGWRITER_WORKSPACE_SLUG=your-workspace
AUTOBLOGWRITER_REVALIDATE_SECRET=your_webhook_secret- Fetch and render posts:
import { fetchBlogPosts } from "@autoblogwriter/sdk/next";
import { BlogPostList } from "@autoblogwriter/sdk/react";
export default async function BlogPage() {
const { posts } = await fetchBlogPosts({ limit: 20 });
return <BlogPostList posts={posts} />;
}Quick Start (Other Frameworks — Remix / Astro / Express)
Install and set the same environment variables as above.
Fetch and render posts:
import { fetchPosts, fetchPost } from "@autoblogwriter/sdk/helpers";
// List all posts
const { posts } = await fetchPosts({ limit: 20 });
// Single post — returns null if not found (never throws)
const post = await fetchPost("my-post-slug");
if (!post) {
// handle 404 in your framework
}- Generate a sitemap and robots config:
import { buildBlogSitemap, buildBlogRobots } from "@autoblogwriter/sdk/helpers";
// Plain objects — no Next.js dependency
const sitemapEntries = await buildBlogSitemap();
const robotsConfig = buildBlogRobots();Security Notes (Read First)
- Never ship AutoBlogWriter API keys in public, client-side code.
- Run the SDK inside Next.js server components, route handlers, or your own backend proxy.
- If you must expose functionality to the browser, issue scoped public tokens or proxy through your server. Do not mint new auth schemes.
Client Configuration
import { createAutoBlogWriterClient } from "@autoblogwriter/sdk";
const client = createAutoBlogWriterClient({
apiUrl: "https://api.autoblogwriter.app",
apiKey: process.env.AUTOBLOGWRITER_API_KEY!,
workspaceId: process.env.AUTOBLOGWRITER_WORKSPACE_ID,
workspaceSlug: process.env.AUTOBLOGWRITER_WORKSPACE_SLUG,
authMode: "bearer", // or "x-api-key"
headers: { "x-trace-id": "..." },
timeoutMs: 10_000,
fetch: globalThis.fetch, // optional custom fetch
});| Option | Type | Required | Description |
| --- | --- | --- | --- |
| apiUrl | string | yes | Root API URL, e.g. https://api.autoblogwriter.app. Trailing slash removed automatically. |
| apiKey | string | yes | Workspace-scoped API key created in the AutoBlogWriter dashboard. |
| workspaceId | string | one of workspaceId or workspaceSlug | Optional; used for account scoping. |
| workspaceSlug | string | one of workspaceId or workspaceSlug | Required for public content APIs (/v1/public/:workspaceSlug/...). |
| authMode | "bearer" \| "x-api-key" | no (default "bearer") | Sends the API key via Authorization: Bearer or x-api-key. |
| headers | Record<string, string> | no | Additional static headers (merged on each request). |
| fetch | typeof fetch | no | Provide a custom fetch implementation (e.g., for SSR polyfills). |
| timeoutMs | number | no | Optional request timeout enforced client-side. |
Next.js Helper Functions (@autoblogwriter/sdk/next)
The next entry point provides high-level helpers that read configuration from environment variables automatically (via createAutoBlogWriterFromEnv). Set these env vars and the helpers handle client creation, cache tagging, and metadata for you.
Graceful errors: All helpers log a
console.warnand return safe defaults on config or API errors instead of throwing.fetchBlogPostcalls Next.jsnotFound()on all error types so pages always show a 404 rather than crashing.
| Env Variable | Required | Description |
| --- | --- | --- |
| AUTOBLOGWRITER_API_KEY | yes | Workspace API key |
| AUTOBLOGWRITER_WORKSPACE_SLUG | yes | Workspace slug for content APIs |
| AUTOBLOGWRITER_API_URL | no | Defaults to https://api.autoblogwriter.app |
| AUTOBLOGWRITER_WORKSPACE_ID | no | Optional workspace ID |
| SITE_URL or NEXT_PUBLIC_SITE_URL | no | Defaults to http://localhost:3000 |
| AUTOBLOGWRITER_REVALIDATE_SECRET | no | Required for the revalidation handler |
fetchBlogPosts(options?)
Fetches a paginated list of posts with automatic cache tagging.
import { fetchBlogPosts } from "@autoblogwriter/sdk/next";
const { posts, nextCursor } = await fetchBlogPosts({ limit: 20 });fetchBlogPost(slug)
Fetches a single post by slug. Automatically calls notFound() if the post doesn't exist.
import { fetchBlogPost } from "@autoblogwriter/sdk/next";
const post = await fetchBlogPost(params.slug);generatePostMetadata(slug)
Returns a Next.js Metadata object (title, description, Open Graph, Twitter cards) for a blog post. Use it in your generateMetadata export.
import { generatePostMetadata } from "@autoblogwriter/sdk/next";
export async function generateMetadata({ params }: Props) {
return generatePostMetadata(params.slug);
}buildJsonLdScript(post)
Generates a <script type="application/ld+json"> element for manual injection in <head>. Available from the core @autoblogwriter/sdk entry point.
Note:
<BlogPost>automatically injects JSON-LD whenpost.metadata.jsonLdis present —buildJsonLdScript()is only needed for manual<head>injection viagenerateMetadata.
import { buildJsonLdScript } from "@autoblogwriter/sdk";
export async function generateMetadata({ params }: Props) {
const post = await client.getPostBySlug(params.slug);
if (!post) return {};
const jsonLdScript = buildJsonLdScript(post); // null if no JSON-LD
return {
...buildNextMetadata(post),
...(jsonLdScript ? { other: { scripts: [jsonLdScript] } } : {}),
};
}generateBlogSitemap()
Returns a MetadataRoute.Sitemap array for app/sitemap.ts.
import { generateBlogSitemap } from "@autoblogwriter/sdk/next";
export default async function sitemap() {
return generateBlogSitemap();
}generateBlogRobots()
Returns a MetadataRoute.Robots object for app/robots.ts.
import { generateBlogRobots } from "@autoblogwriter/sdk/next";
export default function robots() {
return generateBlogRobots();
}createEnvRevalidateHandler()
Creates a POST route handler for webhook-based revalidation. Reads AUTOBLOGWRITER_REVALIDATE_SECRET from env, verifies the HMAC signature, and calls revalidatePath/revalidateTag.
// app/api/autoblogwriter/revalidate/route.ts
import { createEnvRevalidateHandler } from "@autoblogwriter/sdk/next";
export const POST = createEnvRevalidateHandler();Full Next.js Example
// app/blog/page.tsx
import { fetchBlogPosts } from "@autoblogwriter/sdk/next";
import { BlogPostList } from "@autoblogwriter/sdk/react";
export default async function BlogIndexPage() {
const { posts } = await fetchBlogPosts({ limit: 20 });
return <BlogPostList posts={posts} />;
}// app/blog/[slug]/page.tsx
import { fetchBlogPost, generatePostMetadata } from "@autoblogwriter/sdk/next";
import { BlogPost } from "@autoblogwriter/sdk/react";
interface Props {
params: { slug: string };
}
export async function generateMetadata({ params }: Props) {
return generatePostMetadata(params.slug);
}
export default async function BlogPostPage({ params }: Props) {
const post = await fetchBlogPost(params.slug);
return <BlogPost post={post} />;
}Framework-Agnostic Helper Functions (@autoblogwriter/sdk/helpers)
The helpers entry point provides the same env-based convenience wrappers as ./next but with no Next.js dependency — safe to import in Remix, Astro, Express, Vite SSR, or any other Node.js server environment.
Uses the same env vars as the Next.js helpers (AUTOBLOGWRITER_API_KEY, AUTOBLOGWRITER_WORKSPACE_SLUG, etc.)
Always graceful: Every function catches all errors and returns safe defaults — it never throws.
fetchPostreturnsnullinstead of callingnotFound()so your framework's 404 handling stays in your hands. No ISR cache tags are applied — use./nextif you need tag-based revalidation.
| Function | Returns on error |
| --- | --- |
| fetchPosts(options?) | { posts: [], nextCursor: undefined, pagination: zeroed } |
| fetchPost(slug) | null |
| buildBlogSitemap() | [] |
| buildBlogRobots() | { rules: [{ userAgent: "*", allow: "/" }] } |
| buildJsonLdScript | re-exported from ./metadata |
Also exports FetchPostsOptions type.
fetchPosts(options?)
import { fetchPosts } from "@autoblogwriter/sdk/helpers";
const { posts, nextCursor } = await fetchPosts({ limit: 20 });fetchPost(slug)
import { fetchPost } from "@autoblogwriter/sdk/helpers";
const post = await fetchPost("my-post-slug");
if (!post) {
// show 404 in your framework
}buildBlogSitemap()
import { buildBlogSitemap } from "@autoblogwriter/sdk/helpers";
const entries = await buildBlogSitemap();
// [{ url: "https://yoursite.com/blog/slug", lastModified: "..." }, ...]buildBlogRobots()
import { buildBlogRobots } from "@autoblogwriter/sdk/helpers";
const robots = buildBlogRobots();
// { rules: [{ userAgent: "*", allow: "/" }], sitemap: "https://yoursite.com/sitemap.xml" }React Components (@autoblogwriter/sdk/react)
Pre-built components for rendering blog content. Works in Next.js, Vite, or any React 18+ app.
<Markdown>
Renders a markdown string to HTML using the built-in renderer.
import { Markdown } from "@autoblogwriter/sdk/react";
<Markdown source={post.content} />
<Markdown source={post.content} className="custom-markdown" />| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| source | string \| null | — | Markdown string to render. Returns null if empty. |
| className | string | "ba-markdown" | CSS class on the wrapper <div>. |
<BlogPost>
Renders a full blog post with title, date, reading time, and markdown content.
import { BlogPost } from "@autoblogwriter/sdk/react";
<BlogPost post={post} />
<BlogPost post={post} showDate={false} />
<BlogPost post={post} theme="light" />
<BlogPost post={post} showHeroImage />
<BlogPost
post={post}
linkComponent={Link}
imageComponent={Image}
imageProps={{ width: 1200, height: 630 }}
/>
<BlogPost post={post} renderContent={(md) => <MyCustomRenderer source={md} />} />| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| post | BlogPost | — | The post object from the SDK. |
| showTitle | boolean | true | Show the post title as an <h1>. |
| showDate | boolean | true | Show the published/updated date. |
| showFaq | boolean | true | Render FAQ after content when post.faq exists. |
| showRelatedPosts | boolean | true | Render related posts when post.relatedPosts exists. |
| className | string | "ba-post" | CSS class on the <article>. |
| theme | "light" \| "dark" | — | Applies a theme via data-ba-theme on the wrapper. |
| showHeroImage | boolean | false | Render post.images?.hero?.url under the title when available. |
| heroImageAlt | string | post.title | Optional alt text for the hero image. |
| linkComponent | React.ComponentType | — | Custom link component for related posts (e.g. Next.js Link). |
| imageComponent | React.ComponentType | — | Custom image component for the hero image (e.g. Next.js Image). |
| imageProps | Record<string, unknown> | — | Extra props forwarded to the hero image component. |
| renderContent | (content: string) => ReactNode | — | Override the default <Markdown> renderer. |
| renderFaq | (faq, post) => ReactNode | — | Override FAQ rendering, or return null to hide it. |
| renderRelatedPosts | (posts, post) => ReactNode | — | Override related posts rendering, or return null to hide it. |
| branding | BrandingInfo | from API | Explicit branding tier info. Overrides post.branding. |
| showBranding | boolean | true | Show/hide the "Powered by" footer. On read-only workspaces this is forced true. |
| brandingLinkComponent | React.ComponentType | linkComponent | Link component used specifically for the branding link. |
<BlogFaq>
Renders an FAQ section from post FAQ data.
import { BlogFaq } from "@autoblogwriter/sdk/react";
<BlogFaq faq={post.faq!} />| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| faq | BlogFaq \| BlogFaqItem[] | — | FAQ data to render. |
| title | string | "Frequently Asked Questions" | Section heading text. |
| className | string | "ba-faq" | CSS class on the FAQ section. |
<RelatedPosts>
Renders a related-posts list from post.relatedPosts.
import { RelatedPosts } from "@autoblogwriter/sdk/react";
<RelatedPosts posts={post.relatedPosts ?? []} />
<RelatedPosts posts={post.relatedPosts ?? []} title="Keep reading" routePrefix="/articles" />| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| posts | RelatedPostSummary[] | — | Related post summaries to render. |
| title | string | "Related Posts" | Section heading text. |
| routePrefix | string | "/blog" | URL prefix for links ({routePrefix}/{slug}). |
| linkComponent | React.ComponentType | <a> | Custom link component (e.g. Next.js Link). |
| className | string | "ba-related" | CSS class on the section wrapper. |
| renderItem | (post, href) => ReactNode | — | Override default item rendering. |
<BlogPostList>
Renders a list of post cards with titles, dates, and excerpts.
import { BlogPostList } from "@autoblogwriter/sdk/react";
<BlogPostList posts={posts} />
<BlogPostList posts={posts} title="Blog" routePrefix="/articles" />
<BlogPostList posts={posts} theme="light" layout="list" />
<BlogPostList posts={posts} imageComponent={Image} imageProps={{ width: 1200, height: 675 }} />With Next.js <Link>:
import Link from "next/link";
<BlogPostList posts={posts} linkComponent={Link} />With a custom card:
<BlogPostList
posts={posts}
renderCard={(post, href) => (
<a href={href}>{post.title} — {post.excerpt}</a>
)}
/>With controlled pagination:
<BlogPostList
posts={posts}
postsPerPage={6}
page={page}
onPageChange={setPage}
/>Server-friendly pagination with links:
<BlogPostList
posts={posts}
postsPerPage={6}
page={page}
paginationHref={(nextPage) => `/blog?page=${nextPage}`}
/>Next.js server component example (no client state):
import Link from "next/link";
import { fetchBlogPosts } from "@autoblogwriter/sdk/next";
import { BlogPostList } from "@autoblogwriter/sdk/react";
type Props = {
searchParams?: { page?: string };
};
export default async function BlogPage({ searchParams }: Props) {
const { posts } = await fetchBlogPosts();
const page = Math.max(1, Number(searchParams?.page ?? "1"));
return (
<BlogPostList
posts={posts}
linkComponent={Link}
layout="card"
postsPerPage={6}
page={page}
paginationHref={(nextPage) => `/blog?page=${nextPage}`}
/>
);
}| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| posts | BlogPost[] | — | Array of post objects. |
| title | string | "Latest posts" | Section heading. Pass empty string to hide. |
| routePrefix | string | "/blog" | URL prefix for post links ({routePrefix}/{slug}). |
| linkComponent | React.ComponentType | <a> | Custom link component (e.g. Next.js Link). |
| className | string | "ba-listing" | CSS class on the <section>. |
| theme | "light" \| "dark" | — | Applies a theme via data-ba-theme on the wrapper. |
| layout | "card" \| "list" | "card" | Layout style for the list. |
| postsPerPage | number | — | Page size for controlled pagination. |
| page | number | 1 | Current page (1-based) for controlled pagination. |
| onPageChange | (page: number) => void | — | Handler invoked by pagination buttons. |
| paginationHref | (page: number) => string | — | Render pagination links instead of buttons (server-safe). |
| showPagination | boolean | true (when postsPerPage is set) | Whether to render pagination UI. |
| imageComponent | React.ComponentType | — | Custom image component for card images (e.g. Next.js Image). |
| imageProps | Record<string, unknown> | — | Extra props forwarded to the card image component. |
| renderCard | (post, href) => ReactNode | — | Override the default card rendering. |
| branding | BrandingInfo | inferred from posts | Explicit branding tier info. Inferred from posts[0].branding if not set. |
| showBranding | boolean | true | Show/hide the "Powered by" footer. On read-only workspaces this is forced true. |
| brandingLinkComponent | React.ComponentType | linkComponent | Link component used specifically for the branding link. |
<Branding>
A standalone "Powered by AutoBlogWriter" attribution component. Rendered automatically inside <BlogPost> and <BlogPostList> when branding is required, but can also be used directly.
import { Branding } from "@autoblogwriter/sdk/react";
<Branding />
<Branding linkComponent={Link} showLabel={false} />| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| className | string | "ba-branding" | CSS class on the wrapper element. |
| linkComponent | React.ComponentType | <a> | Custom link component for the attribution link. |
| href | string | AutoBlogWriter site URL | Override the attribution link target. |
| label | string | "AutoBlogWriter" | Override the brand name text. |
| showLabel | boolean | true | Show the "Powered by" label text. |
Paid plans: Branding can be disabled on active paid plans. Read-only workspaces may still enforce attribution.
<AutoBlogPost>
Async React Server Component that fetches and renders a single post in one JSX call.
import { AutoBlogPost } from "@autoblogwriter/sdk/react";
// Basic — fetches using env config
export default async function PostPage({ params }: Props) {
const { slug } = await params;
return <AutoBlogPost slug={slug} />;
}
// With a custom client
import { createAutoBlogWriterClient } from "@autoblogwriter/sdk";
const client = createAutoBlogWriterClient({ apiUrl: "...", apiKey: "..." });
return <AutoBlogPost slug={slug} client={client} showHeroImage />;
// With a custom fetcher
return <AutoBlogPost slug={slug} fetchPost={myFetchPost} />;
// With a pre-fetched post
const post = await fetchBlogPost(slug);
return <AutoBlogPost slug={slug} post={post} />;| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| slug | string | — | (Required) The post slug to fetch. |
| post | BlogPost | — | Optional pre-fetched post. Skips fetch if provided. |
| fetchPost | (slug: string) => Promise<BlogPost \| null> | — | Custom fetch function. Overrides the default client. |
| client | AutoBlogWriterClient | env client | Custom client instance. |
| ...BlogPostProps | — | — | All <BlogPost> props are inherited. |
<AutoBlogPostList>
Async React Server Component that fetches and renders a list of posts in one JSX call.
import { AutoBlogPostList } from "@autoblogwriter/sdk/react";
// Basic
export default async function BlogPage() {
return <AutoBlogPostList />;
}
// With limit and category
return <AutoBlogPostList limit={6} category="engineering" layout="card" />;
// With pre-fetched posts
const { posts } = await fetchBlogPosts({ limit: 20 });
return <AutoBlogPostList posts={posts} layout="list" />;| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| posts | BlogPost[] | — | Optional pre-fetched posts. Skips fetch if provided. |
| fetchPosts | () => Promise<PostsResponse> | — | Custom fetch function. Overrides the default client. |
| client | AutoBlogWriterClient | env client | Custom client instance. |
| limit | number | — | Maximum number of posts to fetch. |
| cursor | string | — | Pagination cursor. |
| category | string | — | Filter posts by category slug. |
| ...BlogPostListProps | — | — | All <BlogPostList> props are inherited. |
Styles (@autoblogwriter/sdk/styles.css)
The SDK ships a default dark-theme stylesheet that styles all the React components out of the box. Import it in your app entry point:
// app/layout.tsx or src/main.tsx
import "@autoblogwriter/sdk/styles.css";All values are driven by CSS custom properties on :root, so you can override them to match your theme:
:root {
--ba-color-bg: #0c0d10;
--ba-color-text: #f2f4f8;
--ba-color-text-muted: rgba(242, 244, 248, 0.6);
--ba-color-link: #6cf;
--ba-color-link-hover: #8df;
--ba-color-border: rgba(255, 255, 255, 0.08);
--ba-color-card-bg: rgba(255, 255, 255, 0.02);
--ba-color-card-bg-hover: rgba(255, 255, 255, 0.04);
--ba-color-code-bg: rgba(255, 255, 255, 0.1);
--ba-color-pre-bg: rgba(255, 255, 255, 0.05);
--ba-radius: 8px;
--ba-max-width: 720px;
--ba-font-sans: system-ui, -apple-system, sans-serif;
--ba-font-mono: ui-monospace, SFMono-Regular, monospace;
}For a light theme, override the color variables:
[data-ba-theme="light"] {
--ba-color-bg: #ffffff;
--ba-color-text: #0c0d10;
--ba-color-text-muted: rgba(12, 13, 16, 0.6);
--ba-color-text-secondary: rgba(12, 13, 16, 0.8);
--ba-color-text-content: rgba(12, 13, 16, 0.9);
--ba-color-link: #0a66c2;
--ba-color-link-hover: #0c74db;
--ba-color-border: rgba(12, 13, 16, 0.12);
--ba-color-card-bg: rgba(12, 13, 16, 0.03);
--ba-color-card-bg-hover: rgba(12, 13, 16, 0.06);
--ba-color-code-bg: rgba(12, 13, 16, 0.08);
--ba-color-pre-bg: rgba(12, 13, 16, 0.04);
}CSS Class Reference
| Class | Used by | Description |
| --- | --- | --- |
| .ba-listing | <BlogPostList> | Outer section wrapper |
| .ba-listing-title | <BlogPostList> | Section heading |
| .ba-posts | <BlogPostList> | Flex column container for cards |
| .ba-posts--card | <BlogPostList> | Card layout modifier |
| .ba-posts--list | <BlogPostList> | List layout modifier |
| .ba-post-card | <BlogPostList> | Individual post card |
| .ba-post-card-title | <BlogPostList> | Card title |
| .ba-post-card-link | <BlogPostList> | Title link |
| .ba-post-card-meta | <BlogPostList> | Card date |
| .ba-post-card-excerpt | <BlogPostList> | Card excerpt |
| .ba-pagination | <BlogPostList> | Pagination wrapper |
| .ba-pagination-btn | <BlogPostList> | Pagination button |
| .ba-pagination-text | <BlogPostList> | Pagination text |
| .ba-post | <BlogPost> | Article wrapper |
| .ba-post-title | <BlogPost> | Post title |
| .ba-post-hero | <BlogPost> | Post hero image |
| .ba-post-meta | <BlogPost> | Date and reading time |
| .ba-related | <RelatedPosts> | Related posts section wrapper |
| .ba-related-title | <RelatedPosts> | Related posts heading |
| .ba-related-list | <RelatedPosts> | Related posts list |
| .ba-related-item | <RelatedPosts> | Related posts item |
| .ba-related-link | <RelatedPosts> | Related posts link |
| .ba-markdown | <Markdown> | Markdown content wrapper |
| .ba-branding | <Branding> | Attribution footer wrapper |
| .ba-branding-link | <Branding> | Attribution anchor/link |
| .ba-branding-label | <Branding> | "Powered by" label text |
Revalidation
When AutoBlogWriter publishes a post it can ping your Next.js site so cached pages refresh immediately.
Quick setup (env-based, one line):
// app/api/autoblogwriter/revalidate/route.ts
import { createEnvRevalidateHandler } from "@autoblogwriter/sdk/next";
export const POST = createEnvRevalidateHandler();Set AUTOBLOGWRITER_REVALIDATE_SECRET in your environment and configure the webhook URL in the AutoBlogWriter dashboard under Workspace Settings → Revalidation URL.
Advanced setup (manual control via @autoblogwriter/sdk/revalidate):
// app/api/autoblogwriter/revalidate/route.ts
import { revalidatePath, revalidateTag } from "next/cache";
import { createRevalidateRouteHandler } from "@autoblogwriter/sdk/revalidate";
export const POST = createRevalidateRouteHandler({
secret: process.env.AUTOBLOGWRITER_REVALIDATE_SECRET!,
revalidatePath,
revalidateTag,
});The handler verifies the X-AutoBlogWriter-Signature HMAC header, enforces timestamp skew (default 5 minutes), and revalidates:
- Paths:
/sitemap.xml,/robots.txt,/blog,/blog/:slug - Tags:
autoblogwriter:<workspaceSlug>:sitemap,autoblogwriter:<workspaceSlug>:posts, andautoblogwriter:<workspaceSlug>:post:<slug>
For manual signature verification:
import { verifyWebhookSignature } from "@autoblogwriter/sdk/revalidate";
const isValid = verifyWebhookSignature({
rawBody,
signature: req.headers.get("x-autoblogwriter-signature"),
secret: process.env.AUTOBLOGWRITER_REVALIDATE_SECRET!,
});Testing
npm testTests cover config validation, auth headers, 404 handling, sitemap output, and revalidation helpers.
