Skip to content

Directory Layout and Kept Blocks

Brandon Young edited this page Nov 5, 2026 · 1 revision

Directory layout and kept blocks

One layout everywhere

pull, push, sync and watch all use the same layout. Each page is a folder holding a Markdown file of the same name, and sub-pages are sub-folders:

docs/                                  # markdown_root
└── Parent Page/
    ├── Parent Page.md
    ├── Meeting Notes/
    │   ├── Meeting Notes.md
    │   └── 2026-11-03/
    │       └── 2026-11-03.md
    └── Roadmap/
        ├── Roadmap.md
        └── Tasks.csv                  # a database on the Roadmap page

The parent page in your configuration is pulled too, as the top folder. Page titles are kept as they are in Notion, spaces included. Only characters that aren't allowed in file names are replaced.

How files map to pages

  • By notion_id, not by path. Each file records its page in the notion_id frontmatter field. If you move or rename a file, pull keeps writing to it where it is.
  • New files go under the page of their folder. A file without a notion_id becomes a sub-page of the page that owns its folder. Both Parent Page/Notes.md and Parent Page/Notes/Notes.md become sub-pages of Parent Page.
  • Missing folder pages are created. If a folder has no page file, a page named after the folder is created and saved as Folder/Folder.md.
  • Duplicate titles don't collide. Two sibling pages with the same title get separate folders, such as Title (1a2b3c4d).
  • Some paths are never synced: files outside markdown_root, paths matching excluded_patterns, and hidden folders such as .git and .notion-sync.

Sub-pages and databases are links

A page's file links to its sub-pages and exported databases. It doesn't include their content (see issue #7):

[📄 Meeting Notes](<./Meeting Notes/Meeting Notes.md>) <!-- notion:child_page 1e369015-c240-8021-a930-e3ee43ea0ef6 -->
[🗃️ Tasks](./Tasks.csv) <!-- notion:child_database 1e369015-c240-8021-a930-e3ee43ea0ef7 -->

Kept blocks

Some blocks are never deleted or changed by a push:

  • sub-pages (child_page) and databases (child_database)
  • images hosted by Notion
  • blocks Markdown can't express: columns, synced blocks, templates, embeds, video, audio, files, PDFs, link previews, links to pages
  • any block that contains one of the above

In your file, each kept block is a marker line:

<!-- notion:embed 1e369015-c240-8021-a930-e3ee43ea0ef8 -->

When a kept block has text, such as a column list, you get a read-only copy of it between begin and end markers:

<!-- notion:column_list 1e369015-... begin -->
...text from the columns...
<!-- notion:end column_list 1e369015-... -->

On push, these lines are not uploaded. They only mark where the kept block sits, so the content you write around them lands in the right place.

  • Edit kept blocks in Notion. Changes you make between begin and end are not uploaded, and push warns you about them.
  • You can move marker lines to reorder your text around a kept block.
  • Deleting a marker line doesn't delete the block in Notion.

How push edits a page

Push compares Notion's blocks with your Markdown one block at a time, using only what Markdown can express (text, formatting, links, block type), and lines them up:

  1. Unchanged blocks are left alone.
  2. Edited blocks of a type that can be updated are changed in place (PATCH), and their children are compared the same way. Block IDs, comments and links to them survive.
  3. Leftover blocks are inserted at the right position, or deleted.

Inserts happen first, then in-place updates, then deletes. If any step fails, the inserted blocks are deleted and updated blocks get their old content back.

Clone this wiki locally