nuxt-x-blog
Nuxt 4 blog layer powered by Nuxt Content v3 — 9 XBL-prefixed components, a useBlog composable, 2 pre-built pages, and configurable blog settings via app.config.ts.
nuxt-x-blog
Blog layer for Nuxt 4. Powered by Nuxt Content v3 — provides 9 auto-imported XBL-prefixed components, a useBlog composable, and 2 pre-built pages. The consuming app writes Markdown posts in content/blog/; the layer handles all UI, routing, pagination, tag filtering, and related-posts logic.
Installation
npm install @xenterprises/nuxt-x-blog
// nuxt.config.ts
export default defineNuxtConfig({
extends: [['@xenterprises/nuxt-x-blog', { install: true }]],
})
Override defaults in app.config.ts:
export default defineAppConfig({
xBlog: {
title: 'Blog',
description: 'Latest articles and updates',
postsPerPage: 9,
dateFormat: 'MMM d, yyyy',
showAuthor: true,
showReadingTime: true,
showTags: true,
showTableOfContents: true,
showShareButtons: true,
},
})
Content Structure
Create blog posts in content/blog/:
content/
blog/
my-first-post.md
getting-started.md
Post Frontmatter
---
title: My First Post
description: A brief description of the post
date: 2025-01-15
author: Jane Doe
image: /images/blog/cover.jpg
tags:
- nuxt
- vue
published: true
---
Your post content here...
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | Post title |
description | string | No | Short excerpt |
date | string | Yes | ISO date string (used for sorting) |
author | string | No | Author display name |
image | string | No | Cover image URL |
tags | string | No | Tag list for filtering |
published | boolean | No | Set false to draft (default: true) |
What the Layer Provides
Components (auto-imported, XBL prefix):
XBLPostCard— Blog post preview card with image, title, date, tags, and reading timeXBLPostList— Paginated list ofXBLPostCarditemsXBLPostHeader— Post page header with title, meta, author, and cover imageXBLPagination— Page navigation controlsXBLSearchInput— Search input for filtering posts by title/descriptionXBLTagList— Tag filter list with countsXBLRecentPosts— Sidebar widget showing the most recent postsXBLTableOfContents— Auto-generated table of contents for post pagesXBLShareButtons— Social share buttons (Twitter, Facebook, LinkedIn, Email)
Composable:
useBlog()— All blog data access methods backed byqueryCollection('blog')
Pages:
/blog— Blog listing page with search, tag filtering, and pagination/blog/[...slug]— Individual post page with TOC, share buttons, and related posts
App Config Options
Configure under the xBlog key in app.config.ts:
| Option | Type | Default | Description |
|---|---|---|---|
title | string | 'Blog' | Blog section title |
description | string | 'Latest articles and updates' | Blog section description |
postsPerPage | number | 9 | Posts per page for pagination |
dateFormat | string | 'MMM d, yyyy' | Display date format |
showAuthor | boolean | true | Show author on post cards and pages |
showReadingTime | boolean | true | Show estimated reading time |
showTags | boolean | true | Show tags on post cards and pages |
showTableOfContents | boolean | true | Show TOC on post pages |
showShareButtons | boolean | true | Show social share buttons on post pages |
useBlog() Composable
const {
config, // BlogConfig from app.config.ts
getPosts, // (options?) => Promise<{ posts, total, page, totalPages, hasMore }>
getPostByPath, // (path) => Promise<BlogPost | null>
getAllTags, // () => Promise<{ tag, count }[]>
getRecentPosts, // (limit?) => Promise<BlogPost[]>
getRelatedPosts, // (post, limit?) => Promise<BlogPost[]>
formatDate, // (dateStr) => string
estimateReadingTime, // (text) => number — minutes
} = useBlog()
getPosts(options?)
| Option | Type | Default | Description |
|---|---|---|---|
page | number | 1 | Page number |
tag | string | — | Filter by tag |
limit | number | postsPerPage | Posts per page override |
Returns: { posts: BlogPost[], total: number, page: number, totalPages: number, hasMore: boolean }
BlogPost Type
interface BlogPost {
id: string
path: string
title: string
description?: string
date: string
author?: string
image?: string
tags?: string[]
published?: boolean
readingTime?: number
body?: any
}
Minimal Usage Example
<script setup>
const { getPosts } = useBlog()
const { posts } = await getPosts({ page: 1 })
</script>
<template>
<XBLPostList :posts="posts" />
</template>
Layer Architecture
| Path | Purpose |
|---|---|
nuxt.config.ts | Registers @nuxt/ui, @nuxt/content, Tailwind CSS |
app.config.ts | All configurable options under xBlog namespace |
app/composables/useBlog.ts | Blog data access via queryCollection('blog') |
app/components/X/BL/ | 9 auto-imported XBL-prefixed components |
app/pages/blog/ | Listing page and catch-all post page |
app/types/index.ts | TypeScript interfaces: BlogPost, BlogAuthor, BlogConfig |
Environment Variables
This layer reads no environment variables. All configuration is through app.config.ts.
AI Context
package: "@xenterprises/nuxt-x-blog"
use-when: >
Adding a complete blog to a Nuxt 4 app backed by Nuxt Content v3.
Write posts as Markdown in content/blog/ with title, date, and published
frontmatter. Use useBlog() for data access — getPosts() for paginated
listings, getPostByPath() for post pages, getAllTags() for tag filters.
All XBL* components are auto-imported; the pre-built /blog and
/blog/[...slug] pages work out of the box. Configure display options
(showAuthor, showReadingTime, postsPerPage, etc.) under xBlog in app.config.ts.
