Gleamr keeps two copies of every article you save: a parsed version for reading and search, and the original HTML for the day the parser got it wrong or the page stopped existing. Here is how that works in Postgres, and why a snapshot over 10 MB gets dropped instead of truncated.
Last reviewed: July 28, 2026.
Extraction is lossy, and you only find out later
Every read-later app runs saved pages through an extraction step. Strip the nav, drop the ads, keep the article. The clean version is what you read and what full-text search indexes.
The problem is that extraction fails quietly. A parser meets an unusual layout and silently drops a table, a code block, a figure caption. You don't notice at save time, because nobody proofreads a page they saved specifically to read later. You notice months afterward, when you open the article and something is missing. If the extracted version is all you stored, that content is gone. The original page may have changed, moved behind a paywall, or disappeared entirely.
I didn't want Gleamr's parser to be a single point of failure for someone's library, so the parser's output is treated as a view, and the raw HTML is treated as the source.
What actually gets stored
Each row in the articles table carries both representations:
content_blocks: the parsed article as a JSONB array of typed blocks (paragraphs, headings, lists, quotes, code, tables, media). This is what the reader renders and what search indexes.original_html: the page's served HTML, as fetched, before any extraction.html_snapshot_size: the snapshot's size in bytes, so we can show and monitor it without loading the whole thing.content_version: which version of the extraction pipeline produced the blocks.
The snapshot lives in a plain TEXT column. Postgres TOAST compresses large values out of the main table, so an average article snapshot adds little to row reads that don't ask for it, and queries that list your library never touch it.
content_version is the quiet workhorse here. When the extraction pipeline improves, we can re-run it against stored snapshots and regenerate the parsed blocks without refetching anything. Pages that died since you saved them still get the better parser.
The 10 MB rule: drop, don't truncate
Snapshots are capped at 10 MB per article. Most pages come in far under that; the ones that don't are usually not articles.
The important decision is what happens at the limit. A snapshot over the cap is dropped entirely and the drop is logged. The article still saves normally with its extracted content. What we never do is store the first 10 MB of a larger page.
A truncated snapshot is worse than no snapshot. It renders as a broken half-page while presenting itself as a faithful copy, and you'd discover the damage at the worst possible time, long after the original went away. An absent snapshot is at least honest about what you have.
Rendering someone else's HTML without getting owned
Storing raw HTML from arbitrary websites means storing whatever was in it, including scripts.
The parsed blocks go through two cleaning layers. On the server, every HTML-bearing block passes a bluemonday allowlist policy that permits basic text markup, links, images, and tables, and nothing else. In the browser, DOMPurify runs as a second layer before anything touches the DOM.
The raw snapshot is different. Sanitizing it would defeat its purpose, since the whole point is fidelity. So the snapshot is never injected into the app's DOM at all. The reader's "original view" renders it inside an iframe via srcDoc with a sandbox attribute that does not include allow-scripts. The browser refuses to execute any script in the snapshot, and the page renders as inert markup. You get the original layout without running the original code.
What a snapshot can't save
An HTML snapshot stores the markup, and only the markup. Images, stylesheets, and fonts are still references to the original servers. If the site dies, the text and structure survive in your library, and the images on the page die with the site.
That limitation is deliberate. Inlining every subresource turns a read-later app into a web archiver, with storage costs and crawling behavior to match. If you need standards-grade capture with every asset embedded, tools like SingleFile and WARC do that job properly, and I wrote up how to use them.
For the job Gleamr is built for, keeping the words you saved readable and findable years later, markup plus extracted content covers the failures that actually bite: parser mistakes, and pages that rot or grow a paywall after you saved them.
Where this shows up in the product
None of this needs configuration. Save an article and both copies are written; the reader's original view is there when the clean view looks off, and the JSON export gives you the parsed content of your whole library to take elsewhere.
Gleamr is a personal reading library with full-text search and data export. The free tier holds 50 articles if you want to see how the dual storage behaves with your own saves.