DeesseJS

Blog Plugin

Complete blog management with posts, categories, and tags

Blog Plugin

The Blog Plugin provides a complete blogging platform with posts, categories, tags, and all the features needed to run a professional blog.

Installation

npm install @deessejs/plugin-blog

Configuration

// deesse.config.ts
import { defineConfig } from '@deessejs/core'
import { blogPlugin } from '@deessejs/plugin-blog'

export const config = defineConfig({
  plugins: [
    blogPlugin({
      // Enable/disable features
      features: {
        categories: true,
        tags: true,
        featuredImage: true,
        excerpt: true,
        readingTime: true,
        author: true,
        comments: false, // Use comments plugin separately
        sharing: true,
        relatedPosts: true,
      },

      // Post options
      posts: {
        statusWorkflow: ['draft', 'review', 'published', 'archived'],
        defaultStatus: 'draft',
        allowScheduledPublishing: true,
      },

      // Display options
      display: {
        postsPerPage: 10,
        excerptLength: 160,
        dateFormat: 'MMMM DD, YYYY',
      },
    }),
  ],
})

Collections

Posts

The main content collection with all blog post fields:

{
  name: 'posts',
  fields: [
    {
      name: 'title',
      type: 'string',
      required: true,
    },
    {
      name: 'slug',
      type: 'string',
      unique: true,
    },
    {
      name: 'content',
      type: 'richtext',
      required: true,
    },
    {
      name: 'excerpt',
      type: 'text',
      admin: {
        description: 'Short description for listings and SEO',
      },
    },
    {
      name: 'featuredImage',
      type: 'media',
    },
    {
      name: 'status',
      type: 'enum',
      enum: ['draft', 'review', 'published', 'archived'],
      defaultValue: 'draft',
    },
    {
      name: 'publishedAt',
      type: 'datetime',
    },
    {
      name: 'author',
      type: 'reference',
      relation: 'users',
    },
    {
      name: 'categories',
      type: 'relation',
      relation: 'categories',
      many: true,
    },
    {
      name: 'tags',
      type: 'relation',
      relation: 'tags',
      many: true,
    },
    {
      name: 'readingTime',
      type: 'number',
      admin: {
        readOnly: true,
      },
    },
    {
      name: 'views',
      type: 'number',
      defaultValue: 0,
    },
  ],
}

Categories

Hierarchical categories for organizing posts:

{
  name: 'categories',
  fields: [
    {
      name: 'name',
      type: 'string',
      required: true,
    },
    {
      name: 'slug',
      type: 'string',
      unique: true,
    },
    {
      name: 'description',
      type: 'text',
    },
    {
      name: 'parent',
      type: 'reference',
      relation: 'categories',
      admin: {
        description: 'Parent category for hierarchy',
      },
    },
    {
      name: 'image',
      type: 'media',
    },
    {
      name: 'color',
      type: 'string',
      admin: {
        description: 'Hex color for category badge',
      },
    },
  ],
}

Tags

Flexible tags for content organization:

{
  name: 'tags',
  fields: [
    {
      name: 'name',
      type: 'string',
      required: true,
      unique: true,
    },
    {
      name: 'slug',
      type: 'string',
      unique: true,
    },
    {
      name: 'description',
      type: 'text',
    },
  ],
}

Admin Pages

The plugin adds the following admin pages:

Posts Page

  • List all posts with filters (status, category, author)
  • Quick actions (publish, archive, delete)
  • Bulk operations
  • Column customization

Categories Page

  • Hierarchical tree view
  • Drag-and-drop reordering
  • Category stats (post count)

Tags Page

  • List all tags
  • Tag popularity (usage count)
  • Merge tags functionality

API Routes

# Get all posts
GET /api/blog/posts

# Get published posts (public)
GET /api/blog/posts/published

# Get post by slug
GET /api/blog/posts/:slug

# Get posts by category
GET /api/blog/posts?category=tech

# Get posts by tag
GET /api/blog/posts?tag=nextjs

# Search posts
GET /api/blog/posts?search=react

# Get categories
GET /api/blog/categories

# Get tags
GET /api/blog/tags

# Get related posts
GET /api/blog/posts/:slug/related

Frontend Components

PostList Component

import { PostList } from '@deessejs/plugin-blog/components'

export default function BlogPage() {
  return (
    <PostList
      pagination={10}
      filters={{
        status: 'published',
        category: 'tech',
      }}
      sort={{ field: 'publishedAt', direction: 'desc' }}
    />
  )
}

PostCard Component

import { PostCard } from '@deessejs/plugin-blog/components'

export function BlogCard({ post }) {
  return (
    <PostCard
      post={post}
      showAuthor
      showDate
      showReadingTime
      showExcerpt
      variant="horizontal"
    />
  )
}

CategoryFilter Component

import { CategoryFilter } from '@deessejs/plugin-blog/components'

export default function Layout() {
  return (
    <CategoryFilter
      showCount
      layout="tree"
      collapsible
    />
  )
}

TagCloud Component

import { TagCloud } from '@deessejs/plugin-blog/components'

export default function Sidebar() {
  return (
    <TagCloud
      maxTags={20}
      sortBy="popularity"
    />
  )
}

Hooks

useBlogPosts

'use client'

import { useBlogPosts } from '@deessejs/plugin-blog/hooks'

export default function Blog() {
  const { posts, loading, error, pagination } = useBlogPosts({
    status: 'published',
    limit: 10,
    sort: '-publishedAt',
  })

  if (loading) return <div>Loading...</div>
  if (error) return <div>Error: {error.message}</div>

  return (
    <div>
      {posts.map(post => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.excerpt}</p>
        </article>
      ))}
    </div>
  )
}

useBlogPost

'use client'

import { useBlogPost } from '@deessejs/plugin-blog/hooks'

export default function PostPage({ slug }) {
  const { post, loading, error } = useBlogPost(slug)

  if (loading) return <div>Loading...</div>
  if (error) return <div>Error: {error.message}</div>

  return (
    <article>
      <h1>{post.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: post.content }} />
    </article>
  )
}

Server Components

getBlogPosts

import { getBlogPosts } from '@deessejs/plugin-blog/server'

export default async function BlogPage() {
  const posts = await getBlogPosts({
    where: { status: 'published' },
    include: { author: true, categories: true, tags: true },
    orderBy: { publishedAt: 'desc' },
    take: 10,
  })

  return (
    <div>
      {posts.map(post => (
        <article key={post.id}>
          <h2>{post.title}</h2>
        </article>
      ))}
    </div>
  )
}

getBlogPost

import { getBlogPost } from '@deessejs/plugin-blog/server'

export default async function PostPage({ params }) {
  const post = await getBlogPost({
    where: { slug: params.slug },
    include: { author: true, categories: true, tags: true },
  })

  return (
    <article>
      <h1>{post.title}</h1>
    </article>
  )
}

URL Structure

The plugin sets up these routes automatically:

/blog                    # Blog home
/blog/:slug              # Individual post
/blog/category/:slug     # Category archive
/blog/tag/:slug          # Tag archive
/blog/page/:page         # Paginated posts
/admin/blog              # Admin posts page
/admin/blog/categories   # Admin categories page
/admin/blog/tags         # Admin tags page

SEO Integration

Works seamlessly with the SEO plugin:

// app/blog/[slug]/page.tsx
import { getBlogPost } from '@deessejs/plugin-blog/server'

export async function generateMetadata({ params }) {
  const post = await getBlogPost({ where: { slug: params.slug } })

  return {
    title: post.seoTitle || post.title,
    description: post.metaDescription || post.excerpt,
    openGraph: {
      title: post.title,
      description: post.excerpt,
      images: post.featuredImage ? [post.featuredImage] : [],
      type: 'article',
      publishedTime: post.publishedAt,
      authors: [post.author.name],
    },
  }
}

Sitemap Integration

Automatically includes blog posts in sitemap:

// The plugin automatically registers with the sitemap plugin
// Posts with status 'published' are included

RSS Feed

Generates RSS feed for published posts:

# RSS feed available at
/rss/blog

# With query parameters
/rss/blog?category=tech
/rss/blog?tag=nextjs

Reading Time

Automatically calculates reading time:

// Calculated based on word count (200 words per minute)
// Can be customized in plugin config
blogPlugin({
  readingTime: {
    wordsPerMinute: 200,
    includeImages: true, // Add time for images
    imageSeconds: 12,    // Seconds per image
  },
})

Automatically suggests related posts based on:

  • Shared categories
  • Shared tags
  • Publication date proximity
blogPlugin({
  relatedPosts: {
    maxResults: 3,
    algorithm: 'categories', // or 'tags', 'mixed'
  },
})

Next Steps

On this page