Hunter Chen
Engineering
Share this article
Hunter Chen
Engineering
Share this article

Mintlify pages can get incredibly large: changelogs, API references, data dictionaries, etc., can get into thousands and even tens of thousands of lines. Editing these files in our editor was once a trudge, but no longer. We've made massive improvements across many different interactions, including navigation, switching between WYSIWYG and markdown mode, and the text editing experience itself.
The greater the page, the greater the gain
Around 2,500 lines was when our previous editor began to subtly chug; editing text generally still had sub-100 ms latencies, but when it came to navigation and switching editing modes, that's when the sluggishness started to be felt. Naturally, this also means these are the sizes where the optimizations start to be felt, and they become greatly amplified as pages scale to 5,000 lines and beyond.
| Page size (lines) | Input latency (s) | Switch to markdown (s) | Back to WYSIWYG (s) | Opening page (s) |
|---|---|---|---|---|
| 2,500 | 0.09 → 0.05 | 0.83 → 0.27 | 0.42 → 0.16 | 1.0 → 0.57 |
| 5,000 | 0.06 → 0.03 | 0.98 → 0.25 | 0.65 → 0.21 | 1.4 → 0.76 |
| 10,000 | 0.11 → 0.04 | 1.7 → 0.45 | 1.3 → 0.31 | 2.9 → 1.0 |
Internally, we have a file dubbed "Nuclear Bomb". It is a 25,534-line behemoth, larger than 99.99% of all customer docs, built for the sole purpose of stress-testing our editor, utilizing dizzying combinations of all the different native components Mintlify has to offer. It was previously nearly unusable, but the experience has been salvaged by the new optimizations.
Here we see a 28x speedup to opening a page, a 14x speedup to key input latency, and a 26x improvement when switching into markdown mode. In an even larger 50,000-line page, we saw the first keystroke speed up from just under 13 s to 80 ms, a whopping 160x increase. We also see a similar 100x increase in speed when opening a 100,000-line file.
How did we achieve these gains? Some of them come from similar optimizations, and some are very different, but they almost all share a common theme: do fewer things.
Don't show what you can't see
Every "block" on the page was used to build a live, interactive preview the moment the page opened, so very simply, we just don't build the things we can't see. This technique is known as virtualization, and it happens in two places:
- Anything offscreen: We're not looking at anything that's not on screen, so don't render it. However, we still maintain a buffer of a few screen heights, so if the user scrolls, we have time to catch up and it isn't obvious that anything was skipped.
- Closed accordions: We stop rendering content inside an accordion unless you open it.
This meant that under specific scenarios in our "Nuclear Bomb", we went from over 5,000 React views rendered to just 2, which resulted in upwards of 10x speedups in opening a page.
Rebuild only what changed
Within the editor, your page is not just text. It's a tree of "blocks", made up of text and components. This tree is a representation of the underlying MDX text, and it is what you see, type into, and share with your collaborators when you use the editor. The files themselves, however, still remain as MDX files in Git.
Oftentimes, the editor also requires the underlying raw MDX, such as when checking if there are any changes that require saving, when editing in source/markdown mode, and also when writing and publishing to Git. In these cases, we need a serializer to convert the tree back into MDX. And in the case of looking for unsaved changes, we run the serializer on every keystroke. So, rather than degrading the user experience by reducing the number of times that serialization happens, we looked into how we could make it much faster.
The editor's document tree is immutable; suppose a page is composed of blocks A, B, C, and D, and a change is made to B. A new tree is created containing the original A, C, and D, and a new copy of B. The serializer, however, never took advantage of this, and walked and rewrote every block each time, even the ones that were the exact same objects as before. Now it remembers its work per block, recognizes unchanged blocks instantly, and only rewrites what changed.
How do we figure out what changed, though? We use two pointers: one walks the old and new blocks together from the top, the other from the bottom, and each stops at the first block that differs. Everything between those two points is the window that has to be rewritten. On occasion, we also need to serialize more than the window, as neighbouring blocks may also be affected, and in these cases, we grow the window on either side until both edges become safe to cut from. Now, we serialize just the window and keep an index of where each block starts and ends, which becomes paramount to the efficiency of future operations. And the window's new text replaces what was previously there, and every position after is shifted forwards.
Putting all these pieces together, an edit now only serializes the changed block itself, and sometimes its immediate neighbours, and splices them into the existing text. All unchanged blocks get a cheap, fixed-cost check to confirm they're unchanged and are left alone. This results in the first edit after load being up to 40x faster, and switching into source mode being up to 90x faster. But even with these changes, there are still occasions when a full-page serialization must take place, which presents us yet another opportunity to do fewer things.
Improving serializer time complexity
Serializing only what changed means large serializations run far less frequently, but they still run on any large change and when a page first loads. And this had a scaling problem of its own — the serializer walks the tree and appends MDX to its output as it goes, and along the way, it has to check the end of what it had just written. This is because Markdown has specific rules which depend on pieces of text that were just written, such as deciding how to deal with syntax like ** for bold and where to place newlines. Previously, every time it wanted to do one of these checks, it looked at the entire string from beginning to end. That meant it scaled quadratically: a page with twice as much text had roughly twice as many checks, with each check taking twice as long, meaning that twice the page was four times the work.
Instead, we now keep a list of the pieces of text, which is joined together only once at the end, with the serializer maintaining a small running summary of how the output ends: the last few characters, the number of trailing spaces, and the number of newlines the current block started with. Now every check just reads that summary instead of re-reading the full text, making each check constant in time, largely independent of document size. The result is that serialization scales linearly with page size, rather than quadratically.
In a file with 6,000 paragraphs, a full serialization went from about 1.4 s to about 22 ms, roughly 63x faster. And in a file with 20,000 paragraphs, it went from 13.9 s to 69 ms, about 200x faster. The page grew a little over 3x. The old serializer took about 10x longer, while the new one took about 3x longer: the difference between quadratic and linear scaling.
Doing less
Every single one of these optimizations ultimately came down to the same thing: doing less. Blocks you can't see aren't built, blocks you didn't change aren't rebuilt, and even the serializer got faster by not re-reading text it had already written. Together with some other optimizations not listed here, that's how we saw an overall 13x speedup on our Nuclear Bomb test file.
Of course, we're not done. There is still work done by the editor that it doesn't need to be doing, so we will continue doing a lot more of "less". In the meantime, see the changes for yourself, live at app.mintlify.com.
More blog posts to read

Inside Mentha, Mintlify's new design system
Mentha is Mintlify's new design system, built with constraints, baked-in patterns, a cooler mint palette, and agents in mind.
October 12, 2026Fay Dakrouri
Design

Mintlify Pro for Startups and Open Source Projects
Current YC companies get 12 months of Mintlify Pro free. YC alumni and eligible startups get 50% off Pro for 12 months. Non-commercial open source projects get Pro free forever.
October 6, 2026Harkirat Chahal
Growth