Blog
Blog
The blog component suite covers every layer of a content-driven site: listing, discovery, reading, and conversion. Components are designed to compose — XMarkBlogList renders XMarkBlogCard internally; XMarkBlogDetail embeds XMarkBlogAuthor, XMarkBlogNavigation, and a sidebar slot that defaults to XMarkNewsletterForm.
Components
<XMarkBlogCard />
A single post preview. Links to /blog/[slug] automatically. Shows cover image, category badge, formatted date, reading time, excerpt, and author avatar.
<XMarkBlogCard
:post="{
title: 'Getting Started with Nuxt 4',
slug: 'getting-started-nuxt-4',
description: 'A practical walkthrough of the new Nuxt 4 directory structure.',
date: '2025-03-01',
readingTime: 5,
category: 'Tutorials',
img: { src: '/blog/nuxt4.jpg' },
author: { name: 'Jane Doe', avatar: '/avatars/jane.jpg' },
}"
:has-category="true"
:has-author="true"
:has-excerpt="true"
:button="{ label: 'Read More' }"
/>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
post | Object | Example post | Post object { title, slug, img?, description?, date?, readingTime?, category?, author?, tags? } |
hasCategory | Boolean | true | Show category badge |
hasAuthor | Boolean | true | Show author avatar and name |
hasExcerpt | Boolean | true | Show post description |
button | Object | null | Full-width CTA button { label, color?, variant?, icon? } |
<XMarkBlogList />
A paginated grid of post cards with optional search input and category dropdown filter.
<XMarkBlogList
:posts="allPosts"
:columns="3"
:per-page="9"
:has-filters="true"
:has-categories="true"
:has-authors="true"
:categories="['Tutorials', 'News', 'Case Studies']"
:card-button="{ label: 'Read More' }"
/>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
posts | Array | [] | Array of post objects |
columns | Number | 3 | Grid columns: 1, 2, 3 |
perPage | Number | 9 | Posts per page |
hasFilters | Boolean | false | Show search and category filter bar |
hasCategories | Boolean | true | Show category badges on cards |
hasAuthors | Boolean | true | Show author info on cards |
categories | Array | [] | Available categories for filter dropdown |
cardButton | Object | null | Button config passed to every card |
<XMarkBlogDetail />
Full article reading layout. Place your prose content in the default slot. Automatically renders a sticky sidebar on desktop with a table of contents and a newsletter CTA (overridable via the sidebar slot).
<XMarkBlogDetail
:post="post"
:previous-post="previousPost"
:next-post="nextPost"
:toc="[
{ id: 'intro', text: 'Introduction', depth: 2 },
{ id: 'setup', text: 'Setup', depth: 2 },
{ id: 'config', text: 'Configuration', depth: 3 },
]"
>
<!-- Rendered prose content -->
<ContentRenderer :value="post" />
<template #sidebar>
<XMarkNewsletterForm variant="compact" title="Weekly digest" />
</template>
</XMarkBlogDetail>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
post | Object | Example post | Full post object including title, description, date, readingTime, category, author, img, tags |
previousPost | Object | null | Previous post { title, slug } |
nextPost | Object | null | Next post { title, slug } |
toc | Array | [] | Table of contents [{ id, text, depth }] |
Slots
| Slot | Description |
|---|---|
default | The article prose content |
sidebar | Override the sticky sidebar content |
<XMarkBlogAuthor />
Renders author information in three sizes: inline / sm for attribution lines, full for a boxed bio at the end of an article, and card for centered profile cards.
<XMarkBlogAuthor
:author="{
name: 'Jane Doe',
title: 'Senior Engineer',
bio: 'Jane writes about web performance and developer experience.',
avatar: '/avatars/jane.jpg',
social: { twitter: 'https://twitter.com/jane', github: 'https://github.com/jane' },
}"
variant="full"
/>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
author | Object | Required | Author object { name, title?, bio?, avatar?, social?: { [platform]: url } } |
variant | String | 'inline' | Display variant: 'inline', 'sm', 'full', 'card' |
<XMarkBlogNavigation />
A two-column previous/next navigation bar with post titles. Supports Nuxt Content _path or standard slug routing.
<XMarkBlogNavigation
:previous="{ title: 'Deploying to Vercel', slug: 'deploying-to-vercel' }"
:next="{ title: 'Edge Functions Deep Dive', slug: 'edge-functions' }"
base-path="/blog"
previous-label="Previous Article"
next-label="Next Article"
/>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
previous | Object | null | Previous post { title, slug } or { title, _path } |
next | Object | null | Next post { title, slug } or { title, _path } |
basePath | String | '/blog' | Base URL path for slug-based routing |
previousLabel | String | 'Previous' | Label above previous post title |
nextLabel | String | 'Next' | Label above next post title |
<XMarkBlogSidebar />
Sticky blog sidebar. Renders a search input, category links (with active highlighting), tag cloud, recent posts, and a newsletter CTA. Emit search to handle the search query in the parent.
<XMarkBlogSidebar
:has-search="true"
:categories="[
{ name: 'Tutorials', slug: 'tutorials', count: 12 },
{ name: 'News', slug: 'news', count: 5 },
]"
:tags="[
{ name: 'Vue', slug: 'vue' },
{ name: 'Nuxt', slug: 'nuxt' },
]"
:recent-posts="recentPosts"
category-base-path="/blog/category"
tag-base-path="/blog/tag"
:has-cta="true"
@search="handleSearch"
/>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
hasSearch | Boolean | true | Show search input |
categories | Array | [] | Category list [{ name, slug, count }] |
tags | Array | [] | Tag list [{ name, slug }] |
recentPosts | Array | [] | Recent posts [{ title, slug, img?, date? }] |
categoryBasePath | String | '/blog/category' | URL prefix for category links |
tagBasePath | String | '/blog/tag' | URL prefix for tag links |
hasCta | Boolean | true | Show newsletter CTA at bottom |
Slots
| Slot | Description |
|---|---|
cta | Override the newsletter CTA section |
<XMarkBlogCTANewsletter />
An inline newsletter signup widget designed to sit inside blog posts or the sidebar. Supports four size/layout variants.
<XMarkBlogCTANewsletter
title="Never miss a post"
description="Get the latest articles delivered to your inbox every week."
:button="{ label: 'Subscribe', color: 'primary' }"
placeholder="your@email.com"
variant="compact"
:has-privacy="true"
privacy-text="No spam. Unsubscribe anytime."
/>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
title | String | 'Subscribe to our newsletter' | Widget heading |
description | String | 'Get the latest posts delivered right to your inbox.' | Supporting text |
button | Object | { label: 'Subscribe' } | Button config { label, color? } |
placeholder | String | 'Enter your email' | Input placeholder |
variant | String | 'default' | Layout variant: 'default', 'featured', 'inline', 'compact' |
icon | String | 'i-lucide-mail' | Icon shown in featured variant |
hasPrivacy | Boolean | true | Show privacy note |
privacyText | String | 'No spam. Unsubscribe anytime.' | Privacy note text |
AI Context
category: Blog
package: "@xenterprises/nuxt-x-marketing"
components:
- XMarkBlogCard
- XMarkBlogList
- XMarkBlogDetail
- XMarkBlogAuthor
- XMarkBlogNavigation
- XMarkBlogSidebar
- XMarkBlogCTANewsletter
use-when: >
Building a content marketing blog. Use XMarkBlogList on the index page,
XMarkBlogDetail on individual post pages, and XMarkBlogSidebar alongside
both for navigation and discovery.
typical-page-section: >
XMarkBlogList: /blog index. XMarkBlogDetail: /blog/[slug].
XMarkBlogSidebar: right column of list and detail pages.
XMarkBlogCTANewsletter: within post content or sidebar.
