DEV Community

Sanjay Kumar Sah
Sanjay Kumar Sah Subscriber

Posted on

How I Improved Email Rendering Performance 15x Using Rust + WebAssembly (Next.js 16 Devlog)

I decided to do something scary: Build a SaaS product in public, from scratch, sharing every win and every embarrassing error log along the way.

The product is Ansomail—an AI-powered drag-and-drop email editor.
The goal? To build a tool where Developers set the rules (Design System) and Marketers just drag-and-drop blocks, without ever breaking the layout.

Here is the unvarnished truth of my first week: battling the "bleeding edge" tax of Next.js 16, crashing my database, and eventually rewriting my engine in Rust for a 15x speed boost.

The Stack: Betting on Speed

I didn't want a legacy stack. I wanted speed.

  • Runtime: Bun (for instant startup)
  • Framework: Next.js 16 (Turbopack)
  • Monorepo: Turborepo (preparing for the Ansospace ecosystem)
  • Database: Postgres + Drizzle ORM
  • Linting: Biome (Farewell, ESLint)

It looked perfect on paper. Then I started coding.

Days 1-2: The Architecture & The "AI Architect"

The first challenge wasn't code; it was philosophy. How do you stop an AI from generating broken HTML?

Most AI builders treat LLMs like creative writers. I decided to treat them like Strict Architects. I set up a system where the AI's output is validated against a rigid JSON Schema. If the AI hallucinates a CSS class that doesn't exist in my system, the update is rejected before it ever hits the UI.

I also set up the "Hello World" of modern SaaS: An empty repo configured with Biome and Bun. It was clean, fast, and satisfying.

Days 3-4: The "Bleeding Edge" Tax

Then came the pain.

I hit what I call the "Bleeding Edge Tax." Next.js 16 and Bun are incredible, but they don't always play nice together. My dev server started crashing on hot reloads.

And then, I killed my database. 💀

FATAL: sorry, too many clients already

I started getting the dreaded error:
FATAL: sorry, too many clients already

Because I'm using a serverless environment, every hot reload was opening a new connection to Postgres without closing the old one. My database choked.

The Fix: I had to implement strict Connection Pooling. It was a harsh reminder that "Serverless" doesn't mean "Ops-less." Once I capped the active connections, the backend stabilized, and I shipped the "My Templates" dashboard.

Days 5-6: Chasing the "Google Docs" Feel

With the backend stable, I moved to UX. I wanted a "Rename Template" feature that felt like Google Docs—instant.

I implemented Optimistic UI. When a user types, the UI updates immediately. The server validates it in the background. If it fails, we revert. If it works, the user never notices the lag.

I also built the first version of the Rendering Engine:

  1. DB stores JSON.
  2. Parser converts JSON → MJML.
  3. mjml-browser converts MJML → HTML.
  4. Result renders in an iframe.

It worked. But it was slow. ~18ms per render. For a drag-and-drop tool, that felt sluggish.

Day 7: The Rust Rewrite (15x Speed Boost) 🚀

I couldn't settle for 18ms. I knew mjml-browser (Pure JS) was the bottleneck.

So, I did something drastic for a one-week-old project: I swapped the engine to Rust.

import { Engine, type ToHtmlResult } from "mrml";

const convertMJMLToHTML = (mjmlString: string) => {
  const engine = new Engine();
  const result = engine.toHtml(mjmlString);
  return result;
};
Enter fullscreen mode Exit fullscreen mode

I integrated MRML (a Rust port of MJML) via WebAssembly.
The integration was tricky—Rust is strict. It refused to compile my "sloppy" JSON that JS had been happily ignoring. It forced me to fix my data structure.

But once it worked? The results were insane.

  • JS Engine: ~18ms
  • Rust (WASM): ~1.1ms

That is a 15x speed increase. The preview is now effectively real-time, running at 60fps.

That is a 15x speed increase.

What's Next?

Week 1 was about the Foundation. Week 2 is about the Interaction.

Now that I have a 1ms rendering engine, I'm building the actual Drag-and-Drop layer.

I'm documenting this entire journey daily on X (Twitter). If you're into Next.js, Rust, or just want to see a developer struggle and succeed in real-time, come say hi!

Follow the journey: #ansomail / @ansomail
Connect with me: x.com/sanjaysah101

Top comments (0)