import { Link } from "@tanstack/react-router"; import { FileText, ChevronRight, Circle, Search, Pilcrow, Heart } from "lucide-react"; import { useDocumentTitle } from "@/client/hooks/use-document-title"; const STACK = [ { name: "Workers", role: "Hono router + auth" }, { name: "D1", role: "users, pages, memberships" }, { name: "Durable Objects", role: "one per document" }, { name: "R2", role: "uploads" }, { name: "Queues", role: "search indexing" }, ] as const; const MOCK_SIDEBAR_PAGES = [ { title: "Getting Started", depth: 0, active: false }, { title: "Architecture", depth: 0, active: true }, { title: "Workers API", depth: 1, active: false }, { title: "Data Model", depth: 1, active: false }, { title: "Deployment", depth: 0, active: false }, ] as const; export function LandingPage() { useDocumentTitle(undefined); return (
{/* Header */}
bland Docs on Cloudflare
{/* Hero */}

Write together. Ship on Cloudflare.

A block editor with real-time collaboration, nested pages, full-text search, and AI writing help that stays out of the way. Runs entirely on Cloudflare Workers.

Invite only — ask your teammate for a link Sign in
{/* Product mockup */}
{/* Window chrome */}
bland.tools
{/* App body */}
{/* Sidebar mock */}
My workspace
{MOCK_SIDEBAR_PAGES.map((p) => (
))}
{/* Editor mock */}

Architecture

The platform runs on Cloudflare Workers with Durable Objects for real-time document collaboration.{" "} Rachel {" "} Each document is a lightweight server that handles WebSocket connections, Yjs sync, and persistence.

{/* Code block mock */}
                      export class{" "}
                      DocSync extends{" "}
                      YServer {"{"}
                      {"\n  "}
                      async onSave
                      {"() { "}
                      /* ... */
                      {" }"}
                      {"\n}"}
                    
{/* Bottom gradient mask */}
{/* Features — bento grid */}
{/* Collaboration — dominant, full-width lead */}

Real-time collaboration

One Durable Object per document. Yjs-powered presence, live cursors, awareness — the document{" "} is the server. No broker, no third-party sync service, no drift.

Alex · line 4 Rachel · commented Sam · idle

The Worker routes each connection to a matching{" "} Alex {" "} Durable Object that holds the Yjs document in memory, fans updates to every connected client, and persists to SQLite on save.{" "} Rachel

{/* AI writing help — secondary */}

AI writing help

Workers AI

Select a paragraph, pick an intent, accept the edit. Streams into the editor — and keeps quiet about it.

Each document is{" "} its own{" "} lightwieght {" "} server handling WebSocket connections .

{[ { label: "Proofread", desc: "Fix grammar & spelling", active: true }, { label: "Formal", desc: "More professional tone" }, { label: "Casual", desc: "More conversational" }, { label: "Simplify", desc: "Easier to read" }, ].map(({ label, desc, active }) => (
{label}
))}
  • /ai continues at the cursor
  • Summarize asks the page a follow-up
{/* Search — tall card */}

Full-text search

FTS5-powered instant search across every page in the workspace.

{/* Mini visual — search UI */}
{[ { title: "Architecture", match: "…Durable Objects for real-time…" }, { title: "Workers API", match: "…each Durable Object handles…" }, { title: "Data Model", match: "…Object storage and D1…" }, ].map((r) => (

{r.match}

))}
{/* Block types — compact card */}

Rich block editor

Headings, lists, code blocks with syntax highlighting, tables, images, toggles, and more — all from a slash menu.

{/* Mini visual — block sampler */}
{/* Code block */}
Code
                    const doc ={" "}
                    new Y.Doc()
                  
{/* Task list */}
Tasks
✓ Set up D1 schema
Wire search indexer
Deploy to prod
{/* Table */}
Table
Binding Type
DB D1
R2 Bucket
DocSync DO
{/* Stack ribbon — what each binding actually does */}
{STACK.map(({ name, role }) => (
{name}
{role}
))}
{/* Footer */}
); }