React Components
Pre-built React components for rendering blog posts and listings.
React Components
The @autoblogwriter/sdk/react entry point provides ready-to-use React components for rendering blog content. All components work with both server and client components.
import { BlogFaq, BlogPost, BlogPostList, Markdown, RelatedPosts, Branding, AutoBlogPost, AutoBlogPostList } from "@autoblogwriter/sdk/react";<BlogPostList>
Renders a list of blog post cards with titles, dates, and excerpts.
Basic Usage
import Link from "next/link";
import { fetchBlogPosts } from "@autoblogwriter/sdk/next";
import { BlogPostList } from "@autoblogwriter/sdk/react";
export default async function BlogPage() {
const { posts } = await fetchBlogPosts();
return <BlogPostList posts={posts} linkComponent={Link} />;
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
posts | BlogPost[] | — | Array of posts to display |
title | string | "Latest posts" | Section heading. Pass empty string to hide. |
routePrefix | string | "/blog" | URL prefix for post links (e.g., /blog/my-post) |
linkComponent | React.ComponentType | <a> | Link component (use Next.js Link for client navigation) |
className | string | "ba-listing" | CSS class on the wrapper <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 | — | Custom card renderer (overrides default card) |
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 free plans this is forced true. |
brandingLinkComponent | React.ComponentType | linkComponent | Link component used specifically for the branding link. |
Custom Link Component
Pass Next.js Link for client-side navigation:
import Link from "next/link";
<BlogPostList posts={posts} linkComponent={Link} />Custom Route Prefix
<BlogPostList posts={posts} routePrefix="/articles" />
// Links will be /articles/my-post instead of /blog/my-postTheme
<BlogPostList posts={posts} theme="light" />
<BlogPostList posts={posts} theme="dark" />Card vs List Layout
<BlogPostList posts={posts} layout="card" />
<BlogPostList posts={posts} layout="list" />Image Component
Pass a custom image component (e.g., Next.js Image) for optimized card images:
import Image from "next/image";
<BlogPostList
posts={posts}
imageComponent={Image}
imageProps={{ width: 1200, height: 675 }}
/>Controlled Pagination (Client-Side)
Use onPageChange for client-side pagination with React state:
"use client";
import { useState } from "react";
import { BlogPostList } from "@autoblogwriter/sdk/react";
export function BlogList({ posts }) {
const [page, setPage] = useState(1);
return (
<BlogPostList
posts={posts}
postsPerPage={6}
page={page}
onPageChange={setPage}
/>
);
}Server-Friendly Pagination
Use paginationHref for server-side rendering without 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}`}
/>
);
}Custom Card Rendering
Override the default card layout with renderCard:
<BlogPostList
posts={posts}
linkComponent={Link}
renderCard={(post, href) => (
<Link href={href} className="custom-card">
<h3>{post.title}</h3>
<p>{post.excerpt}</p>
{post.metadata?.readingTimeMinutes && (
<span>{post.metadata.readingTimeMinutes} min read</span>
)}
</Link>
)}
/>Default HTML Structure
<section class="ba-listing">
<h1 class="ba-listing-title">Latest posts</h1>
<div class="ba-posts">
<article class="ba-post-card">
<h2 class="ba-post-card-title">
<a href="/blog/my-post" class="ba-post-card-link">My Post</a>
</h2>
<p class="ba-post-card-meta">
<time datetime="2025-01-15T10:00:00.000Z">January 15, 2025</time>
</p>
<div class="ba-post-card-excerpt">
<!-- Rendered excerpt markdown -->
</div>
</article>
<!-- More cards... -->
</div>
</section><BlogPost>
Renders a full blog post article with title, date, reading time, and markdown content.
Basic Usage
import { fetchBlogPost } from "@autoblogwriter/sdk/next";
import { BlogPost } from "@autoblogwriter/sdk/react";
export default async function PostPage({ params }: Props) {
const { slug } = await params;
const post = await fetchBlogPost(slug);
return <BlogPost post={post} />;
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
post | BlogPost | — | The post to render |
showTitle | boolean | true | Show the post title as an <h1> |
showDate | boolean | true | Show publication 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 wrapper <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 | — | Custom content renderer (overrides default Markdown) |
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 free plans this is forced true. |
brandingLinkComponent | React.ComponentType | linkComponent | Link component used specifically for the branding link. |
Hero Image
Display a hero image from post.images.hero:
<BlogPost post={post} showHeroImage />With a custom image component (e.g., Next.js Image):
import Image from "next/image";
<BlogPost
post={post}
showHeroImage
imageComponent={Image}
imageProps={{ width: 1200, height: 630 }}
/>Theme
<BlogPost post={post} theme="light" />
<BlogPost post={post} theme="dark" />Hiding Title and Date
<BlogPost post={post} showTitle={false} showDate={false} />Custom FAQ Rendering
Override the default FAQ section:
<BlogPost
post={post}
renderFaq={(faq, post) => (
<div className="my-faq">
<h2>FAQ about {post.title}</h2>
{faq.items.map((item, i) => (
<details key={i}>
<summary>{item.question}</summary>
<p>{item.answer}</p>
</details>
))}
</div>
)}
/>Custom Related Posts Rendering
Override the default related posts section:
<BlogPost
post={post}
renderRelatedPosts={(posts, post) => (
<aside>
<h3>More like "{post.title}"</h3>
<ul>
{posts.map((p) => (
<li key={p.id}><a href={`/blog/${p.slug}`}>{p.title}</a></li>
))}
</ul>
</aside>
)}
/>Next.js Image and Link Integration
import Link from "next/link";
import Image from "next/image";
<BlogPost
post={post}
showHeroImage
linkComponent={Link}
imageComponent={Image}
imageProps={{ width: 1200, height: 630 }}
/>Custom Content Rendering
Replace the built-in markdown renderer with your own (e.g., for syntax highlighting with react-markdown or next-mdx-remote):
import ReactMarkdown from "react-markdown";
<BlogPost
post={post}
renderContent={(content) => (
<ReactMarkdown className="prose">{content}</ReactMarkdown>
)}
/>Default HTML Structure
<article class="ba-post">
<h1 class="ba-post-title">My Post Title</h1>
<p class="ba-post-meta">
<time datetime="2025-01-15T10:00:00.000Z">January 15, 2025</time>
<span class="ba-post-meta-sep"> · </span>
<span class="ba-post-reading-time">5 min read</span>
</p>
<div class="ba-markdown">
<!-- Rendered markdown HTML -->
</div>
</article><BlogFaq>
Renders an FAQ section from post FAQ data.
Basic Usage
import { BlogFaq } from "@autoblogwriter/sdk/react";
<BlogFaq faq={post.faq!} />Props
| 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. |
Custom Title
<BlogFaq faq={post.faq!} title="Common Questions" />With BlogPost
The <BlogPost> component automatically renders <BlogFaq> when post.faq exists. Control it with showFaq:
<BlogPost post={post} showFaq={false} /><RelatedPosts>
Renders a related-posts list from post.relatedPosts.
Basic Usage
import { RelatedPosts } from "@autoblogwriter/sdk/react";
<RelatedPosts posts={post.relatedPosts ?? []} />Props
| 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. |
Custom Title and Route Prefix
<RelatedPosts
posts={post.relatedPosts ?? []}
title="Keep reading"
routePrefix="/articles"
/>With Next.js Link
import Link from "next/link";
<RelatedPosts posts={post.relatedPosts ?? []} linkComponent={Link} />Custom Item Rendering
<RelatedPosts
posts={post.relatedPosts ?? []}
renderItem={(post, href) => (
<a href={href} className="my-related-link">
<strong>{post.title}</strong>
{post.excerpt && <p>{post.excerpt}</p>}
</a>
)}
/>With BlogPost
The <BlogPost> component automatically renders <RelatedPosts> when post.relatedPosts exists. Control it with showRelatedPosts:
<BlogPost post={post} showRelatedPosts={false} /><Markdown>
Renders a markdown string to HTML using the built-in renderer.
Basic Usage
import { Markdown } from "@autoblogwriter/sdk/react";
<Markdown source="# Hello World\n\nThis is **bold** text." />Props
| 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> |
Custom Class
<Markdown source={post.content} className="prose dark:prose-invert" />How It Works
The <Markdown> component uses the built-in renderMarkdownToHtml() function which:
- Converts headings (
# H1through###### H6) - Renders bold, italic, and
inline code - Converts links
[text](url)withtarget="_blank"andrel="noreferrer" - Converts images
with proper attributes - Wraps code blocks in
<pre><code> - Escapes HTML to prevent XSS attacks
- Has zero dependencies
If you need more advanced markdown features (tables, footnotes, syntax highlighting), use renderContent on <BlogPost> with a library like react-markdown.
Standalone Markdown Renderer
The renderMarkdownToHtml() function is also available directly:
import { renderMarkdownToHtml } from "@autoblogwriter/sdk";
const html = renderMarkdownToHtml("# Hello\n\nWorld");
// "<h1>Hello</h1>\n<p>World</p>"<Branding>
A standalone "Powered by AutoBlogWriter" attribution component. This is rendered automatically inside <BlogPost> and <BlogPostList> when branding is required, but you can also use it directly.
Basic Usage
import { Branding } from "@autoblogwriter/sdk/react";
<Branding />Props
| 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 alongside the brand name. |
Example
import Link from "next/link";
import { Branding } from "@autoblogwriter/sdk/react";
<Branding linkComponent={Link} className="my-branding" />Free plan note: On free-tier workspaces, branding is required and cannot be hidden. Attempting to set
showBranding={false}on<BlogPost>or<BlogPostList>will have no effect if the API response indicatesbrandingRequired: true. Upgrade to Pro+ to remove attribution.
<AutoBlogPost>
An async React Server Component that fetches and renders a single blog post in one JSX call. Requires Next.js App Router (or any environment that supports async components).
Basic Usage
import { AutoBlogPost } from "@autoblogwriter/sdk/react";
export default async function PostPage({ params }: Props) {
const { slug } = await params;
return <AutoBlogPost slug={slug} />;
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
slug | string | — | (Required) The post slug to fetch. |
post | BlogPost | — | Optional pre-fetched post. If provided, skips the fetch. |
fetchPost | (slug: string) => Promise<BlogPost | null> | — | Custom fetch function. Overrides the default client. |
client | AutoBlogWriterClient | env client | Custom client instance to use for fetching. |
| ...BlogPostProps | — | — | All props from <BlogPost> are supported. |
Usage Examples
Basic:
import { AutoBlogPost } from "@autoblogwriter/sdk/react";
export default async function PostPage({ params }: Props) {
const { slug } = await params;
return <AutoBlogPost slug={slug} />;
}With a custom client:
import { createAutoBlogWriterClient } from "@autoblogwriter/sdk";
import { AutoBlogPost } from "@autoblogwriter/sdk/react";
const client = createAutoBlogWriterClient({ apiUrl: "...", apiKey: "..." });
export default async function PostPage({ params }: Props) {
const { slug } = await params;
return <AutoBlogPost slug={slug} client={client} showHeroImage />;
}With a custom fetcher:
import { AutoBlogPost } from "@autoblogwriter/sdk/react";
async function myFetchPost(slug: string) {
// your own data-fetching logic
}
export default async function PostPage({ params }: Props) {
const { slug } = await params;
return <AutoBlogPost slug={slug} fetchPost={myFetchPost} />;
}With a pre-fetched post (e.g., from parallel data loading):
import { fetchBlogPost } from "@autoblogwriter/sdk/next";
import { AutoBlogPost } from "@autoblogwriter/sdk/react";
export default async function PostPage({ params }: Props) {
const { slug } = await params;
const post = await fetchBlogPost(slug);
return <AutoBlogPost slug={slug} post={post} showHeroImage />;
}<AutoBlogPostList>
An async React Server Component that fetches and renders a list of blog posts in one JSX call.
Basic Usage
import { AutoBlogPostList } from "@autoblogwriter/sdk/react";
export default async function BlogPage() {
return <AutoBlogPostList />;
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
posts | BlogPost[] | — | Optional pre-fetched posts. If provided, skips the fetch. |
fetchPosts | () => Promise<PostsResponse> | — | Custom fetch function. Overrides the default client. |
client | AutoBlogWriterClient | env client | Custom client instance to use for fetching. |
limit | number | — | Maximum number of posts to fetch. |
cursor | string | — | Pagination cursor for fetching a specific page. |
category | string | — | Filter posts by category slug. |
| ...BlogPostListProps | — | — | All props from <BlogPostList> are supported. |
Usage Examples
Basic:
import { AutoBlogPostList } from "@autoblogwriter/sdk/react";
export default async function BlogPage() {
return <AutoBlogPostList />;
}With limit and category filter:
import { AutoBlogPostList } from "@autoblogwriter/sdk/react";
export default async function BlogPage() {
return (
<AutoBlogPostList
limit={6}
category="engineering"
layout="card"
/>
);
}With pre-fetched posts:
import { fetchBlogPosts } from "@autoblogwriter/sdk/next";
import { AutoBlogPostList } from "@autoblogwriter/sdk/react";
export default async function BlogPage() {
const { posts } = await fetchBlogPosts({ limit: 20 });
return <AutoBlogPostList posts={posts} layout="list" />;
}Next Steps
- Styling — Customize the look of these components with CSS.
- Next.js Helpers — Fetch data for the components.
On this page
- <BlogPostList>
- Basic Usage
- Props
- Custom Link Component
- Custom Route Prefix
- Theme
- Card vs List Layout
- Image Component
- Controlled Pagination (Client-Side)
- Server-Friendly Pagination
- Custom Card Rendering
- Default HTML Structure
- <BlogPost>
- Basic Usage
- Props
- Hero Image
- Theme
- Hiding Title and Date
- Custom FAQ Rendering
- Custom Related Posts Rendering
- Next.js Image and Link Integration
- Custom Content Rendering
- Default HTML Structure
- <BlogFaq>
- Basic Usage
- Props
- Custom Title
- With BlogPost
- <RelatedPosts>
- Basic Usage
- Props
- Custom Title and Route Prefix
- With Next.js Link
- Custom Item Rendering
- With BlogPost
- <Markdown>
- Basic Usage
- Props
- Custom Class
- How It Works
- Standalone Markdown Renderer
- <Branding>
- Basic Usage
- Props
- Example
- <AutoBlogPost>
- Basic Usage
- Props
- Usage Examples
- <AutoBlogPostList>
- Basic Usage
- Props
- Usage Examples
- Next Steps