logicspike/docs

Blog Engine

Vlozi Books — Product Vision

Last Updated: 2026-04-29 Status: Draft (idea validation) Service: extension of apps/blog-service SDK: extension of @vlozi/blog

The goal: Be the easiest place on the internet to publish a book. Not a 500-page novel — a 12-chapter, 80-page founder's playbook, design handbook, cookbook, or course. Markdown-grade simplicity. Magazine-grade reading experience. Zero "platform tax."


TL;DR

Idea: extend the blog engine with a "book" content type. A book is an ordered collection of chapters (each is a long-form post) with a cover, table of contents, and a public reading interface that feels like a real book — not a blog series.

Verdict (my take): Strong fit. ~80 % of the infrastructure already exists (editor, content schema, public renderer, SDK). The new surface area is small but high-leverage. But it should not block launch. Ship it as v1.5 (~3 weeks after blog goes live) so the core blog gets validated first and books inherit a battle-tested foundation.

Why it's interesting:

  • No incumbent owns "polished short-book publishing." GitBook is for technical docs, Substack does serial posts (not books), Medium "Series" is an afterthought, Notion publishing is informal.
  • It compounds with everything else vlozi already has: lead capture (chapter 1 free → email gate), Brain (write outlines, convert posts to chapters), newsletter (book launches as drip), forms (waitlist for next book), contact intelligence (who's reading what).
  • It's a category change in product framing — blog tools sell "post engines"; books sell "publish a thing your readers will keep coming back to."

1. Purpose (The Why)

Founders, indie creators, and educators write structured long-form content — playbooks, handbooks, courses, technical guides — and have nowhere good to publish it.

What they have What's wrong
Blog series (multi-part posts) Discovery is broken. Reader lands on Part 4, has no idea Part 1 exists. SEO splits across N URLs. No reading progress.
Notion / Google Docs No design, no SEO, no email capture, no analytics, ugly URL.
GitBook Technical-first. Sidebar-heavy. Looks like docs, not a book. Branding is GitBook's, not yours.
Substack book series Email-first. No real "book home." No TOC. No cover.
PDF on Gumroad Can't be read on the web, kills SEO, friction to access, no analytics on what readers actually read.
Self-hosted Next.js MDX site Engineer-only. Two weeks of yak-shaving for a 12-chapter handbook.

The gap is a "book" that lives on the open web with all the affordances readers expect from a book:

  • a cover and back-cover blurb
  • a table of contents you can jump around in
  • a reader UI with prev / next, reading progress, and a sense of "where you are in the book"
  • a single canonical URL for the book itself + clean per-chapter URLs for SEO and sharing
  • an offline export (EPUB / PDF) for people who want to read on Kindle or print
  • a way to gate part of it behind email capture or payment without becoming Substack

Vlozi Books fills this gap as a natural extension of the blog engine — same editor, same SDK, same dashboard, new reading surface.

1.1 The "no one else can do this" insight

Standalone book platforms (Leanpub, GitBook) are content silos. They don't know who reads chapter 3, can't trigger a chatbot follow-up when someone finishes the book, and can't auto-add "people who read chapter 1 of Pricing for Founders" to a newsletter segment.

Standalone blogs (Ghost, Wordpress) treat long-form as "just more posts." No book metadata, no reading order, no cover, no completion signal.

Vlozi already has the identity layer (Contact Intelligence), the AI layer (Brain), the distribution layer (Newsletter), and the capture layer (Forms). Adding books makes every reading event a signal that flows through the rest of the stack. A book chapter completion is a stronger lead signal than a newsletter signup. No one else can act on it because no one else has the rest of the platform.


2. Target Audience (The Who)

Persona What they want to publish Why vlozi
Indie founder writing "The X Playbook" 8–15 chapters, ~50–100 pages, free or email-gated Replaces a Notion doc + Gumroad PDF + landing page combo. One link.
Course creator packaging written material A static book companion to a video course Embedded in their existing site via SDK; chapter-completion → enroll-in-cohort signal
Designer / dev with a strong opinion "How to build dark UIs," "Side-project marketing in 2026" Magazine-grade reader UI without designing one
Agency or consultancy Methodology handbook, client onboarding guide Per-tenant — every client gets their own
Teacher / educator Free open textbook on a niche topic Public reading + EPUB export, SEO discovery built in
Newsletter author with archives Compile 50 posts into 4 themed books "Convert posts to chapters" workflow — 1-click

Not the audience: novelists shipping 100k-word fiction, technical reference docs (use GitBook), companies needing branded multi-author wikis (use Notion).


3. The Three Pillars

┌─────────────────────────────────────────────────────────────────┐
│                         Vlozi Books                              │
├──────────────────┬──────────────────┬───────────────────────────┤
│   📚 Build        │   🔗 Bind         │   📖 Read                  │
│                  │                  │                           │
│  Author with the │  Structure into  │  Surface as a polished    │
│  same editor you │  a book with     │  reading experience for   │
│  use for posts   │  cover + TOC     │  the public web           │
├──────────────────┼──────────────────┼───────────────────────────┤
│ Reuse TipTap     │ Drag-to-reorder  │ Cover page                │
│ editor           │ chapters         │ Auto-built TOC            │
│ Slash commands   │ Cover + blurb    │ Prev / next nav           │
│ AI brainstorm    │ Sections /parts  │ Reading progress          │
│ Markdown import  │ Slug + URL plan  │ Estimated reading time    │
│ Convert posts→   │ Status: draft /  │ Print / EPUB / PDF export │
│   chapters       │   published       │ Share-anywhere snippets   │
└──────────────────┴──────────────────┴───────────────────────────┘

4. Pillar 1 — Build 📚

4.1 Authoring reuses the existing editor 100 %

A chapter is a long-form document. The blog editor already does this. No new editor work.

What changes:

  • New entry point: /dashboard/blog/books/[bookId]/chapters/[chapterId] (instead of /blog/[postId]).
  • Editor's right panel shows book context instead of post-level SEO: parent book name, chapter position, prev / next chapter links, "jump to TOC" button.
  • "Save & next chapter" button — the most-used affordance when writing a book in a flow.

4.2 New authoring features (book-specific)

Feature Description
Outline-first mode Spawn a book by listing chapter titles. Each becomes an empty chapter. Jump in to write any one.
AI outline Brain prompt: "Generate an 8-chapter outline for Pricing for Indie Founders." Returns titles + 1-line synopses. Editable.
Convert posts → chapters Multi-select 6 posts, "Compile into book." Creates a draft book with each post as a chapter. Manual ordering after.
Markdown import per chapter Existing MarkdownImportDialog works as-is.
"Continue from last chapter" When the editor opens, jump to the last-edited chapter — not the cover.
Cross-chapter links Slash command /chapter autocompletes chapter titles within the same book. Generates a stable internal link that survives reordering.
Footnotes New TipTap extension. Books need them, posts rarely do. Renders as a numbered list at chapter end.
Reading-time estimate per chapter Word count → minutes, shown inline.

5. Pillar 2 — Bind 🔗

The "book object" itself.

5.1 Book entity

A book is not "a tag that groups posts." It's its own first-class entity with:

  • id, tenantId, title, subtitle, slug, description
  • coverImageUrl — required for publish
  • aboutAuthorJson — TipTap doc for an "About the author" block on the cover
  • status: draft / published / archived
  • publishedAt, updatedAt
  • theme: editorial (default), manual, playbook, textbook — preset reading-UI looks
  • accessMode: public / email_gated / paid (see §8)
  • chapterCount, totalReadingMinutes (denormalized, recomputed on publish)

5.2 Chapters

Three options for the chapter table:

Option Pro Con
A. Reuse blog_posts + add bookId + orderIndex Zero new tables. Editor pages work. Posts list now mixes posts with chapters; adds filters everywhere. Chapters get scheduling / SEO panels they don't need.
B. Separate blog_book_chapters table Conceptual cleanliness. Posts list stays simple. Chapters get a tailored editor panel. Re-implements some persistence (small). Two content tables to query.
C. Polymorphic blog_documents Most extensible. Massive refactor. Premature.

Recommendation: B. A chapter is conceptually different enough that the schema split saves more pain than it costs. ~80 lines of new CRUD; the editor component is shared.

blog_book_chapters {
  id              text PK
  bookId          text FK → blog_books.id  ON DELETE CASCADE
  tenantId        text  -- denormalized for tenant-isolation filters
  title           text
  slug            text   -- unique per book, not per tenant
  contentJson     jsonb  -- TipTap doc
  excerpt         text
  orderIndex      int    -- 0-indexed, monotonic, dense
  partTitle       text?  -- optional grouping ("Part I — Foundations")
  status          text   -- "draft" | "published"
  readingMinutes  int    -- denormalized
  createdAt, updatedAt
}

5.3 Book management UI

Single-page Book Editor, not a list:

┌─────────────────────────────────────────────────────────────┐
│  ← All books         BOOK · DRAFT          [Publish book]   │
│                                                             │
│  ┌────────────┐    Pricing for Indie Founders               │
│  │  COVER     │    A 12-chapter playbook for figuring out   │
│  │            │    what to charge before you ship.          │
│  │            │                                             │
│  │            │    by Dipanshu Vishwakarma                  │
│  └────────────┘                                             │
│                                                             │
│  TABLE OF CONTENTS                                          │
│  ┌──────────────────────────────────────────────────────┐  │
│  │ ⠿ 01  Why pricing is broken      ✓ DRAFT   12 min   │  │
│  │ ⠿ 02  The three pricing axes     ✓ DRAFT    8 min   │  │
│  │ ⠿ 03  Anchoring without lying    ◌ EMPTY    —       │  │
│  │ ⠿ 04  Free trials, again          ◌ EMPTY    —       │  │
│  │                                                      │  │
│  │  + New chapter    + Add part heading                │  │
│  └──────────────────────────────────────────────────────┘  │
│                                                             │
│  ESTIMATED READ TIME · 1 h 14 m   ·   12 chapters           │
└─────────────────────────────────────────────────────────────┘
  • Drag handle () reorders chapters; saves orderIndex on drop.
  • Click chapter → opens the existing editor in book context.
  • Chapter status badges: EMPTY / DRAFT / READY (passed minimum criteria) / PUBLISHED.
  • "Publish book" only enabled when ≥ 1 chapter is READY and book has cover + title + slug.

6. Pillar 3 — Read 📖

This is where the project earns its differentiation.

6.1 URL structure

/book/:bookSlug                    → cover page, TOC, chapter 1 CTA
/book/:bookSlug/:chapterSlug       → chapter reader
/book/:bookSlug/about              → optional about page
/book/:bookSlug/print              → single-page printable HTML (CSS print)
/book/:bookSlug.epub               → static EPUB download (built on publish)
/book/:bookSlug.pdf                → static PDF download (built on publish)

Every chapter is a canonical URL for SEO. The book home page is the canonical entry. <link rel="prev/next"> ties chapters together.

6.2 Reader UI

Three-column layout that collapses gracefully:

┌────────────┬───────────────────────────────────────┬────────────┐
│            │                                       │            │
│  TOC       │   Chapter 03                          │  ON THIS   │
│            │   Anchoring without lying             │  PAGE      │
│  01 Why ✓  │                                       │            │
│  02 Three✓ │   Lorem ipsum dolor sit amet,         │  · Why     │
│ ▶03 Anchor │   consectetur adipiscing elit. Sed   │    anchors │
│  04 Free   │   do eiusmod tempor incididunt ut     │  · How     │
│  05 ...    │   labore et dolore magna aliqua...    │    not to  │
│            │                                       │  · Tactics │
│            │                                       │            │
│ 26% read   │   ─────────── continue ───────────    │            │
│ ▰▰▰▱▱▱▱▱   │                                       │            │
│            │   Next: Free trials, again      →    │            │
│            │                                       │            │
└────────────┴───────────────────────────────────────┴────────────┘
Element Detail
Left rail (TOC) Sticky. Shows part headings if defined. Indicates current chapter, completed chapters (✓), and overall reading progress (% based on chapters viewed).
Center column Pure content. Generous line-height, serif option, dark mode. Same TipTap renderer as blog.
Right rail Auto-built "On this page" from H2/H3 headings inside the chapter.
Footer of every chapter Big "Next: chapter title →" button. The single most-clicked element on a book.
Reading progress Stored in localStorage for anonymous readers; in contact_book_progress if reader is identified.
Reading time "12 min read" on every chapter, "1 h 14 m total" on the book home.
Quote-to-share Highlight any sentence → floating "Share quote" button → generates a 1200×630 OG image with that sentence and a link back.
Keyboard nav j / k next/prev chapter, ? opens TOC overlay, t toggles theme.

6.3 The cover page

The cover page is the landing page of the book, not a stub:

  • Hero: cover image, title, subtitle, author, "Start reading →" CTA
  • Stats strip: chapters · total reading time · last updated
  • Long blurb (TipTap doc — author can format it like a back-cover)
  • Sample paragraph from chapter 1 (auto-pulled, ~120 words) with "Continue →"
  • TOC preview (full table of contents)
  • Optional: "Get notified when next chapter drops" form (auto-creates a Vlozi Form, ties into Newsletter)
  • Author block (photo, bio, "More books by")

6.4 SEO

  • BookMetadata schema.org type on cover page; Article with isPartOf: Book on chapter pages.
  • Single sitemap entry per book + per chapter.
  • og:type=book on cover, og:type=article on chapters.
  • All chapter pages cross-link to siblings via <link rel="prev/next"> and a structured "part of book" header.
  • One canonical book URL means search engines treat the book as a unit; chapters get long-tail traffic.

7. Distribution Models

The same book can ship in any of these modes — picked at publish time, switchable later.

7.1 Public (free, open web)

  • Default. All chapters readable by anyone.
  • Best for: brand-building, lead-magnet books, open source documentation.

7.2 Email-gated (the wedge model)

  • Chapter 1 + 2 free, chapters 3+ require email.
  • Email submission auto-subscribes to Vlozi Newsletter (this is why books and newsletter are bundled).
  • Drip option: "Send one chapter per day to my inbox" — turns the book into a 12-day nurture sequence.
  • Best for: lead-gen, audience-building.

7.3 Paid (Stripe-backed)

  • Whole book behind a one-time purchase.
  • Stripe Checkout integration (vlozi-managed; no separate Stripe dashboard).
  • Buyers get a magic-link login to read on any device + EPUB / PDF download.
  • Refund support.
  • Best for: paid playbooks ($10–$50 range — not enterprise).

7.4 Bundled (newsletter-member benefit)

  • "Free for paying newsletter members."
  • Ties into existing newsletter paid tier.
  • Reader's identity comes from newsletter cookie/session.

All four modes use the same book object — the access policy is a config flip, not a re-author.


8. AI Integration (Brain + Books)

The Brain already exists. Books amplify it.

8.1 Authoring assistance

  • "Outline this book for me" — given title + 2-line description, Brain returns 8–14 chapter titles with synopses.
  • "Continue this chapter" — extend the current chapter in author's style.
  • "Convert these blog posts to chapters" — Brain reads selected posts, normalizes voice, suggests chapter ordering and one-paragraph chapter intros that bridge them.
  • "Suggest cross-chapter links" — Brain scans all chapters, surfaces opportunities to cross-link concepts ("Chapter 7 mentions 'anchoring' — link to Chapter 3 §2?").

8.2 Reader-side AI

  • "Ask this book" — a chatbot at the book level (per-book context, not site-wide). Reader can ask "what does the author say about discounts?" and get a sourced answer with chapter links. Uses existing chatbot infra, scoped to one book's knowledge.
  • "TL;DR this chapter" — collapsible auto-summary at the top of every chapter, generated on publish, cached.
  • Personalized chapter recommendations — for paid books, "Based on what you've read, you might want to skip to Chapter 9."

8.3 Author-side analytics

  • Brain weekly digest: "This week, 142 readers started Pricing for Indie Founders; 38 finished chapter 1; 6 finished the book. Top quotes shared: ... Top exit chapter: 4 (consider revising)."
  • "Where readers drop off" — heatmap chapter-by-chapter.

9. Schema Sketch

CREATE TABLE blog_books (
  id                 text PRIMARY KEY,
  tenant_id          text NOT NULL,
  title              text NOT NULL,
  subtitle           text,
  slug               text NOT NULL,
  description        text,
  cover_image_url    text,
  about_author_json  jsonb,
  status             text NOT NULL DEFAULT 'draft',
  theme              text NOT NULL DEFAULT 'editorial',
  access_mode        text NOT NULL DEFAULT 'public',
  -- Denormalized for cover page rendering
  chapter_count      integer NOT NULL DEFAULT 0,
  total_reading_min  integer NOT NULL DEFAULT 0,
  published_at       timestamp,
  created_at         timestamp NOT NULL DEFAULT now(),
  updated_at         timestamp NOT NULL DEFAULT now(),
  UNIQUE (tenant_id, slug)
);
 
CREATE TABLE blog_book_chapters (
  id              text PRIMARY KEY,
  book_id         text NOT NULL REFERENCES blog_books(id) ON DELETE CASCADE,
  tenant_id       text NOT NULL,
  title           text NOT NULL,
  slug            text NOT NULL,
  content_json    jsonb NOT NULL,
  excerpt         text,
  order_index     integer NOT NULL,
  part_title      text,
  status          text NOT NULL DEFAULT 'draft',
  reading_minutes integer NOT NULL DEFAULT 0,
  created_at      timestamp NOT NULL DEFAULT now(),
  updated_at      timestamp NOT NULL DEFAULT now(),
  UNIQUE (book_id, slug)
);
CREATE INDEX ON blog_book_chapters (book_id, order_index);
 
CREATE TABLE blog_book_progress (
  contact_id    text NOT NULL,
  book_id       text NOT NULL REFERENCES blog_books(id) ON DELETE CASCADE,
  last_chapter  text,    -- chapter id
  chapters_done text[],  -- chapter ids
  percent       integer NOT NULL DEFAULT 0,
  updated_at    timestamp NOT NULL DEFAULT now(),
  PRIMARY KEY (contact_id, book_id)
);

For anonymous readers, progress lives in localStorage keyed by book slug; on email submission it's merged into blog_book_progress for the resolved contact.


10. SDK & Integration

@vlozi/blog already exposes posts. Add the books surface:

import { vloziBlog } from "@vlozi/blog"
 
const blog = vloziBlog({ apiKey: "..." })
 
// List published books
const { books } = await blog.books.list()
 
// Fetch a book with its TOC
const book = await blog.books.get("pricing-for-indie-founders")
 
// Fetch a chapter
const chapter = await blog.books.getChapter(
  "pricing-for-indie-founders",
  "anchoring-without-lying"
)
// React components — drop-in book pages
import { BookCover, BookReader, BookTOC } from "@vlozi/blog/react"
 
<BookCover slug="pricing-for-indie-founders" theme="editorial" />
<BookReader slug="pricing-for-indie-founders" chapter="anchoring-without-lying" />

Customers can compose their own pages from these or use the prebuilt <VloziBookSite /> mounted at one route.


11. MVP Scope (the minimum that ships)

Cut to the bone for a 2–3 week build:

Layer In Out
Schema blog_books, blog_book_chapters blog_book_progress (use localStorage for anon only)
Backend CRUD for books + chapters, reorder endpoint, public read endpoint EPUB / PDF export, paid mode, "ask this book" chatbot
Authoring Book editor with TOC drag-reorder, chapter editor (reuses existing), cover image picker, publish AI outline, "convert posts to chapters", footnotes, cross-chapter slash command
Reader Cover page, TOC sidebar, chapter content, prev/next, reading time, anonymous progress Right-rail "on this page", quote-to-share, keyboard nav, theme picker, dark serif theme
SDK books.list/get/getChapter, <BookReader /> React component <VloziBookSite /> all-in-one mount
Distribution Public mode only Email-gated, paid, newsletter-bundled
SEO Book schema.org, sitemap, prev/next, OG images Auto-generated quote OG images

Result: a polished free book reader that beats anything on the market for indie publishing. Email-gated and paid modes layer in v1.6 / v1.7 once the core reader is real.


12. Build Order (phased)

Phase Scope Estimate
P1 — Backend Tables, CRUD, reorder endpoint, public read endpoint 3–4 days
P2 — Book editor (admin) Book list, book editor (TOC drag, chapter create), reuse blog editor under /book/:id/chapter/:cid 4–5 days
P3 — Public reader Cover page, chapter reader, TOC sidebar, prev/next, reading progress (localStorage) 4–5 days
P4 — SDK + components @vlozi/blog book methods, <BookReader /> / <BookCover /> React components 2 days
P5 — Polish + docs Empty states, share-quote OG, sitemap, schema.org, integration guide 2 days
Total MVP ~3 weeks
Post-MVP (v1.6+) EPUB/PDF export, AI outline, email-gated, "ask this book", paid mode, contact_book_progress layer over months

13. Risks & Open Questions

Risk / Question Discussion
Scope creep before launch Books should NOT block the blog launch. Confirm: blog v1 ships first, books ship as v1.5 once blog has been used by at least 1 real author.
"Is a book actually different from a series?" Yes — different reading interface, single canonical home, ordered, single cover, single download. A "series" can be a degenerate book (1 chapter), but conceptually they're different surfaces. Don't unify the abstractions.
Pricing model for books on vlozi Pre-launch decision: do paid books take a vlozi cut? Open question — Stripe Connect requires a fee structure call. Default assumption: 0 % cut for v1.5, monetize via plan tiers.
EPUB / PDF render quality EPUB is a known-tractable problem (pandoc-based pipeline on a worker). PDF is harder for non-trivial layouts. Recommend: ship "print-friendly HTML" first (/book/:slug/print), defer EPUB/PDF to v1.7.
Multi-author books Defer. Single-author covers 95 % of the founder/creator audience.
Book translations / multi-language Defer. Out of v1.5/v1.6 scope.
Discoverability across all vlozi books Could there be a vlozi.app/books directory of all public books? Possibly — but not in MVP.
What happens if a chapter is deleted while a contact has progress? chapters_done is text[] — orphan ids are tolerated. Render code skips unknown chapters.
Slug uniqueness scope for chapters Per-book, not per-tenant. unique (book_id, slug). Two books can each have a "chapter 1" with slug intro.
Editor reuse risk Some editor features (post SEO panel, scheduling, publish credits) are post-specific. The chapter editor must hide those panels conditionally. ~1 day of conditional rendering.

14. Why This Wins

  1. Reuse, don't rebuild: 80 % of the surface is already shipped. The new work is concentrated and high-leverage.
  2. No incumbent: No "Substack for books" exists. GitBook is for docs. Leanpub is for engineers. Substack series aren't books. Notion publishing isn't curated. The space is open.
  3. Compounds with the rest of the stack: Books → leads → contacts → newsletter → chatbot → forms. Every other vlozi product becomes more useful when there's a book funneling readers in.
  4. Pricing leverage: Free public books are a brand strategy. Paid books are a revenue line item. Both supported by the same infrastructure.
  5. Marketing flywheel: Vlozi ships its own books on the platform — Pricing for Indie Founders, The Solo Stack, Side-Project Marketing in 2026 — which double as marketing material and product proof.

15. Recommendation

Ship books — but not yet.

  1. Now (April 2026): Validate this doc with 1–2 trusted users who would write books. Tighten scope based on their reactions.
  2. Blog launch first: Get the blog engine in front of real users. Books inherit the blog's editor and SDK, so blog stability is a hard prerequisite.
  3. Books v1.5 — target 3 weeks after blog GA: ship public-mode-only books per §11. Use vlozi's own first book ("vlozi for founders") as the dogfooding case.
  4. Books v1.6+: layer email-gated mode, then paid, then AI features, in that order — each unlocks a clear new revenue or growth lane without bloating the core reader.

This is the right idea, at the wrong time to start it now, but at the right time to design it now. Park this doc as the canonical spec, finish blog v1, and the moment blog is breathing on its own, books become the obvious next ship.


16. Addendum — Series (the lightweight cousin)

While drafting this doc, an obvious smaller idea surfaced: series. Not the same thing as a book, but the precursor that should ship first.

16.1 The post → series → book ladder

Tier What it is Commitment Reader expects
Post One self-contained article Hours of writing A read in one sitting
Series Ordered group of standalone posts under a shared theme A few weekends "Part 3 of 7 — there's more before and after this"
Book A polished publication with its own home, cover, and reading interface Weeks to months A coherent, finished work to read end-to-end

A series post is still a regular blog post — dated, indexed for SEO, readable standalone, lives on the same /blog/[slug] URL it always did. The only difference: it knows it belongs to a series, and the public site shows that context.

A book chapter is content that only makes sense inside its book — no standalone life, no standalone URL outside the book namespace, no dated newsletter feel.

Don't conflate these. Posts that get tagged into a series stay posts. Chapters that go into a book leave the post world.

16.2 Why series is the right thing to ship before books

  • Tiny scope. ~1–2 days. Reuses 100 % of the post infrastructure.
  • Solves a real, current problem — the "reader lands on Part 4 with no idea Part 1 exists" Substack-series failure mode I called out in §1.
  • Authors will use it immediately. Anyone who has written 3+ related posts wants this.
  • Stepping stone for books. Series proves the "ordered content" UX cheaply. By the time books ship, we already know how authors think about ordering, how readers navigate ordered content, and what the prev/next affordance looks like in the wild.
  • Doesn't crowd books. They sit on different parts of the commitment ladder; no overlap.

16.3 What's new (everything else stays)

Layer Change
Schema New blog_series table (id, tenantId, title, slug, description, coverImageUrl?, status); new pivot blog_post_series (postId, seriesId, orderIndex) — many-to-many because a post could theoretically appear in two series, though we'll start with at-most-one in the UI.
Editor One field in the post-settings sheet: "Add to series" combobox. Shows "Part 3 of 7 in Building in Public" once selected, with a drag-handle to reorder within the series.
Series admin page /dashboard/blog/series — list + create + reorder, mirrors the categories/tags pages but with drag-to-reorder.
Public reader Two surfaces:
(a) Series index page at /blog/series/:slug — list of posts in order, with a 1-paragraph series description at the top. (b) A breadcrumb-style header on each post in a series: "Part 3 of 7 in Building in Public" with prev/next buttons at the end of the post.
SEO <link rel="prev/next"> tying ordered posts together, like books do. The series index page is the canonical hub.
SDK blog.series.list(), blog.series.get(slug) (returns series + posts in order), <SeriesIndex /> and <SeriesNav /> React components.

16.4 Schema sketch

CREATE TABLE blog_series (
  id              text PRIMARY KEY,
  tenant_id       text NOT NULL,
  title           text NOT NULL,
  slug            text NOT NULL,
  description     text,
  cover_image_url text,
  status          text NOT NULL DEFAULT 'draft',  -- 'draft' | 'published'
  created_at      timestamp NOT NULL DEFAULT now(),
  updated_at      timestamp NOT NULL DEFAULT now(),
  UNIQUE (tenant_id, slug)
);
 
CREATE TABLE blog_post_series (
  post_id      text NOT NULL REFERENCES blog_posts(id) ON DELETE CASCADE,
  series_id    text NOT NULL REFERENCES blog_series(id) ON DELETE CASCADE,
  order_index  integer NOT NULL,
  PRIMARY KEY (post_id, series_id)
);
CREATE INDEX ON blog_post_series (series_id, order_index);

blog_post_series is a pivot, not a column on blog_posts. Cheap insurance against the day a post belongs to two series (a "Building in Public" post that's also part of a "2026 Year in Review" series). UI initially enforces at-most-one — DB doesn't.

16.5 URL pattern

/blog/[slug]                       → unchanged. Posts in a series gain a header banner.
/blog/series                       → directory of all published series.
/blog/series/[seriesSlug]          → series index — description, ordered post list.

16.6 Reader UX on a series post

┌──────────────────────────────────────────────────────────┐
│ ← Building in Public ·  Part 3 of 7                  │
│                                                          │
│  Why I deleted my analytics tool                        │
│  Apr 12, 2026 · 6 min read                              │
│                                                          │
│  ──────────────  post content  ──────────────           │
│                                                          │
│  ...                                                     │
│                                                          │
│  ──────────────  series footer  ──────────────          │
│                                                          │
│  ← Part 2: The first 100 users (and what they cost me)  │
│  → Part 4: Picking the wrong feature, on purpose        │
│                                                          │
│  See all 7 parts in *Building in Public* →              │
└──────────────────────────────────────────────────────────┘

Two extra UI elements on a regular post: a top breadcrumb showing position, and a footer block showing prev/next + "view full series."

16.7 Build order

Phase Scope Estimate
S1 — Schema + CRUD blog_series, blog_post_series, admin endpoints (list/get/create/update/delete + reorder) 0.5 day
S2 — Admin UI Series list page (mirrors Categories), series detail page with drag-to-reorder posts, "Add to series" combobox in post settings sheet 0.5–1 day
S3 — Public surfaces Series index page, series breadcrumb + footer on posts, prev/next links, SEO tags 0.5 day
S4 — SDK blog.series methods + <SeriesIndex /> / <SeriesNav /> React components 0.25 day
Total ~1.5–2 days

16.8 Why not just use tags?

A category or tag is unordered set membership. It can't answer "what's after this post?" The whole point of series is the ordering — you can't fake that with tags. Different mechanic.

Ship series in blog v1.0 if there's a half-day of slack, else v1.1 immediately after launch. Books still target v1.5 — series doesn't change that timeline, it just makes the blog noticeably better at ordered content well before books arrive.

Blog Engine