← All posts

MDN rebuilds its frontend with web components instead of a React wrapper

How MDN moved from a React SPA shell to Lit, server components, and interactive islands—and why content sites should care.

MDN rebuilds its frontend with web components instead of a React wrapper
Contents

In brief

MDN shipped a new frontend: unified design on the surface, and under the hood a break from using React as a wrapper around static HTML. The team adopted web components (Lit), custom server components, and a flat component CSS/JS layout so documentation pages do not pay for a full SPA bundle.

What happened

Content still lives in Markdown in git; the build turns it into JSON; the frontend assembles pages on the server. The old stack (yari, ejected Create React App, heavy Webpack, mixed Sass/modern CSS) carried debt: React wrapped docs via dangerouslySetInnerHTML, in-content interactivity used raw DOM APIs, and logic was duplicated.

Since 2024 the team experimented with Lit for embeds (Scrims, interactive examples), then rewrote the site shell: server HTML templating instead of an SPA, isolated custom elements with Declarative Shadow DOM, and per-component CSS/JS instead of one blob per route.

Why it matters

For content-heavy sites (docs, blogs, references), a single React bundle fights reality: most of the page is prose and samples, not an app. React Server Components help but imply framework migration and RSC complexity.

MDN’s approach is closer to islands (Astro-style): cheap static HTML, interactivity only where needed. Web components can be dropped into Markdown macros (<scrim-inline>, <interactive-example>) without reparsing HTML in the client.

Old yari pain New direction
React cannot “see” Markdown HTML Web components inside content
One CSS bundle for all routes element.css / server.css per component
React + DOM API duplication One Lit component per widget
Extra JS to re-verify static UI SSR without SPA re-render tax

In practice

  1. Avoid SPAs for documentation — local interactivity (copy button, playground, embed) belongs in a custom element or lazy iframe.
  2. Lit vs React for small state (dialog, lazy iframe) is often simpler: template literals, no JSX build step for an in-article widget.
  3. Flat component folders (element.js, server.js, global.css) prevent accidental cross-page style leaks.
  4. Migrate incrementally — MDN rendered Lit inside the legacy React app before rewriting the whole Playground.

If you already use Astro/SSG, you are aligned with this philosophy; the MDN post is a solid reference when arguing against “Next for the entire reference site.”

Takeaway

MDN proves documentation is not an SPA. Web components plus server templating reduce JS, simplify in-content interactivity, and remove the wrapper anti-pattern. Worth revisiting your public docs stack, not only product dashboards.