Last Updated: 2026-04-29 Status: Draft (idea validation) Service: extension of
apps/blog-serviceSDK: 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,descriptioncoverImageUrl— required for publishaboutAuthorJson— TipTap doc for an "About the author" block on the coverstatus:draft/published/archivedpublishedAt,updatedAttheme:editorial(default),manual,playbook,textbook— preset reading-UI looksaccessMode: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; savesorderIndexon 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
READYand 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
BookMetadataschema.org type on cover page;ArticlewithisPartOf: Bookon chapter pages.- Single sitemap entry per book + per chapter.
og:type=bookon cover,og:type=articleon 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
- Reuse, don't rebuild: 80 % of the surface is already shipped. The new work is concentrated and high-leverage.
- 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.
- 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.
- Pricing leverage: Free public books are a brand strategy. Paid books are a revenue line item. Both supported by the same infrastructure.
- 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.
- Now (April 2026): Validate this doc with 1–2 trusted users who would write books. Tighten scope based on their reactions.
- 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.
- 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.
- 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.
16.9 Recommended timing
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.