A simple, embeddable blog engine for React applications with support for both local MDX files and Supabase database as content sources.
- π₯ Multiple Content Sources: Support for both local MDX files and Supabase database, more can be added in the future
- π MDX Support: Rich content with React components in markdown
- π€ Author Management: Built-in author profiles with social links
- π¨ Customizable: Tailwind CSS styling with customizable components
- π SEO Ready: Metadata generation for blog posts
- π± Responsive: Mobile-friendly design out of the box
npm install react-blog-module
# or
yarn add react-blog-module
# or
pnpm add react-blog-module
npm install react react-dom @supabase/supabase-js gray-matter next-mdx-remote lucide-react tailwindcss class-variance-authority clsx tailwind-merge tailwindcss-animate react-syntax-highlighter
If you want to use Supabase as a content source, create a table in your Supabase database:
CREATE TABLE blog_posts (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
slug TEXT UNIQUE NOT NULL,
title TEXT NOT NULL,
description TEXT,
author TEXT NOT NULL,
content TEXT NOT NULL,
draft BOOLEAN DEFAULT false,
date TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Add RLS policies as needed
ALTER TABLE blog_posts ENABLE ROW LEVEL SECURITY;
-- Example: Allow public read access to published posts
CREATE POLICY "Public can read published posts" ON blog_posts
FOR SELECT USING (draft = false);
Create a blog configuration:
import { createBlogConfig, BlogAuthorInfo } from 'react-blog-module';
import { createClient } from '@supabase/supabase-js';
// Set up Supabase client
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
// Define authors
const authors: BlogAuthorInfo[] = [
{
name: 'John Doe',
avatar: '/avatars/john.jpg',
bio: 'Software engineer and technical writer',
social: {
twitter: 'johndoe',
github: 'johndoe',
linkedin: 'johndoe'
}
}
];
// Create blog configuration
export const blogConfig = createBlogConfig(
'My Blog', // title
'A blog about tech and code', // description
authors, // authors array
supabase, // Supabase client
'/blog', // basePath (optional)
'content/blog' // contentPath (optional)
);
Create a content directory for your MDX files:
content/blog/
βββ my-first-post.mdx
βββ another-post.mdx
βββ ...
Example MDX file (content/blog/my-first-post.mdx
):
---
title: "My First Blog Post"
date: "2024-01-15"
description: "This is my first blog post using the React Blog Module"
author: "John Doe"
draft: false
---
# Welcome to My Blog
This is the content of my first blog post. I can use **markdown** and even React components!
## Code Example
```javascript
function hello() {
console.log('Hello, world!');
}
import { BlogPostsPage, getMetadata } from 'react-blog-module';
import { blogConfig } from './blog-config';
export default function BlogPage() {
return <BlogPostsPage config={blogConfig} />;
}
// For Next.js metadata
export async function generateMetadata() {
return await getMetadata(blogConfig);
}
import { SingleBlogPost, generateBlogMetadata, generateNextStaticParams } from 'react-blog-module';
import { blogConfig } from '../blog-config';
interface Props {
params: Promise<{ slug: string }>;
}
export default function BlogPost({ params }: Props) {
return <SingleBlogPost params={params} config={blogConfig} />;
}
// For Next.js
export async function generateMetadata({ params }: Props) {
return await generateBlogMetadata({ params, config: blogConfig });
}
export async function generateStaticParams() {
return await generateNextStaticParams(blogConfig);
}
import { createDatabasePost, getDatabasePosts } from 'react-blog-module';
import { blogConfig } from './blog-config';
// Create a new post
const newPost = await createDatabasePost(blogConfig.supabase, {
title: 'New Post from Database',
description: 'This post is stored in Supabase',
author: 'John Doe',
content: '# Hello from the database!\n\nThis content is stored in Supabase.',
draft: false
});
// Get all database posts
const posts = await getDatabasePosts(blogConfig.supabase);
The blog engine supports both file-based and database content sources:
- Database posts take priority over file-based posts with the same slug
- File-based posts are loaded from the
contentPath
directory - Posts from both sources are merged and sorted by date
import { mdxComponents } from 'react-blog-module';
// Extend default components
const customComponents = {
...mdxComponents,
h1: (props: any) => <h1 className="custom-h1" {...props} />,
CustomComponent: ({ children }: { children: React.ReactNode }) => (
<div className="custom-wrapper">{children}</div>
)
};
// Use in SingleBlogPost
<MDXRemote source={content} components={customComponents} />
The components use Tailwind CSS classes. Make sure your project has Tailwind configured and includes the necessary classes, or override the styles as needed.
BlogConfig
- Main configuration objectBlogAuthorInfo
- Author information structureBlogPost
- Blog post data structureDatabasePost
- Database post structure
createBlogConfig()
- Create blog configurationgetAllPosts()
- Get all posts from both sourcesgetPostBySlug()
- Get single post by slugcreateDatabasePost()
- Create new database postgetDatabasePosts()
- Get all database postsgenerateBlogMetadata()
- Generate SEO metadata
BlogPostsPage
- Blog posts listing pageSingleBlogPost
- Individual blog post pageAuthorCard
- Author information componentCodeBlock
- Syntax-highlighted code blocks
- React 19+
- TypeScript (recommended)
- Tailwind CSS
- Supabase (optional, for database posts)
MIT
Contributions are welcome! Please feel free to submit issues and pull requests.