How we Rewrote 130K Lines from React to Svelte in Two Weeks
We ported Strawberry's entire frontend from React to Svelte in two weeks using coding agents. It made the browser 2x faster.

Introduction
We ported Strawberry browser's entire UI from React to Svelte. It took two weeks.
One of our founding engineers had become obsessed with browser performance. Convinced that Svelte was the answer, he spent a few evenings and a weekend using swarms of coding agents to rewrite everything. By the time he showed the team, he was 60% through the 130k LoC migration. The performance gains made it obvious: we had to finish what he started.
So we locked in and made it happen. Here's how we did it.
TL;DR
- The Result: Strawberry is 2x faster in production and our responsiveness is now on par with incumbent browsers
- React's problem: React's virtual DOM rebuilds and reconciles on every render. The wrong model for a browser that needs to be instant. Parallel LLM token streams made it worse, triggering constant re-renders across the UI.
- Coding agents with strict rules: To pull this off, a detailed Svelte ruleset for agents was necessary. It allowed one person to move faster than an entire team of devs could two years ago.
1. Why We Moved Away from React
Strawberry is a web browser, not a website. That distinction matters more than it sounds.
React maintains a virtual DOM. On every render, it rebuilds an internal model of the UI and reconciles it against the real DOM. That overhead is negligible for a single-page website. In a browser, it compounds fast:
- Multiple React apps running at once: Strawberry uses separate renderer processes for settings pages, modals, dropdowns, and other UI components. Each renderer contained its own React app. So, if you open four settings pages, the chat sidebar, and a dropdown, that's six React apps simultaneously. Events from our main process would cause all of them to update at once, creating rendering cascades that made everything feel sluggish, even on high-end devices.
- AI agents cause re-renders constantly: AI running in the background pushes updates to the UI non-stop. React has no surgical way to handle this, which means every update triggers a re-render of the component tree, even when only one value changed.
- Workarounds don't fix the model: State libraries like Jotai and Zustand exist for exactly this problem. But they're patches bolted on top of React. Svelte 5's Runes give you fine-grained reactivity natively, compiling to plain JavaScript that surgically updates only what changed, with no runtime overhead.
React was working for us, it just wasn't fast enough.
When building a browser, each keystroke, each tab opened, each menu opened needs to feel instant. With Svelte, they do.
2. How We Pulled It Off In Two Weeks
Instead of writing a migration proposal and spending weeks on planning, Markus built 60% of the replacement and showed it to the team. This is a key lesson in the age of coding agents: If you're trying to convince a team to do a major rewrite or feature implementation, a working demo will do more than any document.
A year ago this would be terrible advice. You'd risk weeks on something that gets scrapped. But now that building a working proof of concept with agents costs less than writing the proposal, the calculus has changed.
Researching and Creating Rules Before Starting
Markus started by using Strawberry to research Svelte best practices across docs, repos, and community threads. He compiled everything into a master ruleset. Things like "Avoid $effect", "Prefer stores over component state", "Keep reactivity explicit." That took about a day, but it's the reason everything after moved so fast.
"LLMs write much worse Svelte than you think. They reach for $effect out of React habit and you quickly lose Svelte's performance benefits. However, with a strict, best practices ruleset the agents will keep the code clean and performant."
— Markus Andersson, Founding Engineer
How the Actual Port was Done
The migration was feature-by-feature, starting with Strawberry's tabs functionality. Markus let Cursor port the tab provider from React to a Svelte singleton class powered by $state runes. He tested the new implementation thoroughly, then moved on. Every other feature followed the same pattern:
- Read the rules,
- Port the code,
- Test it,
- Repeat until all 130k lines are migrated.
We mainly used Cursor and Cursor Rules. Claude Code and Codex were used toward the end of the rewrite, but in general Strawberry devs prefer Cursor since it grants finer, more granular control over the code.
We kept the old React code in a react-src folder on the rewrite branch so agents had a reference for how everything originally worked. Since the rest of the team kept shipping features on the old codebase, react-src had to be continuously synced with dev.
This syncing wasn't always perfect, and it resulted in a few features being silently dropped at launch. They've since been reimplemented.
It was a small price to pay for performance salvation.
Missing Svelte Packages
One concern going into the rewrite was that Svelte's ecosystem is less mature than React's, and that we might be missing critical packages.
In practice, almost everything we needed existed. The one exception was a drag and drop library. No existing Svelte package had all the configuration options we needed for our tab bar, so another founding engineer built one from scratch. It ended up being the most time-consuming part of the entire migration and still wasn't fully stable at launch.
In the following update it was fully stable, and now even has super satisfying haptic feedback on Mac.
103K lines added. 124K lines removed. One engineer.
A year ago, this type of work was a months-long team effort. Today, one person with coding agents and strict project guidelines can move faster than a team could without them.
For context: Lovable rewrote their 44K-line Python backend in Go in two months. We rebuilt the frontend of our browser (130K LoC) in two weeks.
Neither would have been possible two years ago.
3. What Actually Changed
Switching to Svelte gave us a browser that keeps up with the agents running inside it.
- 2x faster in production mode
- FCP (First Content Paint) for new tab page reduced from 300ms to 124ms, meaning tabs show up faster
- 200ms gained from a single tree-shaking pass, thanks to better import structure
- Token streams from LLMs are much smoother, re-renders reduced by 10x
Before Svelte, agent streams would occasionally outpace the UI, making the stream of tokens feel sluggish. After the switch, the UI finishes rendering before the stream completes. That's what zero runtime overhead looks like in practice.
We also took the opportunity to tighten the codebase, add more tests, and remove 56 unused dependencies. A nice side effect is that the codebase is now easier for LLMs to work with.
4. What This Means If You're Building AI Products
The standard advice is: don't rewrite.
And it's usually right.
But we think the case is stronger than most people acknowledge for one specific type of product: one where the UI is constantly responding to AI. Streaming output, parallel agents, real-time state changes. If that describes what you're building, React's overhead is a compounding cost every time something updates (for an AI product, that's constantly).
For comparison: Chrome's UI is a mix of native C++ views and TypeScript frontends with C++ message handlers in between. That means compile steps after every change and type boundaries where agents can introduce hard-to-find bugs.
Our entire UI is Svelte and TypeScript, end to end. Agents get instant feedback and can work across the full stack without switching contexts. That matters now, and it'll matter more in six months.
AI-assisted development has also made migrations far more doable. Parallel coding agents and good project guidelines got us to 85% completion in two weeks, on a product with a year of React behind it.
The question isn't whether you can afford to do it. It's whether you can afford to wait.
Conclusion
The whole rewrite took two weeks and made the browser noticeably faster. The codebase is cleaner. Tests are better. And honestly, it's just more fun to work in now.