Hosted onbitcointuesdaymadrid.comvia theHypermedia Protocol

Column Layouts in the EditorUsers can't place content side by side in the editor. We're adding a /Columns command that creates horizontal layouts by extending the existing block system — no new block types, no backend changes.

    Problem

      Our editor forces all content into a single vertical stack. Every block — paragraphs, headings, images, code — sits directly above or below the next one. There is no way for users to place content side by side.

      This is a significant limitation. Users regularly need multi-column layouts for:

        Comparisons: two approaches, before/after, pros/cons

        Mixed media: text alongside an image or embed

        Structured sections: splitting a page into logically distinct areas

        Information density: making better use of wide screens instead of leaving margins empty

      Competing editors (Notion, Google Docs, Coda) all support columns or similar horizontal layout. Users who come from these tools expect it. Without columns, users resort to workarounds like embedding tables (that de do not support either) or creating images, both of which break the editing experience.

    Solution

      Documents are trees of BlockNode { block, children }. Blocks stack vertically — no way to place blocks side-by-side. We want two layout features: Fixed Columns (explicit column containers) and Flow Grid (items wrap into N columns). This document analyzes 3 solutions and recommends one.

      The Deciding Constraint: ProseMirror Node Hierarchy

      doc → blockChildren → blockNode+ → (block, blockChildren?)

        block nodes can ONLY contain inline* or ''cannot contain other blocks

        blockChildren is the only nesting mechanism, has a listType attribute

        All editor commands (Enter, Backspace, Tab, split, merge) assume this exact hierarchy

        New block types created via createReactBlockSpec are block group nodes — same constraint

      This means dedicated column node types outside the hierarchy (Solution B2) require refactoring 15-20+ editor files. And flat attribute markers (Solution C) fight ProseMirror's model entirely.

      Three Solutions Evaluated

        Solution A: childrenType: 'Columns' — RECOMMENDED

        1

          Add 'Columns' to HMBlockChildrenType. Renders children as side-by-side columns.

          blockNode (container, childrenType: "Columns")
            block:paragraph ("")                    ← empty, invisible
            blockChildren [listType='Columns']      ← render as flex row
              blockNode (column 1)
                block:paragraph ("")                ← empty, invisible
                blockChildren [listType='Group']    ← column 1 content
                  blockNode → paragraph ("Left text")
                  blockNode → image (...)
              blockNode (column 2)
                block:paragraph ("")
                blockChildren [listType='Group']    ← column 2 content
                  blockNode → paragraph ("Right text")
          

          DimensionRatingPM schema fitPerfect — no new node typesCRDT safetyHigh — each column is an independent RGA sublistEditor effortModerate — keyboard guards on ~6-8 handlersBackwards compatModerate — old clients stack blocks vertically

        Solution B: Dedicated Columns + Column Block Types

          Two new block types with explicit semantics (like Notion's column_list/column).

            B1 (within existing hierarchy): Functionally identical to A in ProseMirror — the dedicated type is just a semantic label on the block content node. Same effort as A.

            B2 (custom PM nodes): Requires refactoring getBlockInfoFromPos, getGroupInfoFromPos, nodeToBlock/blockToNode, all keyboard shortcuts, normalizeFragment, splitBlock/mergeBlocks/nestBlock/unnestBlock, side menu detection. 15-20+ files, high risk.

          Old clients show "Unsupported Block" for the column wrappers.

        Solution C: Flat Attribute Markers (columnGroupId, columnIndex)

          No structural changes — sibling blocks get column attributes, rendering groups them visually.

            CRDT fragile: concurrent edits interleave blocks, break group contiguity

            Editor: requires complex ProseMirror decoration plugin, fights the document model

            Best backwards compat but worst reliability

      Comparison

        A: childrenTypeB1: Dedicated (same PM)B2: Dedicated (new PM)C: FlatPM schema changesNoneNoneMajor (2 nodes)NoneEditor effort~6-8 guardsSame as A15-20+ filesPlugin rewriteCRDT safetyHighHighHighLowBackwards compatModerateModeratePoorBestRiskLowLowHighVery High

      Recommendation: Solution A

        Solution A wins across all dimensions. Same tree shape as Notion's model, expressed within our existing ProseMirror hierarchy. No new node types, no hierarchy changes. Can optionally layer B1 semantic naming on top later.

      Two Layout Features (Both Wanted)

        1. Fixed Columns (childrenType: 'Columns')

          Each child IS a column container with its own content blocks

          Column count = number of children (users add/remove explicitly)

          Optional columnWidths attribute for proportions

          Use case: side-by-side content sections

        2. Flow Grid (childrenType: 'Grid')

          Items wrap into N columns automatically (like Query block card view)

          columnCount attribute defines max columns, items stack/wrap

          Single flat list of children, CSS grid handles flow

          Use case: card grids, gallery layouts

        Both use the same pattern: new childrenType values + CSS layout in BlockNodeList.

      Mobile view concerns

      1

        For the Column layout we have two options:

          keep columns and add a horizontal scroll so readers can see all columns

          stack columns vertically so users don't have to scroll horizontally to see all the content.

        On the other hand, for Grid Layout, we will always stack items one of top of the other, and we will do exactly what happens with the grid view in query blocks.

        For columns, I prefer to just stack them vertically, I don't think its a bad expectation for readers. adding an extra horizontal scroll adds a bit more complexity to the layout that I prefer not to add.

      Notion Reference

        Notion uses column_list + column (Solution B). Rules: min 2 columns, min 1 child per column, width_ratio (0-1, sum to 1), no nested columns. They can do this because they built their editor from scratch — no ProseMirror constraint.

    Photo by Jesse Bauer on Unsplash

    Grid Layout — Implementation Plan