next-blog-cms
v1.1.3
Published
Plug-and-play blog CMS for existing Next.js projects.
Maintainers
Readme
next-blog-cms
A pluggable blog CMS for existing Next.js applications. Install one package, wire two routes, and you get an isolated admin panel, SQLite storage, multi-language posts, and frontend hooks for rendering content.
Supported Next.js versions
- Next.js 13+ (App Router)
- Next.js 12+ (Pages Router)
- Tested with Next 16 / React 19
Quick start
Install the package
npm install next-blog-cmsExpose the admin panel
App Router (
app/folder). Create both routes so/blog-adminand nested paths work:// app/blog-admin/page.tsx import { BlogAdmin } from 'next-blog-cms/admin'; export default BlogAdmin;// app/blog-admin/[...path]/page.tsx import { BlogAdmin } from 'next-blog-cms/admin'; export default BlogAdmin;Pages Router (
pages/folder):// pages/blog-admin/[...path].tsx export { BlogAdminPage as default } from 'next-blog-cms/admin';
Expose the API routes
App Router. Again, create the root and the catch-all:
// app/api/blog/route.ts import { blogApiHandlerApp } from 'next-blog-cms/api'; export const GET = blogApiHandlerApp; export const POST = blogApiHandlerApp; export const PUT = blogApiHandlerApp; export const DELETE = blogApiHandlerApp;// app/api/blog/[...path]/route.ts import { blogApiHandlerApp } from 'next-blog-cms/api'; export const GET = blogApiHandlerApp; export const POST = blogApiHandlerApp; export const PUT = blogApiHandlerApp; export const DELETE = blogApiHandlerApp;Pages Router:
// pages/api/blog/[...path].ts export { blogApiHandler as default } from 'next-blog-cms/api';
Run the project
Visit
http://localhost:3000/blog-admin. If no users exist, you will be asked to create the first admin. Afterwards, log in with those credentials to access the dashboard.Configure environment variables
Add a secure secret for JWT signing:
# .env.local BLOG_JWT_SECRET=your-super-secret-token(In development a default is used if this variable is missing, but production deployments should always set it.)
Admin features (MVP)
- Dashboard with key metrics and latest posts
- Full post CRUD (multi-language, slug editing, draft/published toggle, featured image)
- Category management with translations
- Automatic slug suggestions while you type the default-language title
- User management (admin/author roles, safeguards against deleting yourself)
- Language management (enable/disable, set default, add/remove)
- Optional AI-powered translation buttons for posts and categories (uses your configured provider)
- Admin UI built with shadcn/ui + Tailwind (auto light/dark, configurable primary colour)
- Toast notifications and isolated component styling so the host app is unaffected
Database
- Uses SQLite (
./blog.dbby default) viabetter-sqlite3 - Auto-migrations on first run
- Environment overrides: set
BLOG_DB_PATHorNEXT_BLOG_DB_PATHto customize the location
Auth
- Email/password with bcrypt hashing
- JWT stored in httpOnly cookies
- Session length: 7 days (15 days with “remember me”)
Frontend consumption
Use the public hooks to render content inside your app. Example (App Router):
'use client';
import { usePosts } from 'next-blog-cms/hooks';
export default function BlogSection() {
const { posts, loading, error, hasMore, loadMore } = usePosts({ lang: 'en', limit: 6, status: 'published' });
if (loading) return <p>Loading…</p>;
if (error) return <p>Failed to load posts.</p>;
return (
<div>
{posts.map((post) => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</article>
))}
{hasMore && (
<button type="button" onClick={loadMore}>
Load more
</button>
)}
</div>
);
}Available hooks: usePosts, usePost, useCategories, useLanguages.
Configuration (optional)
Create blog.config.js (or .cjs/.json) at the project root to override defaults:
module.exports = {
database: {
path: './data/blog.db'
},
languages: {
default: 'en',
available: ['en', 'pt', 'es']
},
auth: {
jwtSecret: process.env.BLOG_JWT_SECRET,
sessionDuration: 7
},
theme: {
// Accepts hex ("#2563eb") or HSL ("hsl(221 83% 53%)")
primaryColor: '#2563eb'
},
ai: {
translator: {
provider: 'openai',
apiKey: process.env.OPENAI_API_KEY,
model: 'gpt-4o-mini'
}
}
};database.path: SQLite file location (created automatically).languages: default language and the list presented in the admin UI. The package now defaults to English.auth: default JWT secret and session duration (days) when “remember me” is unchecked.theme.primaryColor: adjusts the admin primary colour (light/dark aware).ai.translator: optional, enables the “Auto-translate” button in the post editor. Currently theopenaiprovider is supported; if omitted the button is hidden.
All fields are optional; the CMS runs zero-config. The file is loaded at runtime (CommonJS module.exports) so changes apply without rebuilds.
Admin i18n
The admin UI is English by default. To provide custom copy, pass translations to BlogAdmin:
import { BlogAdmin } from 'next-blog-cms/admin';
const messages = {
'nav.dashboard': 'Painel',
'common.cancel': 'Cancelar'
// ...any other keys used in the admin
};
export default function AdminPage() {
return <BlogAdmin locale="pt" messages={messages} />;
}You can also pass a custom translate function if you have an existing i18n runtime.
Branding
Customise the sidebar title and tagline by providing the branding prop:
import { BlogAdmin } from 'next-blog-cms/admin';
export default function AdminPage() {
return (
<BlogAdmin
branding={{
title: 'Acme CMS',
tagline: 'Content tools tailored for Acme'
}}
/>
);
}Development scripts
npm run dev # tsup in watch mode
npm run build # build dist (CJS + ESM + types + CSS)
npm run lint # eslint
npm run typecheck # tsc --noEmit
npm run test # vitest (placeholder in MVP)Roadmap (short term)
- Rich text editor (Tiptap)
- Image uploads
- Dark mode for the admin
- Additional filters/search in tables
Contributions and feedback are welcome! Open an issue or start a discussion to influence the next features.
