Build a Notion alternative
Build a Notion alternative as a notes tree you can finish. Realtime blocks, databases, and permission graphs are the other product. Here is the split.
You can build a Notion alternative that looks like a notes app. Nested pages, markdown, a search box. The catalog calls Notion a team wiki, not a new operating system.
You pay $12–$24 a person a month. The card says that price scales with the team. The verdict is “Scope is everything”.
Yesterday I wrote about a DocSend alternative. That was a regular backlog post. This one is a teardown: what is public about the architecture, which half you can vibe-code, and which half you should leave alone.
I already wrote why you should not start by rebuilding Notion. This is the architecture behind that advice.
Build a Notion alternative as a wiki
The Notion page splits the work into three rows.
The first row is 10 hours. Markdown pages, nesting, search. That is a tree of documents with a full-text index. You can vibe-code this.
The second row is 1 week. Revisions, mentions, stale-page tracking, OAuth login. Still a wiki. More screens, an audit trail, company login.
The third row is 3 months and up. Databases, live collaboration, granular permissions. The table labels that row as the part you should skip.
If you tell an agent to “clone Notion”, it aims at the third row. The useful v1 is the first one.
Here is the shape of the data. Nothing else has to exist at 10 hours.
const page = {
id: 'page-handbook',
parentId: 'page-company',
slug: 'handbook',
title: 'Engineering handbook',
body: 'Page the primary. Then write the incident note.',
owner: 'flavio',
reviewedAt: '2026-09-01',
}
A workspace, a page, a parent, a markdown body. Comments can wait. Mentions wait until someone needs to ping an owner.
The audience line on the page is a team of up to 20 people maintaining internal documentation. If that is you, a wiki is enough. If the company already lives in Notion, keep paying. The workspace is the source of truth, the same way Linear is for issues.
How does Notion work: everything is a block
Jake Teton-Landis wrote The data model behind Notion’s flexibility on May 18, 2021. I am summarizing that post, not reconstructing their schema. Treat the 2021 process as possibly dated, and the rules as the part that still matches the public API.
Everything you see in Notion is a block. Text, images, lists, a row in a database, the page itself. Each block has an id, a type, a bag of properties, a content array of child ids, and a parent id.
The id is a UUID v4. You can see a page’s id at the end of the URL. Changing the type does not wipe the properties. “Turn into” from a to-do to a heading keeps the checked flag sitting there unused, so turning it back still shows the check.
Indentation is structural. Pressing tab moves the block into a sibling’s content array, so the indent is a parent change, not a style.
Here is the same handbook line as a block. This is a sketch of the idea they published, not their code.
const block = {
id: '380c78c0-e0f5-4565-bdbd-c4ccb079050d',
type: 'to_do',
parent: 'page-handbook',
properties: {
title: 'Write the incident note',
checked: false,
},
content: [],
}
A 10-hour wiki does not need this. One page row with a markdown column is the honest v1. Copy the product rule, not the graph: stable ids, a parent pointer, no cycles when you move a page.
The mini-prompt on the page is “Move a page without creating a wormhole”. Walk from the proposed parent up to the root. If you meet the page you are moving, reject the write. Do that check in the same transaction as the re-parent, because two concurrent moves can each look safe alone.
The public API is that tree, paginated
Notion’s page content guide says a page’s content is a list of block objects. Pages are a special kind of block. You can pass a page id to GET https://api.notion.com/v1/blocks/:id/children.
Nested children are not inlined. If has_children is true, you recurse. They paginate breadth-first, 100 results at a time. Alicia Liu explained why in Creating the Notion API (January 20, 2022). Markdown cannot hold their formatting, and a depth-first dump of a deep toggle list has no bound. They shipped custom JSON instead.
A vibe-coded wiki needs neither endpoint. One person, one form post, a page render. Notion needs the tree because a heading, a to-do, and a database row are the same kind of record, and any of them can grow children.
Typing in one block used to be last-write-wins
Until 2025, two people editing the same paragraph could wipe each other.
How Notion handles concurrent editing with CRDTs (September 18, 2026) is the source. They wrote that until 2025, Notion wasn’t really collaborative. Pages felt collaborative because they are many blocks. Separate blocks could change at the same time. Two writes to the same block were last-write-wins. Offline made that worse, because a laptop that came back could clobber later edits to that block.
In July 2025 they deployed a sequence CRDT based on a Replicated Growable Array, plus Peritext-style operations for bold and links. They process millions of CRDT operations a minute, their number. Pressing Enter in the middle of a block still has to split text across two records while an offline insert aims at the old one. They invented text slices and search labels for that case. Angelique Nehmzow also talked about it at Sync Conf 2025.
I would not copy any of that. The 10-hour row does not need a CRDT.
The mini-prompt is “Two editors, one page, zero silent overwrites”. Every save carries the revision id the editor started from. If the page moved on, reject. Show both versions. Never last-write-wins on the body.
async function savePage(db, { pageId, baseRevisionId, body, author }) {
const page = await db.getPage(pageId)
if (page.revisionId !== baseRevisionId) {
return { ok: false, current: page }
}
const revision = await db.appendRevision({
pageId,
body,
author,
parentRevisionId: baseRevisionId,
})
return { ok: true, revisionId: revision.id }
}
await savePage(db, {
pageId: 'page-handbook',
baseRevisionId: 'rev-12',
body: 'Page the primary. Then write the incident note.',
author: 'flavio',
})
That is a wiki save, not Notion’s protocol. Catch the stale base. Append a revision. Leave the unsaved textarea alone so a conflict cannot eat the draft.
Linear made the same split. The tracker is a weekend. The sync engine is the other product. Figma is the same story with a canvas. Notion’s version is easier to see because they ran last-write-wins in production for years, then wrote the post.
Restore on the site is the other history rule: copy the old body forward as a new revision. Do not truncate the chain. The mini-prompt is “Restore is just another revision”.
Permissions walk up. Databases walk sideways.
Blocks inherit access from the blocks above them. Teton-Landis said they could not use the content array for that. Early on, a block could appear in more than one content array, so the ancestor would be ambiguous. Walking every content array to find parents is also slow on the client. They store an upward parent pointer and use that for permissions.
A later data-lake post, Building and scaling Notion’s data lake, says a block’s permission is not a static column. They construct it by walking ancestors to the workspace. That compute is why search and AI need a denormalized copy. Treat “they still walk the tree at request time in the product” as an educated guess. The public claim is that permission is a graph problem, not a flag on the row.
A 10-hour wiki should require workspace membership on every read and write, which is the guardrail on the page. Per-page guests and a private subpage under a public parent belong on the 3-month row.
Databases are the other skip. Liu’s API post is the clearest public picture. A database page has schema-defined properties. Relations are arrays of page UUIDs. Loading a relation means looking up each related page. A rollup then aggregates a property across those pages, live, like a spreadsheet formula. They paginate the relation with cursors, and they stuff partial sums into the cursor so Sum and Average can finish across batches. Median cannot.
That is Airtable, $24–$54 a person a month, not a wiki feature. The Notion non-goal is explicit: do not build databases, formulas, canvases, public sites, or real-time collaborative editing. If your wiki is already a CRM with rollups, you are starting the Airtable rebuild.
Offline is a forest of reasons
Raymond Xu wrote How we made Notion available offline on December 11, 2025. For years SQLite was a best-effort cache. Offline Mode had to download every record a page depends on and keep it fresh.
A page can be offline because you toggled it, favorited it, visited it recently, or marked its parent database. They keep a forest of offline trees so turning off one reason does not drop a page that still has another. Mark a database offline and they download up to 50 pages in the current view. Clients subscribe to a per-page channel and refetch on push. After a flight they compare lastDownloadedTimestamp to the server’s lastUpdatedTime and skip pages that did not move.
Obsidian Sync is the closer cousin if you already live in local markdown. That card is $5–$10 a month, 2 days for a first cut, and the hard part is still the merge. I would not add Notion’s offline forest to a team wiki.
In Herding elephants (October 6, 2021) they sharded Postgres because the block table was the product. Workspace id was the partition key. They landed on 480 logical shards across 32 physical databases. That write-up is five years old, so I will not claim the fleet still looks like that. At the audience size on the page, one database is enough.
What I would actually rebuild
I would rebuild Notion for a team of 20 or fewer who currently bounce the handbook through Google Docs. Nested markdown pages. Search that respects membership. Page owner and a stale date. Immutable revisions. A conflict screen when two people save.
I would host it next to the product and skip the CRDT. Databases and per-page guests stay off v1.
I would add the backlink index from the mini-prompt after the first week. Store links by page id, update them in the save transaction, so a rename changes zero rows.
I would not rebuild Notion for a company that already treats the workspace as the system of record. The block graph, the CRDT, the permission walk, the databases with live rollups, that is the product they bought. You will spend the 3-month row and still open the paid app on Monday.
The same v1 also replaces Slite, Coda, and Craft if what you wanted from those was a nested wiki. If you wanted Coda’s doc-as-app, that is the skip row.
Start with the first row
Go to Build your own Notion. Copy the AHA prompt, or the stack you already write.
Tell the agent the first estimate only: markdown pages, nesting, search. Ten hours.
If the rest of the team already lives in the shared workspace, keep paying.