Engineering

One Blog System, Two Sections, Zero Duplication: The createPostsLib Pattern

How we built a multi-section blog (Blog + Engineering) with independent content, themes, and comment policies — all powered by one shared factory pattern.

By Lumo EngineeringFeb 17, 20262 min read

We needed two blog sections: one for product updates and one for engineering deep-dives. Same rendering pipeline, different content directories, different color themes, different comment policies. The naive approach — duplicating the entire blog infrastructure — would have worked but violated every instinct I have about code reuse.

So we built a factory.

The Problem

The Lumo blog (/blog) was working well: MDX files, static generation, pagination, tag filtering, RSS feed. Then we decided we wanted an engineering section (/engineering) with:

  • Its own content directory (content/engineering/)
  • An emerald green theme instead of blue
  • Comments enabled (the engineering audience has substantive feedback)
  • Its own RSS feed at /engineering/feed.xml
  • Independent pagination

Copying the blog code would have given us two independent systems that drift apart over time. Every future improvement would need to be applied twice. That's not engineering — that's job security through artificial complexity.

The Factory Pattern

The solution was a createPostsLib factory that takes a content directory and returns a complete posts API:

// lib/blog/posts.ts
export function createPostsLib(contentDirectory: string) {
  function getAllPosts() {
    // Read MDX files from the content directory
    // Parse frontmatter, sort by date, filter drafts
  }
 
  function getPostBySlug(slug: string) {
    // Read and compile a single post
  }
 
  function getPageCount() {
    // Calculate pagination based on total posts
  }
 
  return { getAllPosts, getPostBySlug, getPageCount };
}
 
// Default blog instance
export const {
  getAllPosts,
  getPostBySlug,
  getPageCount,
} = createPostsLib("content/blog");

The engineering section gets its own instance:

// lib/engineering/posts.ts
export const {
  getAllPosts: getEngineeringPosts,
  getPostBySlug: getEngineeringPostBySlug,
  getPageCount: getEngineeringPageCount,
} = createPostsLib("content/engineering");

Same parsing logic, same MDX compilation, same frontmatter schema. Different directory, different export names. Zero duplication.

Theme Variants

The shared components needed to support different visual themes without becoming a mess of conditional styles. We added a variant prop:

// BlogGrid accepts a variant for color theming
<BlogGrid
  posts={posts}
  basePath="/engineering"
  variant="emerald"
  sectionLabel="Engineering"
/>
 
// BlogPagination respects basePath for URL generation
<BlogPagination
  currentPage={page}
  totalPages={totalPages}
  basePath="/engineering"
/>

The variant prop controls accent colors (blue vs emerald), badge styles, and hover states. The basePath prop ensures pagination URLs and post links point to the correct section. Two props, full customization, no duplication.

Comment Policy Per Section

BlogPostEngagement got a showComments prop that defaults to false:

<BlogPostEngagement
  postSlug={slug}
  showComments={true}  // Engineering posts get comments
/>

Blog posts show emoji reactions only. Engineering posts show the full threaded comment system. The decision was deliberate: engineering readers are more likely to have technical questions and corrections worth preserving in a thread. Blog readers just want to signal "I found this useful" and move on.

The Result

Adding a third blog section (say, a design blog or a changelog blog) would take about 15 minutes:

  1. Create content/design/ directory
  2. Create lib/design/posts.ts with createPostsLib("content/design")
  3. Create app/design/page.tsx with a new color variant
  4. Done

No new components, no new rendering logic, no new RSS generation code. Just a new instance of the same factory pointed at a new directory.

The best abstractions are the ones that make the next feature boring to implement.


Abdul Rafay Founder, Syntax Lab Technology

Loading...