npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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.

Documentation

Installation

npm install @autoblogwriter/sdk

Requires Node.js 18+ and native fetch. Bundles ship as ESM, CJS, and types via tsup.

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)

  1. Install the package:
npm install @autoblogwriter/sdk
  1. Configure environment variables:
AUTOBLOGWRITER_API_KEY=ba_pk_your_api_key
AUTOBLOGWRITER_WORKSPACE_SLUG=your-workspace
AUTOBLOGWRITER_REVALIDATE_SECRET=your_webhook_secret
  1. 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)

  1. Install and set the same environment variables as above.

  2. 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
}
  1. 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.warn and return safe defaults on config or API errors instead of throwing. fetchBlogPost calls Next.js notFound() 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 when post.metadata.jsonLd is present — buildJsonLdScript() is only needed for manual <head> injection via generateMetadata.

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. fetchPost returns null instead of calling notFound() so your framework's 404 handling stays in your hands. No ISR cache tags are applied — use ./next if 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, and autoblogwriter:<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 test

Tests cover config validation, auth headers, 404 handling, sitemap output, and revalidation helpers.