I recently launched a text editor for iOS that uses TextKit 2 and is highly performant with files of 5,000 lines (I tested with Moby Dick from Project Gutenberg). I made it between Aug 2025 and Apr 2026, development is ongoing.
Every keystroke is restyled in under 8ms: no debouncing, no delayed rendering. 20 rapid keystrokes are processed in 150ms with full restyling after each one.
Tag and boolean searches complete in under 20ms. Visible-range rendering is 25x faster than full-document styling. 120Hz screen refresh supported.
App file size was 722 KB for 1.0, and 1.1 with more features is looking like ~950 KB.
If I can do it on iOS then it's must be 10x easier on macOS.
It makes sense when the editor is a core feature of your paid product. I understand the sentiment.
But is not it strange that I would need 8 months & a "development is ongoing" mindset just to render Markdown (which is very secondary to the main app features, and mostly just a user convenience people expect in 2026) with a custom low-level solution, effectively playing hardcore engineer instead of building what I actually want to build?
Anyhow, my point is not that "it is impossible". My point in the article is that I understand why people choose web technologies over native for such things. They want to build products, not fight the system’s limitations.
Rendering text beyond ASCII is famously difficult to do; rendering formatted text is sometimes difficult to even make sense of (e.g. what should a style change in the middle of an Arabic word do? how about a selection boundary being moved with arrow keys?); rendering honest-to-goodness Markdown, which can technically include arbitrary HTML tags, is nowhere in the vicinity of a small project.
None of which is to say that you shouldn’t demand that a toolkit solve it for you, only that I understand why the RichEdit control reportedly had a separate team allocated to it in turn-of-the-millenium Microsoft. Working with a large amount of formatted text feels like it should be the most complicated feature of any UI toolkit and I shudder at the thought of even designing the API for it.
(A web browser is good at all this. It also has the API surface of a web browser.)
And some things will still be on you regardless. Did you know Android has two modes for text wrapping, one that won’t reflow the entire paragraph after a single-word change at the end and a different one whose results embarrass a typographer from half a millenium ago? That’s very much the correct way to do things, but if you’re streaming text in, it’s on you to decide whether you want subpar wrapping throughout or a layout jump whenever a paragraph break arrives. Most importantly, it’s on you to know the question exists; there are more, some more important than this one.
Having worked on an interactive novel in 2012 (NSString and attributes), low level glyphs (API deprecated) on a rogue-like, two chat apps (with markdown support for formatting) in SwiftUI, and an idle game using a mix of iOS tricks but all wrapped in SwiftUI.. I’m going to agree with how I summarized this response: skill issue.
It's likely the SwiftUI Mac implementation is subpar. SwiftUI-on-Catalyst might be a better choice for these applications, but it probably has other problems.
Usually performance was the reason for using native APIs rather than web views, but this doesn't seem to be true any more.
Browser rendering engines are pretty mature at this point, with significant GPU acceleration, and over a decade stress-testing by bloated web apps.
Meanwhile SwiftUI doesn't feel particularly fast. Apple's latest and greatest rewrite of System Preferences has dumbed down the UI to mostly rows of checkboxes, and yet switching between sections can lag worse than loading web pages from us-east-1.
It's SwiftUI that is at fault here[1][2], not native apps in general. I wrote my native app in Qt C++ and QML and showed that it is *significantly* faster and uses significantly less RAM than similar web apps[3]. So, no, web apps, in general, are slower and uses more resources than well-engineered native apps.
> It's SwiftUI that is at fault here, not native apps in general.
The article you cited is from 2022 and so is irrelevant, since SwiftUI's performance profile completely changed as of xOS 26.
Claims like "It's hard to build a performant SwiftUI app" get into skill-issue territory, but more importantly, the reality is there are only "SwiftUI-first apps". All non-trivial SwiftUI-first apps will also use UIKit/AppKit as needed, typically for capabilties that aren't yet available via SwiftUI.
iOS 26 is very late to have acceptable performance in the framework that Apple promotes as what you should use. It should have had good performance from the day it was introduced.
WebKit have had great performance for a very long time now.
Why would any startup dare to use tech that only now got fast? Why not go with the battle tested WebKit?
It is also much easier to develop and test html pages than Apple specific tech.
Can you point to a single performant, high-quality SwiftUI-first app with a messages-like chronological transcript and correct scrolling behavior on macOS? The problems with AppKit integration are real and should not be dismissed out of hand.
They even used the distinction “native-like” in the block editor article - which is really good, by the way and explains this distinction in more depth - but edited their comment now and that article is the third link and its anchored to the performance section so you won’t see that unless you scroll to the top.
Their point is more that SwiftUI has generally poor performance. Lots of native Windows frameworks have poor performance as well.
Native UI development is a minefield. If you want to build an app today that will still run in 20 years without a complete rewrite in the UI layer you should probably use wxWidgets if you are committed to native - even if only targeting one OS. But that model is really only appropriate for building traditional desktop apps. I don’t think the market would accept a Slack or Notion built that way today.
At this point on Win32 Qt might as well be the native UI. They did a better job of maintaining a coherent visual theme that says "Windows" and fits the design patterns than the actual owners of the platform.
> Browser rendering engines are pretty mature at this point, with significant GPU acceleration, and over a decade stress-testing by bloated web apps.
Even so, there is a stark difference, even more so on low-powered devices, between native apps and even the lightest of browser apps. I'm traditionally a web developer, but started developing native cross-platform applications the last 6-12 months, and the performance gap is pretty big even for simple stuff, strangely enough.
My experience too, and that's not even touching the disproportionately high RAM usage of frameworks like Electron. Sure, "unused RAM is wasted RAM", until the system starts swapping heavily because of the high RAM usage.
It doesn't even have to be old devices, there are still laptops being sold with 8GB of RAM in 2026.
Electron would be fine… if most of us were running just one electron app.
In reality we’re stuck with 2-5 electron/CEF apps plus whatever is running in browser tabs as well as whatever webviews system stuff are running, which all quickly pushes into VM paging territory on low memory devices.
> Browser rendering engines are pretty mature at this point, with significant GPU acceleration, and over a decade stress-testing by bloated web apps.
They suck on older hardware. Old Chromebooks are a dime a dozen and are decently spec'd light use or purpose-use machines. Browsers run like crap on them.
Web tech in general is responsible for a lot of unnecessary hardware turnover.
If you dig up an 18 year old Core 2 Duo box, upgrade its storage to a cheap SSD, and install Linux on it, it’s shocking how snappy and usable it is for most tasks… up until you open a web browser or Electron app. Then it all falls apart.
Had it not been for resource creep driven overwhelmingly by heavy web apps and Electron/CEF, there’d be little reason for most people to use anything more powerful than a Sandy Bridge machine and we could have laptops and smartphones with week-long battery life thanks to efficiency gains not needing to be consumed by performance increases.
> If you dig up an 18 year old Core 2 Duo box, upgrade its storage to a cheap SSD, and install Linux on it, it’s shocking how snappy and usable it is for most tasks… up until you open a web browser or Electron app. Then it all falls apart.
Can confirm. I'm typing this on such an old Core 2 Duo laptop running Debian and even with only 4GB of RAM and a mechanical HDD, it's still very fast for everything I do on it that doesn't involve Web browsers. Windows 10 is practically unusable on it however. The gazillion background things it insists on running bring it to a crawl, mostly stuck on disk I/O.
Agreed. I have a shitty laptop I bought at Micro Center a couple years ago for like $80. I bought it mostly out of curiosity since I wanted to see what an $80 laptop would be capable of.
I installed NixOS minimal on there and a few apps to develop, and it was actually quite usable. Then I installed Skype on there and it was horrible and laggy.
Well, maybe for simple web apps, but for complex applications there is a noticeable slowdown, I am not even talking about monsters such as jira, but well optimized apps such as vs code, there is a performance ceiling which is lower than for native apps.
TFA actually says the developer couldn't figure out how to do this with native APIs, not that they're slower: "But I still cannot make a simple thing work properly: a chat with Markdown & the ability to select a whole message."
Electron ultimiately sits on native APIs, and has its own performance costs on top of them.
Electron sits on native rendering primitives. Do you suggest that every developer who wants rich interactive text in their app should write a text rendering engine from the ground up?
If you account for dev time, ‘giving up’ and just doing it in a web renderer is totally valid. Choppier, more memory, bad if the device is failing, but 98% of the time that can be acceptable.
Native GUI dev, tho, can enable low-resource performant apps just by sticking the rudimentary OS is a way no higher app really can, and with capabilities that’d choke a web app. Load up a listbox with a few tens of thousands of items with custom rendering… as a fat client dev you’re only in a little trouble, on the web googlers making google apps force pagination a whole lot.
As a point of comparison: WPF was Microsoft’s attempt to nail the ‘best of the desktop & best of the web’ [And I would argue they effectively nailed it, as a specification.]. But, as a brown belt WPF dev and a blue/orange belt with Win32/MFC, the extra overhead related to WPF broke common scenarios we’d never think twice about on the true native side. The web was made for sharing Robotech technical manuals, OS GUI’s to pump all the rectangles as fast as hardware allows. Apples and oranges.
> If you account for dev time, ‘giving up’ and just doing it in a web renderer is totally valid.
For sure, all software is chock full of "best? no/works? yes" compromises. I object to the article framing of "I couldn't figure it out, therefore TextKit 2 does not play well with anything modern", which is a very silly conclusion.
Agreed, but no such conclusion or framing was stated, supported, or suggested in my comment.
I was agreeing and providing more context to the costs of building at a higher level, like Electron, and the limits even when applied by a unified vendor with incentives for high performance.
I added the word "article" before the word "framing" to make it clearer in my comment supporting your comment that the last half of my comment wasn't commenting on your comment.
Electron sits on top of Skia, which renders the text itself. It's designed to look like the host OS but it's not just asking the OS to draw text, because that's actually a lot slower than Skia.
SwiftUI, (but not specifically "SwiftUI", more of paradigm) is not the right tool to incremental changes of large portion of data, and SwiftUI specifically is very bad at it and offer no good API to make incremental changes more optimal. That's one of the reason behind why Apple to this day did not ship usable SwiftUI text view component.
I don't know where's the improvements, give it has all the limitations, now scaled to rich text. That's not the direction that this component should get. It need radical new API that is not much compatible with SwiftUI API
You must know far more about this than me, but as of iOS 26, `TextEditor` evolved into a true rich text editor (`AttributedString` binding, `AttributedTextSelection` rich text selection, custom/app-specific text attributes, etc). https://developer.apple.com/videos/play/wwdc2025/280/ Do you really think people should use web views instead of, say, STTextView?
Oh that hackjob explains some of the inconsistent, frustrating performance
(I do give them credit for some terrible usability elements that would delay a scammer if they had our elderly relative on the phone.)
The AppleScript that has to be written and rewritten to flip a simple switch in settings… (it’s telling the system to move around and click in the UI by count, and the count gets thrown off by what I now suspect to be unpredictable web view UI loading)
It's crazy that people think it's a good idea to throw away thousands of manyears of optimization (and millions of manyears of field testing in real world) just to... Idk, write a lesser text render engine?
A decision to move native because of the crisis seems like an expensive populist move to please not very solvent users. Why bother with that if many predict the RAM crisis will last merely until 2027?
That’s because SwiftUI isn’t particularly good, not because web rendering is as good as native. AppKit still runs circles around both, in performance and resource consumption.
This is the ActiveX/nacl/wasm/etc... argument recapitulated. For decades, people dithered about how to get fast code into browser environments such that it could be deployed safely.
Then the V8 team at Google just asked "well, what if we just made Javascript crazy fast instead?", and here we are. There's still room for native code in environments that don't map nicely to scalar scripting languages, but not a lot of room. Basically everyone is best served by ignoring that the problem ever existed.
It took the rendering side a little longer, but we're here nonetheless. There's still room for specialty apps with real need to exploit the hardware in ways not abstracted by the DOM (not 100% of it is games, but it's close to that). But for general "I need a GUI" problems? Yeah, just use Electron.
Meh. Flaming about this is so exhausting given that the war was already fought and we know who the winner is.
First, that's the typescript compiler, not typescript apps. And it was a ground-up rewrite effort (a very large one) with a specific eye toward improving the performance of the original, which was widely held to be sub-optimal for reasons entirely unrelated to implementation language. Suffice it to say that, hell no, you can't just transpile your code to Go and expect it to run faster. We all know it doesn't work like that.
But more broadly, landing with "Please don't use Electron" in the context of a comment about a MS product seems weird given the implementation framework of Microsoft's single most impactful new UI project of the last decade...
Just stop, basically. You lost. Use Electron. It works great and everyone else already does and proved you wrong.
> that's the typescript compiler, not typescript apps.
Of course it's the typescript compiler. What else is an implementation of "Typescript" that you could actually make faster? And how would Microsoft go to all Typescript users and re-implement their code in Go? How would that work?
But that doesn't change the simple fact that the Typescript compiler written in Typescript was too slow:
"As your codebase grows, so does the value of TypeScript itself, but in many cases TypeScript has not been able to scale up to the very largest codebases."
And to fix that performance problem, they had to reimplement Typescript (aka "the Typescript compiler") in Go. And that made it 10x faster.
And I am not sure you got "just transpire your code to Go" from, because I sure as hell didn't write it. And if you know it doesn't work like that, and I sure as hell didn't claim it works like that, why did you introduce this straw man?
This is all plain facts.
So yes: please stop the flaming. And please stop using Electron. Dennard scaling hasn't been with us for some time now.
Again, meh. I'm just happy you've admitted that the idea that Go is 10x faster than V8 is bunk. Use what you like. But like I said everyone else is using Electron and winning. I don't make the rules, I'm just trying to explain them.
> you've admitted that the idea that Go is 10x faster than V8 is bunk.
I admitted nothing of the sort. Where did you get that from?
On a comparable project, Microsoft determined that
(a) the performance of the existing JS solution was practically insufficient
(b) doing a rewrite while keeping JS would not be sufficient
(c) rewriting the TS/JS compiler in Go yielded an empirical speed boost of 10x.
And once again, I never claimed that "Go is 10x faster than V8". Please do refrain from these straw-men arguments, it is not conducive to a good discussion.
In general, however, it is true that JIT compilers perform far less well on real-world production code than they do on small synthetic benchmarks, and so the 10x performance penalty that Microsoft found empirically is well within the range of what is observed elsewhere.
If you're on macOS, WebKit is a native OS framework. Using WebKit to render Markdown seems completely appropriate.
Now, if you're rendering everything with WebKit, that's ridiculous, in the same way rendering everything with PDFKit would be ridiculous. But for a Markdown view, WebKit seems like a logical choice. There's no need to subsequently flip the table and replace everything with a Chromium web app.
If HTML engines are better than native UI libraries at rendering rich text, possibly the hardest thing UIs need to render, why would I not also use it to render easier things like buttons or text fields?
Also, OS X rendered its UI with DisplayPDF/Quartz for the longest time.
The native Apple libraries are terrific at rendering rich text, it’s one of their strongest assets.
The poster’s issues seem to be specifically because they want to use markdown as the backing. The native rich text backing for native Apple views is attributed strings. They could translate the markdown to attributed strings, but seems like they don’t want to.
Still seems overkill. It's just headers, lists, block quotes, codeblocks, and a few inner emphasis and link formatters, basically. HTML is the whole jungle for a gorilla with a few bananas here.
“For any markup that is not covered by Markdown’s syntax, you simply use HTML itself. There’s no need to preface it or delimit it to indicate that you’re switching from Markdown to HTML; you just use the tags.
The only restrictions are that block-level HTML elements — e.g. <div>, <table>, <pre>, <p>, etc. — must be separated from surrounding content by blank lines, and the start and end tags of the block should not be indented with tabs or spaces. Markdown is smart enough not to add extra (unwanted) <p> tags around HTML block-level tags.”
Because of that, I think I would use a html renderer to render markdown. Because people allowing arbitrary HTML opens a huge can of worms I might also whitelist a restricted set of HTML.
It's still going to be faster than creating and moving around a bunch of NSViews simply because the WebView (native WebView, not Electron) renders the entire content at once, directly on the graphics pipeline, skipping all layout and compositor steps that get done on normal NS(Text)Views.
The key point here is to use the WebView only for the text view. Where it goes wrong is when people start writing entire interactive UIs in the WebView.
The other option would be PDFKit, but most people aren't nearly as comfortable programming with PDF as they are with HTML.
> The overriding design goal for Markdown’s formatting syntax is to make it as readable as possible. The idea is that a Markdown-formatted document should be publishable as-is, as plain text, without looking like it’s been marked up with tags or formatting instructions.
That doesn't make it not true though. Markdown generally supports HTML (though oftentimes only a subset), and is typically styled using CSS. Using a web view makes complete sense to me.
That doesn’t track at all. Markdown was created as a simpler way to mark up text than HTML. They are both ways of encoding a document that provide ways to convey intention. HTML is no one’s end goal.
So I can build my entire app in a WebKit wrapper and call it native? I think ‘native’ in this context is well-understood to mean eschewing web or cross-platform renderers
But why would you expect to use WebKit to render rich text? If using an HTML/CSS/JS renderer to render text is "completely appropriate", what isn't appropriate for it? Why would you not render everything with it?
I don't understand how you go from "rendering text is completely appropriate" but then "rendering everything is ridiculous".
a bit of a roundabout way to answer this, but i think most native devs are okay with a self contained app element using webkit, that is what it is there for and generally stuff like rendering html, markdown, a one off static page, it is just a view from UI perspective. When things get more interactive, have a navigation hierarchy, animations etc are when things start to diverge from native feel and performance and you have gone too far.
> But why would you expect to use WebKit to render rich text?
Because rendering rich text correctly and consistently is one of the hardest problems in software. Bidirectional text, a million glyph shaping complexities, mixed content such as inline images and different text sizes, reflow that should take milliseconds, natural-feeling selection, etc etc.
No implementation comes even close to browser rendering engines in covering all of these.
I mean, this is why I think PDFKit is a good comparison. Could you render your app's entire UI as a series of PDFs? Absolutely! Should you do that? Uh, probably not. You should use the native controls Apple gives you for buttons and dialogs and input fields and so on.
But WebKit is the native UI for HTML, and Markdown is intended to be transpiled to HTML.
It's irrelevant what Markdown was meant to be transpiled to. When people say "Markdown support" these days, they mean "rich text support", since Markdown is actually just ASCII. It is ridiculous to need a Web renderer to render rich text, on any platform. Even terminal emulators can do it.
That’s how it originally worked, but Apple has moved away from PDF to other native GPU frameworks for drawing. Now window content is stored as bitmaps instead of redrawing vector instructions.
You’re correct. I meant to say textures, but even that isn’t completely accurate.
AppKit used to generate the bitmap directly through CoreGraphics (PDF model). Now it uses Core Animation and Metal and no longer draws the bitmap directly.
If we're all agreeing that all the native elements are useless, I can see how the question is "do I use Metal or do I use WebKit", but my question wasn't about WebKit vs metal, it was WebKit vs native elements.
Yeah, this is actually my current in-progress solution: render the final Markdown & the streaming through WebKit.
And yes, I agree: on macOS, WebKit is a native OS framework. In that sense, it is "native". But I think it also supports the broader point I was making: if you want to work with rich text, Markdown, selection, typography, and long-form formatted content properly, web technologies quickly become the only viable option. I am not saying that using WebKit for a Markdown view is wrong. Quite the opposite, it is probably the most reasonable option available. The problem is that the "native" solution here is still effectively a web-rendering solution. There is a cost. Each `WKWebView` brings a WebKit engine with its own performance & memory overhead. So you cannot just sprinkle `WKWebView` everywhere & pretend it is free native macOS component as any other. My frustration is mostly that this is the answer. For this kind of UI, SwiftUI / AppKit / TextKit still do not give you a clean, modern, composable path that feels better than "just use WebKit".
> But I think it also supports the broader point I was making: if you want to work with rich text, Markdown, selection, typography, and long-form formatted content properly, web technologies quickly become the only viable option.
But, like, of course they are. This is what HTML was built for. The other major standard would probably be RTF, but it's a bit less structured, and so less close to Markdown. HTML is the better pick.
If you subsequently want to style that HTML, so that every second-level heading uses a specific font, and every third-level heading uses some other font, and so on, CSS is the best way to do that.
So, yes, we're saying the same thing, but to me it's a bit like saying "If you want to find the answer to 2 + 2, addition is the only viable option." Well, yes!
I think the reason this feels kind of wrong is because that same HTML and CSS renderer you're using for Markdown also comes with an entire 3D graphics pipeline and audio synthesizer. Obviously, we should be able to answer 2 + 2 without opening Mathematica.
I guess the important technical question is whether simply creating a WKWebView also loads in all that other stuff. I would hope and expect the OS is smarter than that, and you can call WebKit for simple HTML without everything else coming along.
But I think my opinionated point from the article still stands: if you need rich text & good typography without fighting the platform, then web technologies quickly become the pragmatic choice.
For my app, I will probably continue with WebKit. It is the most reasonable middle ground for now. But in this situation, it is tempting to jump to something with a stronger rendering engine, like Chromium instead of WebKit, and start using the huge ecosystem of tools that already work. For example, https://diffs.com is one of the most tempting parts for me. The awkward thing is that embedding WebKit & calling it a day does not feel like a clean native solution either. You lose many of the native things you get when rendering through SwiftUI primitives, but you also do not get the full power & ecosystem of a proper web stack. And that makes it much easier to understand why so many companies (good & bad) choose Electron.
From an engineering perspective, even the fact that you can avoid this controversial middle ground entirely & build the app around web technologies from the start makes sense. It is not just laziness or ignorance of native platforms. Sometimes it is simply the more consistent & logical architecture.
Used these myself and had no issues. And I am a moron who doesn't like Swift or SwiftUI - preferred Objective-C, but still managed to do this, without any LLM help.
I tried Textual earlier today with some not-so-good results:
- Static completed Markdown scrolling fails the new focused probe. Result: p95 18.86 ms vs 16.7 ms budget, max 232.49 ms.
- Long live Markdown/code update path also fails. Result: p95 59.33 ms vs 16.7 ms, max 75.94 ms. This is a separate but related stress case around large rich text surfaces during updates.
- Long-history scaling technically passes, but the numbers are not smooth-frame healthy:
- 120 turns: total p95 21.35 ms
- 500 turns: total p95 23.11 ms
- 1000 turns: total p95 36.77 ms
Technically, it is not bad. However, it is a bit slower than my own solution & has similar performance gaps, mostly related to SwiftUI rather than the Textual implementation.
Either you render the markdown document just once (not streaming) or your document is simple and short.
I used to use swift-markdown-ui for my app but the performance is nowhere near using a wkwebview. When streaming large documents with tricky elements like large tables, code blocks, nested quotes, you may even get beached ball. It never happened when using a wkwebview.
> > But I still cannot make a simple thing work properly: a chat with Markdown & the ability to select a whole message.
> Sorry, sounds like bullsh_t.
No... As a user, one thing I notice is that older, non-HTML-based apps don't seem to follow "the rules." Text that I should be able to select and push "control/option C" just isn't selectable, or copy doesn't work.
Then I realized that browsers (and everything based on them) introduced some new paradigms to UI that native UI frameworks just haven't kept up with.
(And I say this as someone who prefers native apps over web-based apps.)
Show your code, or show you the door. There are so many native Mac and iOS apps out there right now perfectly capable of rendering Markdown and streaming text. You just gotta wonder what is this guy’s excuse.
OP says "you want to select a whole Markdown document built from SwiftUI primitives", but who wants that? what sort of product thinking tells us we want that? that sounds like a document editor, which has been hard to build for decades and sounds out of scope for an llm chat ui. everyone has landed on only supporting selection within each contiguous block, with a copy button for the entire message
LLMs are often used to generate Markdown because they're quite good at it and unlike HTML it's very forgiving.
Rendering text into things like chat bubbles or even just generic output panes as it comes in is a massive pain. Every new word requires redoing layout, detecting LTR versus RTL flows and overrides, figuring out word breaks and line breaks, possibly combined with resizing the containing UI element (which involves measuring the render space, which is often implemented by rendering to a dummy canvas and finding out the limits).
Document editors have it relatively easy because humans type at a relatively low speed and pasting is a single operation (although pasting large amounts of text does hit the render performance of the UI). They're also often provide relatively limited features on phones.
If you want to render something like ChatGPT with similar features in native UI, youre going to need to find a fully-fledged document component or build one yourself. And, as it turns out, we have document components that work quite well: web engines.
If you embed a webview rendering just HTML and CSS, you get better performance, features, and accessibility than any home-grown renderer will provide. And with every major OS coming with a browser built in, it won't even bloat your app.
Old versions of macOS / AppKit used to use WebKit to render rich text inside their native NSTextFields. Turns out text is hard :)
And besides, the native WebView is super fast and lightweight, and its not unreasonable to use it as a text layout engine. You could use separate webviews for every row in a table and you'd still get fantastic performance.
iMessage for mac used to use a webview too. Adium as well. HTML is absolutely the right tool for the job if you're rendering rich/marked-up text.
The Mac never used WebKit for NSTextField rendering. When iOS was first written, WebKit was used as the text renderer everywhere initially, including in UIKit controls (the “sweet solution”). This proved to be too heavyweight / cumbersome and the coretext/appkit text rendering approach was brought over.
...although the logic in the article is slightly odd:
1. Discover complex native text rendering is hard
2. Render text in a low-level way, complain about having to (re)implement native interactions
3. Try WebKit and it works great!
4. Throw WebKit away??
5. Have to re-implement native interactions??
I remember being a junior engineer in 2015, and being asked to render a clickable link within a paragraph in an iOS app. Swift had just been released so we were still entirely on the ObjC/UIKit stack. It was an absolute nightmare. I _barely_ managed to make it work. I haven't really touched iOS since about 2016, so I assumed the new SwiftUI stuff would have this stuff built in. Obviously. Kind of insane that it wasn't.
When I say "this stuff" I'm not talking about a link, I'm talking about the overall markdown/text capabilities that the post is talking about. I meant that I expected more parity with what you'd encounter on the web.
You expected highly capable, generic GUI toolkits to show parity with a development environment that has specifically targetted text above all else (though with lots of other stuff and great depth too) for decades?
Even in an era of PWAs and highly reactive UIs, the web is still fundamentally a document presentation mechanism. No generic GUI toolkit fits that description (even if they can be coerced into being one).
I vaguely remember doing this with attributed text for iOS 4.
That said, I also had quite a lot of success on iOS 4 using HTML as the layout engine for the main screen of the app, though the place ran out of money before that went anywhere.
HTML can be really good, the blockers back then were it not being exactly the same as the Apple UI guidelines unless you put in a huge amount of extra work that nobody wanted to spend. I'm not sure when Apple's own guidelines stopped mattering exactly (iOS 7's invisible buttons necessarily had to be ignored, but there was already a decent level of custom UI before them and it was already essentially irrelevant even before Apple became extra-inconsistent with Liquid Glass), but I think we're now at the point where you only follow those guidelines if you (a) don't have your own UI team, and/or (b) want to try to aim for a shout-out from Apple.
Do not know about "forever", at the moment it works okay, I guess. But for a long long time most of the iOS apps were using this https://github.com/TTTAttributedLabel/TTTAttributedLabel to have proper support for links & other basic attributes.
> how immature all these “native” things still are when you step outside simple screens
Well yeah. If people don't invest sufficient effort in a thing why would there be an expectation for that thing to become mature? People are locked into web tech because that's where the greater majority of the effort has been going. Quite literally people look at native, say it isn't developed enough, and go develop for the web even more. Cycle repeats. Hardly anyone wants to put in the effort to improve native when things already "just work" for the browser.
Sure, but those the native UI dev kits are commercial products, right? Isn’t it their job to sell them to people — not people’s job to sell themselves on it? Part of the reason web stuff is so much more mature is the unwillingness of the big commercial OS manufacturers to keep up with the times. Windows UI kits are a hot fucking mess.
Agreed. He's basically complaining and moaning about Markdown not being fast to work with in Swift, when nobody has really put a lot of effort into that yet. yet despite this, he's not willing to contribute to that himself.
Give them a break. Why doesn't the OP just help develop a highly efficient SwiftUI extension for Markdown if it's that important? Remember that there are many efficient MD libraries for Swift/ObjC which would in any case be the sensible approach here (and it also explains why he can't find a SwiftUI alternative to his liking).
I've had pretty much the same experience with my AI chat app. Nothing works well. Markdown rendering is slow and laggy, streaming is slow and laggy, everything locks up the UI. I've tried at least 5 of the most popular text editor components for UIKit and SwiftUI on GitHub, and all were broken in one way or another, buggy, and slow as well. It's ridiculous.
Yep, this is a difficult problem. I wrote extensively how I managed to solve this by creating my block editor from scratch using Qt C++ and QML[1]. I faced similar issues - selection between discrete blocks, showing the underlying Markdown under the cursor, varying delegate sizes, etc.
I'm using what I learned to create a native LLM client with a streaming Markdown parser[2].
If you need to display HTML content (what Markdown usually translates to) then WKWebView is the control to use! Or use something like litehtml which should be more than enough for Markdown unless you want to support "Animated Gifs" (that are actually H.264 movies these days) or whatever else.
You can still use native controls for the rest of the UI and have 0 Javascript running. I'm not sure I understand what the problem with NSTextView was though. It's pretty performant as far as I can tell?
I don’t recall ever struggling with NSTextView. I never really got into Swift, but I’ve never found Cocoa / Objective C to have any of the problems the author mentioned.
Not exactly sure what “streaming” text is, but serial terminal software has been handling incremental text rendering and updating for decades, without performance struggles.
`NSTextView` is good. My point is not that `NSTextView` itself is bad. The problem is that once you are working with all the "modern" Apple stack (Swift, SwiftUI, and the direction Apple is clearly pushing developers towards) `NSTextView` does not fit as naturally anymore. Some newer APIs are not even available for AppKit now, so you quickly end up in an awkward middle ground.
By "streaming" text, I mean a formatted text stream that has to be parsed, formatted, and appended on the fly - basically how every model/AI chat works now. And this is where `NSTextView` becomes tricky. It forces an interesting architectural choice: either go deeper into AppKit with `NSCollectionView`, custom cells, manual layout, etc., or fight the whole SwiftUI model by embedding something like `NSTextView` inside `LazyVStack` / SwiftUI views & then dealing with all the integration problems.
So I am not saying Cocoa / AppKit was always bad, or that `NSTextView` is useless. I am saying that for modern chat-style UI with incrementally rendered formatted text, it does not compose well with the rest of the modern Apple stack.
Yes, MD gets translated to DOM tree. But virtual list implementation in Sciter is a native thing. Load whole chat is not an option usually. Yes, JS is used in process but mostly as a configuration option: take output of one native function -> transform it -> pass as an input to other native function.
Essentially there is no so significant difference with any other SwiftUI/TextKit solution. It is just a difference in terms - SwiftUI uses tree of Views that is conceptually the same as DOM tree in terms of Sciter.
I’ve been there too. Finally opted for Flutter (need cross-platform mobile and desktop). Apple needs to put a big effort into the development tools, or they will face the actual Windows situation: several GUI toolkits, none of them as mature as the ones they are designed to replace. (Try to beat Windows Forms Professional Control Libraries and the boost in development performance you get. You can’t because the tool makers need a clear path and commitment in order to justify developing a new version of the full control libraries. You need cross-platform; forget the native tools. That’s the same scenario Apple is facing right now.)
A thing I’ve always wanted was a visual JSON viewer that instantly opened on multi-hundred-meg files. So I used Claude Code to build one with native text views and it’s true it’s pretty raw. But for a thing that doesn’t need formatting, dictionary, and all that it’s great. The viewer opens fast enough that it’s dominated by the window rendering animation which is about what I wanted here.
So I think the text view is pretty low level so that it can support this.
I just wish there was a native Markdown renderer / editor library in C that I can use cross-platform - in the style of something like IMGUI (where the library outputs a list of primitives for you to render yourself in any graphics API).
Or well... since we now have Claude I might have a jab at this someday in my free time.
The link says litehtml is C++. I can't tell if it exposes an FFI (I bet not)
Of course blitz doesn't expose a FFI either and also if you need anything interactive you have to use the dioxius framework or implement you own APIs for that as well as take care of animation yourself.
Does it mix well with text input? What I really want is a native WYSIWYG Markdown editor - in a similar fashion to Typora (Electron) or Milkdown (a JS library).
We did https://markant.md with TextKit 1, flies through multi-megabytes markdown files with latex rendering etc. took some scaffolding (like only rendering attachments when they are close to the viewport) to make it smooth, but it wasn’t really a big problem.
In a way it really is, in part because HTML + CSS is the back door "universal native GUI framework" that various projects have been chasing for years. Because browsers and GUI elements in them are so important to modern computer use, every OS vendor has a vested interest in ensuring the default look and behavior of elements in their browsers are native (or as native as possible). As a result, if you can render your UI in a browser (or in a browser frame/view) chances are you can get a native looking and native feeling UI for your application with a well understood and robust piece of technology that will retain that look and feel even as the underlying OS changes around it.
One of the reasons I decided to stay in the shadows as an unglamorous, boring backend developer. Every single time I tried any frontend development, either web or mobile, the moment I started running into issues like this requiring witchcraft to accomplish an objectively trivial task, a bit of my soul left my body.
I know nothing about any of these APIs, but the claim of the article seems weird. If naitive APIs are insufficient, or slow, or unsuitable, and implementing your own is too hard, then how does Electron even do what it does? One would assume that Electron has its own library to accomplish the task, in which case this code could either be separated, or re-created once and for all, into its own re-usable library.
You can just implement Prosemirror from one of the greatest web teams on the planet and get pretty much every text editing nicety for free - markdown rendering, document version history, blocks, tables. If you choose to deal with prosemirror-collab-cmmit, yjs, or automerge you also get eventually consistent multiplayer. All the "I wish this was a native app" people don't understand the cathedrals that have been built on the web.
Agreed. In Chromium all the content from HTML is rendered inside a single object from the point of view of the host UI; much like a game engine’s UI rendering. Chromium draws everything itself. Host events like mouse and keyboard events are sent to that top level object (although there are some shenanigans involved to make it look more native to accessibility tools).
Skill issue, I guess. I even tried your SSTextView (which is a very nice piece of software, by the way), though it does fit here, but I tried to understand how wrong my TextKit2 implementation is. In my tests, the SSTextView performed a bit worse with p95 on the static markdown scroll test (70.20 ms vs 16.7 ms for per frame rendering). But it is clear from the traces that SSTextView just does too many things I do not need. At least, I had my confirmation that I am not completely wrong about TextKit.
totally. the there's a lot complexity that adds up to the overall performance issues. and TextKit 2 IS pretty bad at things. especially public API is pretty bad - that result in my case need to workaround things that I should've not. I agree with the general sentiment et all. I also still believe there is a place without bringing the whole browser machine to render text, and have text under control - but without relying on the "TextKit" level. That's the next thing I'm researching right now.
I am currently experimenting with linux based GUIs. It was always something that felt clunky to me, but now with more insights, it's clunky for a reason. If you need more then a framebuffer, then rendering something sophisticated to the screen is insanely complex. Somehow it's easy to expect that rendering text on a screen should be easy, but when you go down the layers you find yourself with a club and a flint stone trying to build a castle with it.
Wayland is another product of this hardships, going wayland native seems only feasible when all stars align around it. But then you are stuck in that place.
That being said, without deeper knowledge about SwiftUI, I find it a bit odd to expect so much from a novel concept. Native desktop dev is already kind of niche, considering the dominance of web dev. Chrome (and it's artifacts) is probably the best funded software in the world and google's incentive to improve it is above all. It's not a miracle that it just works. It's effort and tons of cash.
> Somehow it's easy to expect that rendering text on a screen should be easy
This is a common misconception among programmers, and is actually the opposite of the truth. Drawing arbitrary geometric shapes is easy, rendering text correctly is insanely difficult because ... humans.
Why not use native for UI frame (menu, toolbar, conversation list etc) and WebKit for the actual chat? I think that would combine the best of both worlds.
I understand your pain. That’s why I’ve ported my VMPrint layout engine to Rust. It’s early, but it already shows promising performance improvements over the original TypeScript-based engine, which is already very fast. The Rust version can create fully paginated, publishing-grade layout at around 8,500 pages (or 2,000,000 words) per second on a M4 MacBook. It’s even faster at advanced tasks like mixing texts with irregular exclusion fields. The TS version can do it under 1ms, but I don’t have a measure yet for the Rust version. Unfortunately, people have shown little interest in this kind of components, so I’m no longer inspired to release it in its raw form like I did with VMPrint. My plan is to use it to build a native markdown editor first to test it more fully and just to have fun with it, LOL.
Why not Typst?
Typst is excellent for authored documents and produces beautiful output. It is also a Rust binary. You cannot import it into a Node process, run it in a browser, deploy it to a Cloudflare Worker, or call it as a library from a TypeScript application. If you need a layout engine embedded in a JS or TS runtime, Typst is not available to you.
So, wouldn't porting VMPrint to Rust make it such that Typst is the clear winner? Or is there something else missing?
Just checked out VMPrint and it's crazy! Keep up the efforts. If you/someone could get a HTML/CSS input layer in front of VMPrint that would be a killer feature? Or is it possible already?
Basic text styles are ok, but things like authored pagination, page header/footer, mirrored margins, margin notes, footnotes and references are basically unsupported or need to be hacked together.
Everyone loves to complain about the "bloat" in Chrome, but how many have actually taken the time to measure it against a native rewrite? Love this article. We take so much for granted in the modern WebKit/Blink stack. Modern multilingual text processing is a genuinely hard problem.
This is my pet project, a desktop app for working with xAI models & capabilities, so by "performance" I mostly mean "pleasant to use" (as it goes, simple & opinionated). Technically speaking, something like: stable FPS, no visible lags, and the ability to scroll smoothly while the model is streaming.
Regarding the parent comment: yes, memory is important, and I absolutely get the point. There should be a red line, for sure. But I will not sacrifice UX, productivity, and simple pleasure from using software just to save a few hundred megabytes of RAM (or even a few gigabytes) especially for an app I spend hours with behind the screen.
Memory consumption can & should be optimised with proper engineering for sure. As lags & inadequate performance in basic SDK-level primitives are much harder (impossible?) to fix from the outside.
This is not what the people want. Understand that. Give us Rust and Qt. Why be so focused on trying to sell something that doesn't measure up? Even beta Bridges is better than Slint. Take the advice and put the energy to better use for the good of Rust.
The problem here is that you are not choosing based on knowing how the render pipeline is implemented in these tools and how it would work with your usage of it.
You can do a couple days to a week of reading to understand the fundamentals once and then you will actually know what you are doing.
It is not proper to choose things on “battle tested” or other meaningless words
I think in light of the fact that OP included exactly to what they are trying to do, this comment would be helpful to include a more concrete recommendation.
It’s easy to hand wave and say “this wouldn’t be an issue if you knew what you were doing”, but that indeed is the problem.
This is mainly from my experience developing storage engines using datafusion/polars or arrow2/arrow-rs or rocksdb-msbx.
I was changing between them and searching for comparisons online. This ended up being a massive amount of lost time because all of those choices became crystal clear when I actually roughly understood what these libraries were doing.
And actually learning the thing didn’t take as much time as writing code for comparison and discarding it or doing dead-end web searches.
Recently had a similar experience trying to learn dwarf parsing from LLMs or searching for existing code. Then I just realised that reading the spec is by far the most efficient way to understand it.
I am guessing same principle applies to text rendering because I got the same vibe when watching Raph Levien talk about it on some video.
Searching online to read some “industry-standard” “tried and true” etc. Comments is a big sign that it might be better read some actual source about the topic imo. It doesn’t even take that much time to read a textbook even.
This explains why so many AI chat tools suck at text selection on MacOS / iOS. They got the streaming and markdown part right … flicker free, but at the cost of text selection.
> Try to build a ui where you need non-trivial and non-standard behavior and SwiftUI will fail.
I think this may be a misundertstanding of what SwiftUI is. SwiftUI makes it convenient to create apps that look and behave in a way that align with Apple's HIG using controls like `List`, `Form`, etc., but nothing makes you use any of those. For example, it's straightfoward to build a game engine on SwiftUI. https://blog.jacobstechtavern.com/p/swiftui-game-engine
Electron runs like crap on older hardware. It's sad that native UI frameworks never got their shit together, but I think if you want performant text rendering you just gotta reduce your expectations. If you're fine with less fancy fonts and "scripting" your UI in something else than JS, then native can work. But it very quickly reveal that you should avoid everything that uses JSON. JSON is just a disease vector for the JavaScript world to infect everything else.
Hu? We just switched from textual to native because native markdown rendering is finally good. If it was written a year or two ago ok... but now is odd.
the only place where native UI is still better is for ultra-complex UIs - image/video/3d/audio editors. and only because it's easier to create custom UI widgets/renderers than on web stack.
that's it, for everything else native UIs are complete garbage compared to HTML/CSS/reactive frameworks.
I once tried mobile development in semi early days android. At the time I made a free Hackaday reader app because I was a daily reader and loved it.
I remember spending 4 hours to make a scrollable element that wasn't jumpy or buggy. There were several stackoverflow answers full of gotchas explaining all you had to do. I finished and published the app but never again. Native stuff has terrible developer experience.
Outside of niche applications (e.g. virtual desktops, gamming, embedded systems) native UIs are dead.
There are even parts of both Windows and MacOS rendered through HTML. If I remember correctly, at least in Windows 10, File Explorer was rendered through Internet Explorer.
Web rendering doesn't need to be only through Electron/Node. There are other libraries much more performant and lean (Dioxus, etc).
Not in the world of macOS and iOS at least. Here native apps still rule, as there's literally no performant alternative (the OP's complaints about Markdown are misplaced - there's been no interest in MD and SwiftUI and that's why there's no good option. But in ObjC/Swift there is).
In fact, most of the apps I am using on a day to day is native. The Electron apps I use are okay (e.g. Slack) but they absolutely fail the native Turing test.
The browser is faster because they went native, in particular, GPU.
Every issue described is text rendering related. Everyone.
And I would bet most of the SwiftUI issues could be solved with a text render cache.
Something like Casey Murati's refterm toy that showed what that can do with no other optimizations, or the work for GPU accelerated terminal emulators like alacritty or ghostty.
I recently launched a text editor for iOS that uses TextKit 2 and is highly performant with files of 5,000 lines (I tested with Moby Dick from Project Gutenberg). I made it between Aug 2025 and Apr 2026, development is ongoing.
Every keystroke is restyled in under 8ms: no debouncing, no delayed rendering. 20 rapid keystrokes are processed in 150ms with full restyling after each one.
Tag and boolean searches complete in under 20ms. Visible-range rendering is 25x faster than full-document styling. 120Hz screen refresh supported.
App file size was 722 KB for 1.0, and 1.1 with more features is looking like ~950 KB.
If I can do it on iOS then it's must be 10x easier on macOS.
https://www.gingerbeardman.com/apps/papertrail/
It makes sense when the editor is a core feature of your paid product. I understand the sentiment.
But is not it strange that I would need 8 months & a "development is ongoing" mindset just to render Markdown (which is very secondary to the main app features, and mostly just a user convenience people expect in 2026) with a custom low-level solution, effectively playing hardcore engineer instead of building what I actually want to build?
Anyhow, my point is not that "it is impossible". My point in the article is that I understand why people choose web technologies over native for such things. They want to build products, not fight the system’s limitations.
> just to render Markdown
Rendering text beyond ASCII is famously difficult to do; rendering formatted text is sometimes difficult to even make sense of (e.g. what should a style change in the middle of an Arabic word do? how about a selection boundary being moved with arrow keys?); rendering honest-to-goodness Markdown, which can technically include arbitrary HTML tags, is nowhere in the vicinity of a small project.
None of which is to say that you shouldn’t demand that a toolkit solve it for you, only that I understand why the RichEdit control reportedly had a separate team allocated to it in turn-of-the-millenium Microsoft. Working with a large amount of formatted text feels like it should be the most complicated feature of any UI toolkit and I shudder at the thought of even designing the API for it.
(A web browser is good at all this. It also has the API surface of a web browser.)
And some things will still be on you regardless. Did you know Android has two modes for text wrapping, one that won’t reflow the entire paragraph after a single-word change at the end and a different one whose results embarrass a typographer from half a millenium ago? That’s very much the correct way to do things, but if you’re streaming text in, it’s on you to decide whether you want subpar wrapping throughout or a layout jump whenever a paragraph break arrives. Most importantly, it’s on you to know the question exists; there are more, some more important than this one.
Having worked on an interactive novel in 2012 (NSString and attributes), low level glyphs (API deprecated) on a rogue-like, two chat apps (with markdown support for formatting) in SwiftUI, and an idle game using a mix of iOS tricks but all wrapped in SwiftUI.. I’m going to agree with how I summarized this response: skill issue.
It's likely the SwiftUI Mac implementation is subpar. SwiftUI-on-Catalyst might be a better choice for these applications, but it probably has other problems.
> If I can do it on iOS then it's must be 10x easier on macOS.
I strongly doubt this. I suspect it's the exact opposite situation. But I'd like to hear from someone who knows.
Usually performance was the reason for using native APIs rather than web views, but this doesn't seem to be true any more.
Browser rendering engines are pretty mature at this point, with significant GPU acceleration, and over a decade stress-testing by bloated web apps.
Meanwhile SwiftUI doesn't feel particularly fast. Apple's latest and greatest rewrite of System Preferences has dumbed down the UI to mostly rows of checkboxes, and yet switching between sections can lag worse than loading web pages from us-east-1.
It's SwiftUI that is at fault here[1][2], not native apps in general. I wrote my native app in Qt C++ and QML and showed that it is *significantly* faster and uses significantly less RAM than similar web apps[3]. So, no, web apps, in general, are slower and uses more resources than well-engineered native apps.
[1] https://notes.alinpanaitiu.com/SwiftUI%20is%20convenient,%20...
[2] https://x.com/daniel_nguyenx/status/1734495508746702936
[3] https://rubymamistvalove.com/block-editor#8-performance
> It's SwiftUI that is at fault here, not native apps in general.
The article you cited is from 2022 and so is irrelevant, since SwiftUI's performance profile completely changed as of xOS 26.
Claims like "It's hard to build a performant SwiftUI app" get into skill-issue territory, but more importantly, the reality is there are only "SwiftUI-first apps". All non-trivial SwiftUI-first apps will also use UIKit/AppKit as needed, typically for capabilties that aren't yet available via SwiftUI.
iOS 26 is very late to have acceptable performance in the framework that Apple promotes as what you should use. It should have had good performance from the day it was introduced.
WebKit have had great performance for a very long time now.
Why would any startup dare to use tech that only now got fast? Why not go with the battle tested WebKit?
It is also much easier to develop and test html pages than Apple specific tech.
Can you point to a single performant, high-quality SwiftUI-first app with a messages-like chronological transcript and correct scrolling behavior on macOS? The problems with AppKit integration are real and should not be dismissed out of hand.
Qt is the opposite of native. It's just reimplementing the look and feel of a native app, but the seams are extremely visible.
They even used the distinction “native-like” in the block editor article - which is really good, by the way and explains this distinction in more depth - but edited their comment now and that article is the third link and its anchored to the performance section so you won’t see that unless you scroll to the top.
Their point is more that SwiftUI has generally poor performance. Lots of native Windows frameworks have poor performance as well.
Native UI development is a minefield. If you want to build an app today that will still run in 20 years without a complete rewrite in the UI layer you should probably use wxWidgets if you are committed to native - even if only targeting one OS. But that model is really only appropriate for building traditional desktop apps. I don’t think the market would accept a Slack or Notion built that way today.
At this point on Win32 Qt might as well be the native UI. They did a better job of maintaining a coherent visual theme that says "Windows" and fits the design patterns than the actual owners of the platform.
I disagree. I use P4V (Qt-based) regularly. Its lists are quirky and sometimes don't scale property with the OS DPI settings.
Unless you're on KDE where it's literally native?
> Browser rendering engines are pretty mature at this point, with significant GPU acceleration, and over a decade stress-testing by bloated web apps.
Even so, there is a stark difference, even more so on low-powered devices, between native apps and even the lightest of browser apps. I'm traditionally a web developer, but started developing native cross-platform applications the last 6-12 months, and the performance gap is pretty big even for simple stuff, strangely enough.
My experience too, and that's not even touching the disproportionately high RAM usage of frameworks like Electron. Sure, "unused RAM is wasted RAM", until the system starts swapping heavily because of the high RAM usage.
It doesn't even have to be old devices, there are still laptops being sold with 8GB of RAM in 2026.
Electron would be fine… if most of us were running just one electron app.
In reality we’re stuck with 2-5 electron/CEF apps plus whatever is running in browser tabs as well as whatever webviews system stuff are running, which all quickly pushes into VM paging territory on low memory devices.
This is so true. It even happens to me in my 16GB work laptop when I have to use multiple Electron apps at work. My personal 8GB Air is way faster.
On the other hand, WebViews can be really fast without the baggage of Electron and Javascript frameworks/libraries.
> Browser rendering engines are pretty mature at this point, with significant GPU acceleration, and over a decade stress-testing by bloated web apps.
They suck on older hardware. Old Chromebooks are a dime a dozen and are decently spec'd light use or purpose-use machines. Browsers run like crap on them.
Web tech in general is responsible for a lot of unnecessary hardware turnover.
If you dig up an 18 year old Core 2 Duo box, upgrade its storage to a cheap SSD, and install Linux on it, it’s shocking how snappy and usable it is for most tasks… up until you open a web browser or Electron app. Then it all falls apart.
Had it not been for resource creep driven overwhelmingly by heavy web apps and Electron/CEF, there’d be little reason for most people to use anything more powerful than a Sandy Bridge machine and we could have laptops and smartphones with week-long battery life thanks to efficiency gains not needing to be consumed by performance increases.
> If you dig up an 18 year old Core 2 Duo box, upgrade its storage to a cheap SSD, and install Linux on it, it’s shocking how snappy and usable it is for most tasks… up until you open a web browser or Electron app. Then it all falls apart.
Can confirm. I'm typing this on such an old Core 2 Duo laptop running Debian and even with only 4GB of RAM and a mechanical HDD, it's still very fast for everything I do on it that doesn't involve Web browsers. Windows 10 is practically unusable on it however. The gazillion background things it insists on running bring it to a crawl, mostly stuck on disk I/O.
Agreed. I have a shitty laptop I bought at Micro Center a couple years ago for like $80. I bought it mostly out of curiosity since I wanted to see what an $80 laptop would be capable of.
I installed NixOS minimal on there and a few apps to develop, and it was actually quite usable. Then I installed Skype on there and it was horrible and laggy.
> Chromebooks
Have you tried with stock ChromeOS?
Well, maybe for simple web apps, but for complex applications there is a noticeable slowdown, I am not even talking about monsters such as jira, but well optimized apps such as vs code, there is a performance ceiling which is lower than for native apps.
For Jira I think the limitation is buried all the backend execution and rendering. It's still fast if you go only by the frontend user-executed part.
According to the article, native is slower though.
TFA actually says the developer couldn't figure out how to do this with native APIs, not that they're slower: "But I still cannot make a simple thing work properly: a chat with Markdown & the ability to select a whole message."
Electron ultimiately sits on native APIs, and has its own performance costs on top of them.
Electron sits on native rendering primitives. Do you suggest that every developer who wants rich interactive text in their app should write a text rendering engine from the ground up?
I'm suggesting that instead of going with the heaviest-possible option, they first explore built-in capabilities like TextEditor (https://developer.apple.com/documentation/swiftui/texteditor) and/or the many open source projects which offer Markdown support (https://github.com/gonzalezreal/textual).
If you account for dev time, ‘giving up’ and just doing it in a web renderer is totally valid. Choppier, more memory, bad if the device is failing, but 98% of the time that can be acceptable.
Native GUI dev, tho, can enable low-resource performant apps just by sticking the rudimentary OS is a way no higher app really can, and with capabilities that’d choke a web app. Load up a listbox with a few tens of thousands of items with custom rendering… as a fat client dev you’re only in a little trouble, on the web googlers making google apps force pagination a whole lot.
As a point of comparison: WPF was Microsoft’s attempt to nail the ‘best of the desktop & best of the web’ [And I would argue they effectively nailed it, as a specification.]. But, as a brown belt WPF dev and a blue/orange belt with Win32/MFC, the extra overhead related to WPF broke common scenarios we’d never think twice about on the true native side. The web was made for sharing Robotech technical manuals, OS GUI’s to pump all the rectangles as fast as hardware allows. Apples and oranges.
> If you account for dev time, ‘giving up’ and just doing it in a web renderer is totally valid.
For sure, all software is chock full of "best? no/works? yes" compromises. I object to the article framing of "I couldn't figure it out, therefore TextKit 2 does not play well with anything modern", which is a very silly conclusion.
Agreed, but no such conclusion or framing was stated, supported, or suggested in my comment.
I was agreeing and providing more context to the costs of building at a higher level, like Electron, and the limits even when applied by a unified vendor with incentives for high performance.
I added the word "article" before the word "framing" to make it clearer in my comment supporting your comment that the last half of my comment wasn't commenting on your comment.
Electron sits on top of Skia, which renders the text itself. It's designed to look like the host OS but it's not just asking the OS to draw text, because that's actually a lot slower than Skia.
SwiftUI, (but not specifically "SwiftUI", more of paradigm) is not the right tool to incremental changes of large portion of data, and SwiftUI specifically is very bad at it and offer no good API to make incremental changes more optimal. That's one of the reason behind why Apple to this day did not ship usable SwiftUI text view component.
They did last year. I can understand why you're confused, since it came in the form of the radically-improved `TextEditor`. https://wwdcnotes.com/documentation/wwdcnotes/wwdc25-280-cod...
I don't know where's the improvements, give it has all the limitations, now scaled to rich text. That's not the direction that this component should get. It need radical new API that is not much compatible with SwiftUI API
You must know far more about this than me, but as of iOS 26, `TextEditor` evolved into a true rich text editor (`AttributedString` binding, `AttributedTextSelection` rich text selection, custom/app-specific text attributes, etc). https://developer.apple.com/videos/play/wwdc2025/280/ Do you really think people should use web views instead of, say, STTextView?
I think people should use anything but SwiftUI.TextEditor is my current stance
System Preferences also sometimes just render a WebView - most notably in the Apple Account settings
Oh that hackjob explains some of the inconsistent, frustrating performance
(I do give them credit for some terrible usability elements that would delay a scammer if they had our elderly relative on the phone.)
The AppleScript that has to be written and rewritten to flip a simple switch in settings… (it’s telling the system to move around and click in the UI by count, and the count gets thrown off by what I now suspect to be unpredictable web view UI loading)
I assume because there’s no good “render a view hierarchy based on layout delivered from a server” option. Whereas this is what HTML is with caveats.
WebView is "native" too in the end.
It's crazy that people think it's a good idea to throw away thousands of manyears of optimization (and millions of manyears of field testing in real world) just to... Idk, write a lesser text render engine?
Now RAM use is the main reason to prefer native APIs over web views.
I am not sure WebViews are the actual problem, and fairly confident it is running the entire application + gargantuan frameworks as JavaScript is.
I am currently working on something I call HTMXNative, which is what it sounds like: using HTMX in WebViews for hybrid apps.
I haven't really looked much at memory consumption, but when I've looked so far it's been very comparable to equivalent apps using native UI.
A decision to move native because of the crisis seems like an expensive populist move to please not very solvent users. Why bother with that if many predict the RAM crisis will last merely until 2027?
If you can predict the future so accurately, why aren't you the richest man on the planet?
Because predicting the future roughly as well as everyone else doesn't make you rich.
Predicting a surge in RAM supply after a surge in RAM demand and a huge increase in RAM margins is economics 101.
We are still bound by natural resources, as much as economics 101 loves to ignore this simple fact.
Scarcity of natural resources is squarely within the realm of economics 101.
The open question in my mind is for how long semiconductor demand will continue to grow faster than we can increase production capacity.
I can't. But I'm sure I will be happier and richer person if I don't opt out self from a working stack in favor of saving 100-200mb RAM
You're off by an order of magnitude there.
super happier and super richer?
> Usually performance was the reason for using native APIs rather than web views, but this doesn't seem to be true any more.
It's still true. There's no way around it, web views will always be slower.
> Meanwhile SwiftUI doesn't feel particularly fast.
That’s because SwiftUI isn’t particularly good, not because web rendering is as good as native. AppKit still runs circles around both, in performance and resource consumption.
This is the ActiveX/nacl/wasm/etc... argument recapitulated. For decades, people dithered about how to get fast code into browser environments such that it could be deployed safely.
Then the V8 team at Google just asked "well, what if we just made Javascript crazy fast instead?", and here we are. There's still room for native code in environments that don't map nicely to scalar scripting languages, but not a lot of room. Basically everyone is best served by ignoring that the problem ever existed.
It took the rendering side a little longer, but we're here nonetheless. There's still room for specialty apps with real need to exploit the hardware in ways not abstracted by the DOM (not 100% of it is games, but it's close to that). But for general "I need a GUI" problems? Yeah, just use Electron.
Except JavaScript isn't "crazy fast".
Not by a long shot.
How did Microsoft just make Typescript 10x faster? Oh right, by reimplementing it in Go.
https://devblogs.microsoft.com/typescript/typescript-native-...
See also:
https://blog.metaobject.com/2015/10/jitterdammerung.html
Please don't use Electron.
I am going to miss being able to host TypeScript in a web view however.
Meh. Flaming about this is so exhausting given that the war was already fought and we know who the winner is.
First, that's the typescript compiler, not typescript apps. And it was a ground-up rewrite effort (a very large one) with a specific eye toward improving the performance of the original, which was widely held to be sub-optimal for reasons entirely unrelated to implementation language. Suffice it to say that, hell no, you can't just transpile your code to Go and expect it to run faster. We all know it doesn't work like that.
But more broadly, landing with "Please don't use Electron" in the context of a comment about a MS product seems weird given the implementation framework of Microsoft's single most impactful new UI project of the last decade...
Just stop, basically. You lost. Use Electron. It works great and everyone else already does and proved you wrong.
> that's the typescript compiler, not typescript apps.
Of course it's the typescript compiler. What else is an implementation of "Typescript" that you could actually make faster? And how would Microsoft go to all Typescript users and re-implement their code in Go? How would that work?
But that doesn't change the simple fact that the Typescript compiler written in Typescript was too slow:
"As your codebase grows, so does the value of TypeScript itself, but in many cases TypeScript has not been able to scale up to the very largest codebases."
And to fix that performance problem, they had to reimplement Typescript (aka "the Typescript compiler") in Go. And that made it 10x faster.
And I am not sure you got "just transpire your code to Go" from, because I sure as hell didn't write it. And if you know it doesn't work like that, and I sure as hell didn't claim it works like that, why did you introduce this straw man?
This is all plain facts.
So yes: please stop the flaming. And please stop using Electron. Dennard scaling hasn't been with us for some time now.
Again, meh. I'm just happy you've admitted that the idea that Go is 10x faster than V8 is bunk. Use what you like. But like I said everyone else is using Electron and winning. I don't make the rules, I'm just trying to explain them.
> you've admitted that the idea that Go is 10x faster than V8 is bunk.
I admitted nothing of the sort. Where did you get that from?
On a comparable project, Microsoft determined that
(a) the performance of the existing JS solution was practically insufficient
(b) doing a rewrite while keeping JS would not be sufficient
(c) rewriting the TS/JS compiler in Go yielded an empirical speed boost of 10x.
And once again, I never claimed that "Go is 10x faster than V8". Please do refrain from these straw-men arguments, it is not conducive to a good discussion.
In general, however, it is true that JIT compilers perform far less well on real-world production code than they do on small synthetic benchmarks, and so the 10x performance penalty that Microsoft found empirically is well within the range of what is observed elsewhere.
Overview and links to research here:
https://blog.metaobject.com/2015/10/jitterdammerung.html
And remember: this was a response to this comment:
"well, what if we just made Javascript crazy fast instead?", and here we are."
JavaScript is not "crazy fast".
Whether it won or is otherwise just the bees knees is irrelevant. It ain't "crazy fast".
The typescript compiler is a typescript app. Or was, I guess
If you're on macOS, WebKit is a native OS framework. Using WebKit to render Markdown seems completely appropriate.
Now, if you're rendering everything with WebKit, that's ridiculous, in the same way rendering everything with PDFKit would be ridiculous. But for a Markdown view, WebKit seems like a logical choice. There's no need to subsequently flip the table and replace everything with a Chromium web app.
If HTML engines are better than native UI libraries at rendering rich text, possibly the hardest thing UIs need to render, why would I not also use it to render easier things like buttons or text fields?
Also, OS X rendered its UI with DisplayPDF/Quartz for the longest time.
The native Apple libraries are terrific at rendering rich text, it’s one of their strongest assets.
The poster’s issues seem to be specifically because they want to use markdown as the backing. The native rich text backing for native Apple views is attributed strings. They could translate the markdown to attributed strings, but seems like they don’t want to.
> Using WebKit to render Markdown seems completely appropriate.
It doesn't? Needs an explanation.
markdown is a markup language 'intended' to be rendered as HTML, WebKit seems appropriate to render HTML
Still seems overkill. It's just headers, lists, block quotes, codeblocks, and a few inner emphasis and link formatters, basically. HTML is the whole jungle for a gorilla with a few bananas here.
> It's just headers, lists, block quotes, codeblocks, and a few inner emphasis and link formatters, basically
That, and in-line HTML. https://daringfireball.net/projects/markdown/syntax#html:
“For any markup that is not covered by Markdown’s syntax, you simply use HTML itself. There’s no need to preface it or delimit it to indicate that you’re switching from Markdown to HTML; you just use the tags.
The only restrictions are that block-level HTML elements — e.g. <div>, <table>, <pre>, <p>, etc. — must be separated from surrounding content by blank lines, and the start and end tags of the block should not be indented with tabs or spaces. Markdown is smart enough not to add extra (unwanted) <p> tags around HTML block-level tags.”
Because of that, I think I would use a html renderer to render markdown. Because people allowing arbitrary HTML opens a huge can of worms I might also whitelist a restricted set of HTML.
It's still going to be faster than creating and moving around a bunch of NSViews simply because the WebView (native WebView, not Electron) renders the entire content at once, directly on the graphics pipeline, skipping all layout and compositor steps that get done on normal NS(Text)Views.
The key point here is to use the WebView only for the text view. Where it goes wrong is when people start writing entire interactive UIs in the WebView.
The other option would be PDFKit, but most people aren't nearly as comfortable programming with PDF as they are with HTML.
More importantly all HTML is valid markdown.
Putting HTML in markdown defeats the purpose IMHO.
I don't think it does at all!
> The overriding design goal for Markdown’s formatting syntax is to make it as readable as possible. The idea is that a Markdown-formatted document should be publishable as-is, as plain text, without looking like it’s been marked up with tags or formatting instructions.
https://daringfireball.net/projects/markdown/
Using some semantic HTML as an occasional escape hatch is perfectly in line with this overall goal.
That doesn't make it not true though. Markdown generally supports HTML (though oftentimes only a subset), and is typically styled using CSS. Using a web view makes complete sense to me.
That doesn’t track at all. Markdown was created as a simpler way to mark up text than HTML. They are both ways of encoding a document that provide ways to convey intention. HTML is no one’s end goal.
OP thinks "native" = only using Swift/ObjC primitives
WebKit is cheating I guess? Because it exists on other platforms?
Might as well use Java
So I can build my entire app in a WebKit wrapper and call it native? I think ‘native’ in this context is well-understood to mean eschewing web or cross-platform renderers
But why would you expect to use WebKit to render rich text? If using an HTML/CSS/JS renderer to render text is "completely appropriate", what isn't appropriate for it? Why would you not render everything with it?
I don't understand how you go from "rendering text is completely appropriate" but then "rendering everything is ridiculous".
a bit of a roundabout way to answer this, but i think most native devs are okay with a self contained app element using webkit, that is what it is there for and generally stuff like rendering html, markdown, a one off static page, it is just a view from UI perspective. When things get more interactive, have a navigation hierarchy, animations etc are when things start to diverge from native feel and performance and you have gone too far.
> But why would you expect to use WebKit to render rich text?
Because rendering rich text correctly and consistently is one of the hardest problems in software. Bidirectional text, a million glyph shaping complexities, mixed content such as inline images and different text sizes, reflow that should take milliseconds, natural-feeling selection, etc etc.
No implementation comes even close to browser rendering engines in covering all of these.
I mean, this is why I think PDFKit is a good comparison. Could you render your app's entire UI as a series of PDFs? Absolutely! Should you do that? Uh, probably not. You should use the native controls Apple gives you for buttons and dialogs and input fields and so on.
But WebKit is the native UI for HTML, and Markdown is intended to be transpiled to HTML.
It's irrelevant what Markdown was meant to be transpiled to. When people say "Markdown support" these days, they mean "rich text support", since Markdown is actually just ASCII. It is ridiculous to need a Web renderer to render rich text, on any platform. Even terminal emulators can do it.
HTML is basically analogous to RTF, no?
maybe html 0.9 :)
If think that the MacOS GUI is actually entirely rendered as a series of PDFs, if I have understood things correctly.
That’s how it originally worked, but Apple has moved away from PDF to other native GPU frameworks for drawing. Now window content is stored as bitmaps instead of redrawing vector instructions.
That's not how it ever worked.
Quartz is a vector drawing engine whose output can be captured as a PDF and which is closely aligned with the PDF imaging model.
Window content was always stored as bitmaps.
You’re correct. I meant to say textures, but even that isn’t completely accurate.
AppKit used to generate the bitmap directly through CoreGraphics (PDF model). Now it uses Core Animation and Metal and no longer draws the bitmap directly.
You have understood things incorrectly.
> what isn't appropriate for it?
It'd be very silly to render a shader pipeline in WebKit. You could, but with Metal sitting right there, it would be silly.
If we're all agreeing that all the native elements are useless, I can see how the question is "do I use Metal or do I use WebKit", but my question wasn't about WebKit vs metal, it was WebKit vs native elements.
Yeah, this is actually my current in-progress solution: render the final Markdown & the streaming through WebKit.
And yes, I agree: on macOS, WebKit is a native OS framework. In that sense, it is "native". But I think it also supports the broader point I was making: if you want to work with rich text, Markdown, selection, typography, and long-form formatted content properly, web technologies quickly become the only viable option. I am not saying that using WebKit for a Markdown view is wrong. Quite the opposite, it is probably the most reasonable option available. The problem is that the "native" solution here is still effectively a web-rendering solution. There is a cost. Each `WKWebView` brings a WebKit engine with its own performance & memory overhead. So you cannot just sprinkle `WKWebView` everywhere & pretend it is free native macOS component as any other. My frustration is mostly that this is the answer. For this kind of UI, SwiftUI / AppKit / TextKit still do not give you a clean, modern, composable path that feels better than "just use WebKit".
> But I think it also supports the broader point I was making: if you want to work with rich text, Markdown, selection, typography, and long-form formatted content properly, web technologies quickly become the only viable option.
But, like, of course they are. This is what HTML was built for. The other major standard would probably be RTF, but it's a bit less structured, and so less close to Markdown. HTML is the better pick.
If you subsequently want to style that HTML, so that every second-level heading uses a specific font, and every third-level heading uses some other font, and so on, CSS is the best way to do that.
So, yes, we're saying the same thing, but to me it's a bit like saying "If you want to find the answer to 2 + 2, addition is the only viable option." Well, yes!
I think the reason this feels kind of wrong is because that same HTML and CSS renderer you're using for Markdown also comes with an entire 3D graphics pipeline and audio synthesizer. Obviously, we should be able to answer 2 + 2 without opening Mathematica.
I guess the important technical question is whether simply creating a WKWebView also loads in all that other stuff. I would hope and expect the OS is smarter than that, and you can call WebKit for simple HTML without everything else coming along.
I am with you here.
But I think my opinionated point from the article still stands: if you need rich text & good typography without fighting the platform, then web technologies quickly become the pragmatic choice.
For my app, I will probably continue with WebKit. It is the most reasonable middle ground for now. But in this situation, it is tempting to jump to something with a stronger rendering engine, like Chromium instead of WebKit, and start using the huge ecosystem of tools that already work. For example, https://diffs.com is one of the most tempting parts for me. The awkward thing is that embedding WebKit & calling it a day does not feel like a clean native solution either. You lose many of the native things you get when rendering through SwiftUI primitives, but you also do not get the full power & ecosystem of a proper web stack. And that makes it much easier to understand why so many companies (good & bad) choose Electron.
From an engineering perspective, even the fact that you can avoid this controversial middle ground entirely & build the app around web technologies from the start makes sense. It is not just laziness or ignorance of native platforms. Sometimes it is simply the more consistent & logical architecture.
> But I still cannot make a simple thing work properly: a chat with Markdown & the ability to select a whole message.
Sorry, sounds like bullsh_t. One can leverage mature markdown renderers in SwiftUI. See https://github.com/gonzalezreal/swift-markdown-ui and its next gen replacement https://github.com/gonzalezreal/textual .
Used these myself and had no issues. And I am a moron who doesn't like Swift or SwiftUI - preferred Objective-C, but still managed to do this, without any LLM help.
I tried Textual earlier today with some not-so-good results:
- Static completed Markdown scrolling fails the new focused probe. Result: p95 18.86 ms vs 16.7 ms budget, max 232.49 ms.
- Long live Markdown/code update path also fails. Result: p95 59.33 ms vs 16.7 ms, max 75.94 ms. This is a separate but related stress case around large rich text surfaces during updates.
- Long-history scaling technically passes, but the numbers are not smooth-frame healthy: - 120 turns: total p95 21.35 ms - 500 turns: total p95 23.11 ms - 1000 turns: total p95 36.77 ms
Technically, it is not bad. However, it is a bit slower than my own solution & has similar performance gaps, mostly related to SwiftUI rather than the Textual implementation.
Can those handle streaming in new text without flickering?
Either you render the markdown document just once (not streaming) or your document is simple and short.
I used to use swift-markdown-ui for my app but the performance is nowhere near using a wkwebview. When streaming large documents with tricky elements like large tables, code blocks, nested quotes, you may even get beached ball. It never happened when using a wkwebview.
The first one is the Claude iOS app uses and it seems to perform okay and has the ability to select text and stream stuff.
> > But I still cannot make a simple thing work properly: a chat with Markdown & the ability to select a whole message.
> Sorry, sounds like bullsh_t.
No... As a user, one thing I notice is that older, non-HTML-based apps don't seem to follow "the rules." Text that I should be able to select and push "control/option C" just isn't selectable, or copy doesn't work.
Then I realized that browsers (and everything based on them) introduced some new paradigms to UI that native UI frameworks just haven't kept up with.
(And I say this as someone who prefers native apps over web-based apps.)
Show your code, or show you the door. There are so many native Mac and iOS apps out there right now perfectly capable of rendering Markdown and streaming text. You just gotta wonder what is this guy’s excuse.
OP says "you want to select a whole Markdown document built from SwiftUI primitives", but who wants that? what sort of product thinking tells us we want that? that sounds like a document editor, which has been hard to build for decades and sounds out of scope for an llm chat ui. everyone has landed on only supporting selection within each contiguous block, with a copy button for the entire message
LLMs are often used to generate Markdown because they're quite good at it and unlike HTML it's very forgiving.
Rendering text into things like chat bubbles or even just generic output panes as it comes in is a massive pain. Every new word requires redoing layout, detecting LTR versus RTL flows and overrides, figuring out word breaks and line breaks, possibly combined with resizing the containing UI element (which involves measuring the render space, which is often implemented by rendering to a dummy canvas and finding out the limits).
Document editors have it relatively easy because humans type at a relatively low speed and pasting is a single operation (although pasting large amounts of text does hit the render performance of the UI). They're also often provide relatively limited features on phones.
If you want to render something like ChatGPT with similar features in native UI, youre going to need to find a fully-fledged document component or build one yourself. And, as it turns out, we have document components that work quite well: web engines.
If you embed a webview rendering just HTML and CSS, you get better performance, features, and accessibility than any home-grown renderer will provide. And with every major OS coming with a browser built in, it won't even bloat your app.
I build an AI chat app for a living (20k+ customers) and I can tell you, everyone has been asking for that.
sounds out of scope for an llm chat ui
What? No. This is like building a Slack clone without the ability to copy a stream of messages. It is entirely reasonable to want to do this.
Slack also basically fails at this. You end up getting a jumble of timestamps, names and messages.
Yeah... people expect that when they copy chat messages. It's correct behavior.
I don't mind the timestamps and names existing, but they're formatted all wrong.
Without web view ? Share the code ?
Fun fact: This is how Apple used to do it too.
Old versions of macOS / AppKit used to use WebKit to render rich text inside their native NSTextFields. Turns out text is hard :)
And besides, the native WebView is super fast and lightweight, and its not unreasonable to use it as a text layout engine. You could use separate webviews for every row in a table and you'd still get fantastic performance.
iMessage for mac used to use a webview too. Adium as well. HTML is absolutely the right tool for the job if you're rendering rich/marked-up text.
You’re confusing iOS and Mac OS here.
The Mac never used WebKit for NSTextField rendering. When iOS was first written, WebKit was used as the text renderer everywhere initially, including in UIKit controls (the “sweet solution”). This proved to be too heavyweight / cumbersome and the coretext/appkit text rendering approach was brought over.
...although the logic in the article is slightly odd:
Personally, I would have stopped at (3).I remember being a junior engineer in 2015, and being asked to render a clickable link within a paragraph in an iOS app. Swift had just been released so we were still entirely on the ObjC/UIKit stack. It was an absolute nightmare. I _barely_ managed to make it work. I haven't really touched iOS since about 2016, so I assumed the new SwiftUI stuff would have this stuff built in. Obviously. Kind of insane that it wasn't.
It's quite literally called Link
https://developer.apple.com/documentation/swiftui/link
I'm not sure how much easier they can make it at this point.
When I say "this stuff" I'm not talking about a link, I'm talking about the overall markdown/text capabilities that the post is talking about. I meant that I expected more parity with what you'd encounter on the web.
You expected highly capable, generic GUI toolkits to show parity with a development environment that has specifically targetted text above all else (though with lots of other stuff and great depth too) for decades?
Even in an era of PWAs and highly reactive UIs, the web is still fundamentally a document presentation mechanism. No generic GUI toolkit fits that description (even if they can be coerced into being one).
Given how much shit the web gets from native developers, yeah kinda? They make it seem like it's light years ahead of the web, often arrogantly so.
Qt made this pretty easy 10 years ago
Why pay a license fee when you can make a bloated and slow electron app instead?
NSLinkAttributeName?
My thought exactly. However, Apple’s developer documentation has never been particularly helpful, so I don’t blame very much for missing that.
I thought attributed text handled this fine since forever. Did it not?
I vaguely remember doing this with attributed text for iOS 4.
That said, I also had quite a lot of success on iOS 4 using HTML as the layout engine for the main screen of the app, though the place ran out of money before that went anywhere.
HTML can be really good, the blockers back then were it not being exactly the same as the Apple UI guidelines unless you put in a huge amount of extra work that nobody wanted to spend. I'm not sure when Apple's own guidelines stopped mattering exactly (iOS 7's invisible buttons necessarily had to be ignored, but there was already a decent level of custom UI before them and it was already essentially irrelevant even before Apple became extra-inconsistent with Liquid Glass), but I think we're now at the point where you only follow those guidelines if you (a) don't have your own UI team, and/or (b) want to try to aim for a shout-out from Apple.
Do not know about "forever", at the moment it works okay, I guess. But for a long long time most of the iOS apps were using this https://github.com/TTTAttributedLabel/TTTAttributedLabel to have proper support for links & other basic attributes.
>being asked to render a clickable link within a paragraph in an iOS app
The specific ask was already a bad idea.
the whole company was a shitshow; that was probably the least insane thing i was asked to do there.
> how immature all these “native” things still are when you step outside simple screens
Well yeah. If people don't invest sufficient effort in a thing why would there be an expectation for that thing to become mature? People are locked into web tech because that's where the greater majority of the effort has been going. Quite literally people look at native, say it isn't developed enough, and go develop for the web even more. Cycle repeats. Hardly anyone wants to put in the effort to improve native when things already "just work" for the browser.
Sure, but those the native UI dev kits are commercial products, right? Isn’t it their job to sell them to people — not people’s job to sell themselves on it? Part of the reason web stuff is so much more mature is the unwillingness of the big commercial OS manufacturers to keep up with the times. Windows UI kits are a hot fucking mess.
Agreed. He's basically complaining and moaning about Markdown not being fast to work with in Swift, when nobody has really put a lot of effort into that yet. yet despite this, he's not willing to contribute to that himself.
Sounds like it would be Apple's job to develop their own platform.
I think SwiftUI etc al don't work on Linux and Windoes and Android, right? While HTML works?
Give them a break. Why doesn't the OP just help develop a highly efficient SwiftUI extension for Markdown if it's that important? Remember that there are many efficient MD libraries for Swift/ObjC which would in any case be the sensible approach here (and it also explains why he can't find a SwiftUI alternative to his liking).
I've had pretty much the same experience with my AI chat app. Nothing works well. Markdown rendering is slow and laggy, streaming is slow and laggy, everything locks up the UI. I've tried at least 5 of the most popular text editor components for UIKit and SwiftUI on GitHub, and all were broken in one way or another, buggy, and slow as well. It's ridiculous.
Yep, this is a difficult problem. I wrote extensively how I managed to solve this by creating my block editor from scratch using Qt C++ and QML[1]. I faced similar issues - selection between discrete blocks, showing the underlying Markdown under the cursor, varying delegate sizes, etc.
I'm using what I learned to create a native LLM client with a streaming Markdown parser[2].
[1] https://rubymamistvalove.com/block-editor
[2] https://www.get-vox.com
If you need to display HTML content (what Markdown usually translates to) then WKWebView is the control to use! Or use something like litehtml which should be more than enough for Markdown unless you want to support "Animated Gifs" (that are actually H.264 movies these days) or whatever else.
You can still use native controls for the rest of the UI and have 0 Javascript running. I'm not sure I understand what the problem with NSTextView was though. It's pretty performant as far as I can tell?
I don’t recall ever struggling with NSTextView. I never really got into Swift, but I’ve never found Cocoa / Objective C to have any of the problems the author mentioned.
Not exactly sure what “streaming” text is, but serial terminal software has been handling incremental text rendering and updating for decades, without performance struggles.
`NSTextView` is good. My point is not that `NSTextView` itself is bad. The problem is that once you are working with all the "modern" Apple stack (Swift, SwiftUI, and the direction Apple is clearly pushing developers towards) `NSTextView` does not fit as naturally anymore. Some newer APIs are not even available for AppKit now, so you quickly end up in an awkward middle ground.
By "streaming" text, I mean a formatted text stream that has to be parsed, formatted, and appended on the fly - basically how every model/AI chat works now. And this is where `NSTextView` becomes tricky. It forces an interesting architectural choice: either go deeper into AppKit with `NSCollectionView`, custom cells, manual layout, etc., or fight the whole SwiftUI model by embedding something like `NSTextView` inside `LazyVStack` / SwiftUI views & then dealing with all the integration problems.
So I am not saying Cocoa / AppKit was always bad, or that `NSTextView` is useless. I am saying that for modern chat-style UI with incrementally rendered formatted text, it does not compose well with the rest of the modern Apple stack.
> There is no real alternative.
Not sure if my Sciter qualifies as a native solution.
Check this chat alike virtual list with MD items: https://sciter.com/wp-content/uploads/2026/05/virtual-list-m...
Yes, MD gets translated to DOM tree. But virtual list implementation in Sciter is a native thing. Load whole chat is not an option usually. Yes, JS is used in process but mostly as a configuration option: take output of one native function -> transform it -> pass as an input to other native function.
Essentially there is no so significant difference with any other SwiftUI/TextKit solution. It is just a difference in terms - SwiftUI uses tree of Views that is conceptually the same as DOM tree in terms of Sciter.
I’ve been there too. Finally opted for Flutter (need cross-platform mobile and desktop). Apple needs to put a big effort into the development tools, or they will face the actual Windows situation: several GUI toolkits, none of them as mature as the ones they are designed to replace. (Try to beat Windows Forms Professional Control Libraries and the boost in development performance you get. You can’t because the tool makers need a clear path and commitment in order to justify developing a new version of the full control libraries. You need cross-platform; forget the native tools. That’s the same scenario Apple is facing right now.)
Yep. Electron is the worst way to make a desktop app… except for all the others!
A thing I’ve always wanted was a visual JSON viewer that instantly opened on multi-hundred-meg files. So I used Claude Code to build one with native text views and it’s true it’s pretty raw. But for a thing that doesn’t need formatting, dictionary, and all that it’s great. The viewer opens fast enough that it’s dominated by the window rendering animation which is about what I wanted here.
So I think the text view is pretty low level so that it can support this.
Just use AttributedString
I just wish there was a native Markdown renderer / editor library in C that I can use cross-platform - in the style of something like IMGUI (where the library outputs a list of primitives for you to render yourself in any graphics API).
Or well... since we now have Claude I might have a jab at this someday in my free time.
For just rendering (no editing) you could use https://github.com/litehtml/litehtml (C) or https://github.com/DioxusLabs/blitz (Rust).
Both are actually lightweight HTML rendering libraries, so you need to compile markdown to HTML to use them. But there are many libraries for that.
The link says litehtml is C++. I can't tell if it exposes an FFI (I bet not)
Of course blitz doesn't expose a FFI either and also if you need anything interactive you have to use the dioxius framework or implement you own APIs for that as well as take care of animation yourself.
Does it mix well with text input? What I really want is a native WYSIWYG Markdown editor - in a similar fashion to Typora (Electron) or Milkdown (a JS library).
Blitz has good plaintext text input support. But there's no contenteditable (which is what you would need for rich text editing) yet.
litehtml appears to have no built-in text input support so far as I can see.
We did https://markant.md with TextKit 1, flies through multi-megabytes markdown files with latex rendering etc. took some scaffolding (like only rendering attachments when they are close to the viewport) to make it smooth, but it wasn’t really a big problem.
Maybe controversial but I think HTML + CSS is truly the most powerful system to make GUIs.
There’s really nothing else out there that competes with a similar performance and productivity.
This old article by the Missive team (the email client) convinced me.
https://medium.com/missive-app/our-dirty-little-secret-cross...
In a way it really is, in part because HTML + CSS is the back door "universal native GUI framework" that various projects have been chasing for years. Because browsers and GUI elements in them are so important to modern computer use, every OS vendor has a vested interest in ensuring the default look and behavior of elements in their browsers are native (or as native as possible). As a result, if you can render your UI in a browser (or in a browser frame/view) chances are you can get a native looking and native feeling UI for your application with a well understood and robust piece of technology that will retain that look and feel even as the underlying OS changes around it.
Powerful, perhaps. Slow, for sure.
I don’t know. Resizing a window with complex ui in html tends to be faster than native guis for me. It haven’t tested this properly though.
I just use a fullscreen <canvas> and WebGPU. It's performant as hell.
Skill issue, I guess. /s
Performance is relative. Every electron app I have used has been slower than their equivalent native app.
One of the reasons I decided to stay in the shadows as an unglamorous, boring backend developer. Every single time I tried any frontend development, either web or mobile, the moment I started running into issues like this requiring witchcraft to accomplish an objectively trivial task, a bit of my soul left my body.
I know nothing about any of these APIs, but the claim of the article seems weird. If naitive APIs are insufficient, or slow, or unsuitable, and implementing your own is too hard, then how does Electron even do what it does? One would assume that Electron has its own library to accomplish the task, in which case this code could either be separated, or re-created once and for all, into its own re-usable library.
You can just implement Prosemirror from one of the greatest web teams on the planet and get pretty much every text editing nicety for free - markdown rendering, document version history, blocks, tables. If you choose to deal with prosemirror-collab-cmmit, yjs, or automerge you also get eventually consistent multiplayer. All the "I wish this was a native app" people don't understand the cathedrals that have been built on the web.
Chromium has had an insane amount of investment from many large companies. Way more than native UI frameworks have had over the last decade or so.
> I know nothing about any of these APIs
Agreed. In Chromium all the content from HTML is rendered inside a single object from the point of view of the host UI; much like a game engine’s UI rendering. Chromium draws everything itself. Host events like mouse and keyboard events are sent to that top level object (although there are some shenanigans involved to make it look more native to accessibility tools).
Electron is just a thin shell around Chromium
"skills issue" but also "native" frameworks are lacking polished API. On macOS TextKit2 is unfortunately kinda broken, how do I know? I reimplemented TextView with it https://blog.krzyzanowskim.com/2025/08/14/textkit-2-the-prom...
Hey Marcin,
Skill issue, I guess. I even tried your SSTextView (which is a very nice piece of software, by the way), though it does fit here, but I tried to understand how wrong my TextKit2 implementation is. In my tests, the SSTextView performed a bit worse with p95 on the static markdown scroll test (70.20 ms vs 16.7 ms for per frame rendering). But it is clear from the traces that SSTextView just does too many things I do not need. At least, I had my confirmation that I am not completely wrong about TextKit.
totally. the there's a lot complexity that adds up to the overall performance issues. and TextKit 2 IS pretty bad at things. especially public API is pretty bad - that result in my case need to workaround things that I should've not. I agree with the general sentiment et all. I also still believe there is a place without bringing the whole browser machine to render text, and have text under control - but without relying on the "TextKit" level. That's the next thing I'm researching right now.
I am currently experimenting with linux based GUIs. It was always something that felt clunky to me, but now with more insights, it's clunky for a reason. If you need more then a framebuffer, then rendering something sophisticated to the screen is insanely complex. Somehow it's easy to expect that rendering text on a screen should be easy, but when you go down the layers you find yourself with a club and a flint stone trying to build a castle with it.
Wayland is another product of this hardships, going wayland native seems only feasible when all stars align around it. But then you are stuck in that place.
That being said, without deeper knowledge about SwiftUI, I find it a bit odd to expect so much from a novel concept. Native desktop dev is already kind of niche, considering the dominance of web dev. Chrome (and it's artifacts) is probably the best funded software in the world and google's incentive to improve it is above all. It's not a miracle that it just works. It's effort and tons of cash.
> Somehow it's easy to expect that rendering text on a screen should be easy
This is a common misconception among programmers, and is actually the opposite of the truth. Drawing arbitrary geometric shapes is easy, rendering text correctly is insanely difficult because ... humans.
Why not use native for UI frame (menu, toolbar, conversation list etc) and WebKit for the actual chat? I think that would combine the best of both worlds.
Yep. That is what I did for my AI chat app and it’s indeed the best of both worlds.
I understand your pain. That’s why I’ve ported my VMPrint layout engine to Rust. It’s early, but it already shows promising performance improvements over the original TypeScript-based engine, which is already very fast. The Rust version can create fully paginated, publishing-grade layout at around 8,500 pages (or 2,000,000 words) per second on a M4 MacBook. It’s even faster at advanced tasks like mixing texts with irregular exclusion fields. The TS version can do it under 1ms, but I don’t have a measure yet for the Rust version. Unfortunately, people have shown little interest in this kind of components, so I’m no longer inspired to release it in its raw form like I did with VMPrint. My plan is to use it to build a native markdown editor first to test it more fully and just to have fun with it, LOL.
Your FAQ says:
So, wouldn't porting VMPrint to Rust make it such that Typst is the clear winner? Or is there something else missing?Just checked out VMPrint and it's crazy! Keep up the efforts. If you/someone could get a HTML/CSS input layer in front of VMPrint that would be a killer feature? Or is it possible already?
HTML/CSS is notoriously bad for print.
Basic text styles are ok, but things like authored pagination, page header/footer, mirrored margins, margin notes, footnotes and references are basically unsupported or need to be hacked together.
Everyone loves to complain about the "bloat" in Chrome, but how many have actually taken the time to measure it against a native rewrite? Love this article. We take so much for granted in the modern WebKit/Blink stack. Modern multilingual text processing is a genuinely hard problem.
How is "performance" defined? Does it take into account the amount of memory required in each case?
The purpose of limiting memory use is so your computer does not become laggy as you run out of memory. We don’t do it for its own sake.
But then, what’s the point in using an inherently laggy technique to save memory?
The purpose of not wasting memory is so we have free memory to use productively.
What's the point of having 64-128GB of RAM if we're using apps that eat 10GB to do the same things we were doing 20 years ago using a few MB?
Was going to answer almost the same.
This is my pet project, a desktop app for working with xAI models & capabilities, so by "performance" I mostly mean "pleasant to use" (as it goes, simple & opinionated). Technically speaking, something like: stable FPS, no visible lags, and the ability to scroll smoothly while the model is streaming.
Regarding the parent comment: yes, memory is important, and I absolutely get the point. There should be a red line, for sure. But I will not sacrifice UX, productivity, and simple pleasure from using software just to save a few hundred megabytes of RAM (or even a few gigabytes) especially for an app I spend hours with behind the screen.
Memory consumption can & should be optimised with proper engineering for sure. As lags & inadequate performance in basic SDK-level primitives are much harder (impossible?) to fix from the outside.
How about running many tasks on the machine at the same time?
Apparently devs forget that there are other apps running on the target machines. It's OK to just gobble as much of the resources as possible.
I am sure that they use MacBook Neo for development. /s
first you make it correct, than you make it fast
a fast performant incomplete solution will lose to a slow correct complete one
You can’t make a lightweight airplane by making a heavy airplane and cutting off some parts.
This is where QT/JUCE can help. Although you are limited to c++.
There are bindings for Go (https://github.com/mappu/miqt) and Zig (https://github.com/rcalixte/libqt6zig) but not for Rust. We need bindings like these for Rust.
If you are looking for something similar but not limited to C++, you can check Slint out: https://github.com/slint-ui/slint/
This is not what the people want. Understand that. Give us Rust and Qt. Why be so focused on trying to sell something that doesn't measure up? Even beta Bridges is better than Slint. Take the advice and put the energy to better use for the good of Rust.
It is tricky, but it is not unheard of to write Qt applications as something other than C++.
These days you write the logic in C++ and UI in QML which is a very pleasent experience.
Rust bindings exist if you don't like c++
The problem here is that you are not choosing based on knowing how the render pipeline is implemented in these tools and how it would work with your usage of it.
You can do a couple days to a week of reading to understand the fundamentals once and then you will actually know what you are doing.
It is not proper to choose things on “battle tested” or other meaningless words
I think in light of the fact that OP included exactly to what they are trying to do, this comment would be helpful to include a more concrete recommendation.
It’s easy to hand wave and say “this wouldn’t be an issue if you knew what you were doing”, but that indeed is the problem.
This is mainly from my experience developing storage engines using datafusion/polars or arrow2/arrow-rs or rocksdb-msbx.
I was changing between them and searching for comparisons online. This ended up being a massive amount of lost time because all of those choices became crystal clear when I actually roughly understood what these libraries were doing.
And actually learning the thing didn’t take as much time as writing code for comparison and discarding it or doing dead-end web searches.
Recently had a similar experience trying to learn dwarf parsing from LLMs or searching for existing code. Then I just realised that reading the spec is by far the most efficient way to understand it.
I am guessing same principle applies to text rendering because I got the same vibe when watching Raph Levien talk about it on some video.
Searching online to read some “industry-standard” “tried and true” etc. Comments is a big sign that it might be better read some actual source about the topic imo. It doesn’t even take that much time to read a textbook even.
I was using Markdown text editors with WPF back in 2012....
And yes WPF is a framework native to the Windows platform ecosystem.
If you squint enough, you'll see the official Google doc app for Android/iOS is a webview (i.e the editor part)
Fancy text rendering/editing is hard to implement when you leave the luxury of webviews.
This explains why so many AI chat tools suck at text selection on MacOS / iOS. They got the streaming and markdown part right … flicker free, but at the cost of text selection.
Just AI tools? Most "modern" chat apps suck at text selection because they're in Electron. I can't select parts of whatsapp messages for example.
Those usually use Electron not the native controls.
How bear solves this? It is looks native to me.
Not just text. Try to build a ui where you need non-trivial and non-standard behavior and SwiftUI will fail. AppKit is still better in this regard.
> Try to build a ui where you need non-trivial and non-standard behavior and SwiftUI will fail.
I think this may be a misundertstanding of what SwiftUI is. SwiftUI makes it convenient to create apps that look and behave in a way that align with Apple's HIG using controls like `List`, `Form`, etc., but nothing makes you use any of those. For example, it's straightfoward to build a game engine on SwiftUI. https://blog.jacobstechtavern.com/p/swiftui-game-engine
Electron runs like crap on older hardware. It's sad that native UI frameworks never got their shit together, but I think if you want performant text rendering you just gotta reduce your expectations. If you're fine with less fancy fonts and "scripting" your UI in something else than JS, then native can work. But it very quickly reveal that you should avoid everything that uses JSON. JSON is just a disease vector for the JavaScript world to infect everything else.
Kotlin MP is also pretty decent on Mac
You can just embed a web view in your app, though?
markdown to html. or markdown stays markdown.
the browser never chokes on html.
No insight as to why this is happening?
Where is the profile? Where is the bottleneck?
Just complaining with nothing to contribute.
Hu? We just switched from textual to native because native markdown rendering is finally good. If it was written a year or two ago ok... but now is odd.
the only place where native UI is still better is for ultra-complex UIs - image/video/3d/audio editors. and only because it's easier to create custom UI widgets/renderers than on web stack.
that's it, for everything else native UIs are complete garbage compared to HTML/CSS/reactive frameworks.
I once tried mobile development in semi early days android. At the time I made a free Hackaday reader app because I was a daily reader and loved it.
I remember spending 4 hours to make a scrollable element that wasn't jumpy or buggy. There were several stackoverflow answers full of gotchas explaining all you had to do. I finished and published the app but never again. Native stuff has terrible developer experience.
> "I once tried mobile development in semi early days android."
Yeah those early days ~2010ish were very painful. Things got much better as early as 2016 and they have improved each subsequent year since.
I'd say there has never been a better time than now, in terms of tooling, to pick up native Android.
Plenty of rough edges still around though.
Outside of niche applications (e.g. virtual desktops, gamming, embedded systems) native UIs are dead.
There are even parts of both Windows and MacOS rendered through HTML. If I remember correctly, at least in Windows 10, File Explorer was rendered through Internet Explorer.
Web rendering doesn't need to be only through Electron/Node. There are other libraries much more performant and lean (Dioxus, etc).
> native UIs are dead.
Not in the world of macOS and iOS at least. Here native apps still rule, as there's literally no performant alternative (the OP's complaints about Markdown are misplaced - there's been no interest in MD and SwiftUI and that's why there's no good option. But in ObjC/Swift there is).
In fact, most of the apps I am using on a day to day is native. The Electron apps I use are okay (e.g. Slack) but they absolutely fail the native Turing test.
> the native Turing test
What does this mean?
Basically that it's easy - for a user - to determine that a particular app is definitely not built using native SDK. Slack is an example.
do you miss Hypercard yet?
I think the article misses the actual point?
The browser is faster because they went native, in particular, GPU.
Every issue described is text rendering related. Everyone.
And I would bet most of the SwiftUI issues could be solved with a text render cache.
Something like Casey Murati's refterm toy that showed what that can do with no other optimizations, or the work for GPU accelerated terminal emulators like alacritty or ghostty.
I thought models were so good we could vibecode a text renderer for $50. What's the problem here? /s