984: How to Make a DOM Library Render Anything w/ Paolo Ricciuti

Summary of 984: How to Make a DOM Library Render Anything w/ Paolo Ricciuti

by Wes Bos & Scott Tolinski - Full Stack JavaScript Web Developers

49mMarch 4, 2026

Overview of 984: How to Make a DOM Library Render Anything w/ Paolo Ricciuti (Syntax)

This episode of Syntax (hosts Wes Bos & Scott Tolinski) features Paolo Ricciuti (Svelte maintainer and lead of the Svelte team at Mainmatter) discussing Svelte custom renderers — what they are, why the Svelte team is building them, the technical challenges, the approaches taken, and the current status/timeline. The conversation contrasts Svelte’s compiled DOM-focused model with React’s reconciler/virtual-DOM approach, explains the work needed to make Svelte render to non-DOM targets (terminals, native UIs, WebGL, etc.), and describes Paolo’s proof-of-concept work (notably for Lynx).

Key takeaways

  • Svelte historically targets the browser by compiling components into direct DOM-manipulating JS. That differs from React’s virtual-DOM + renderer model which makes multi-target rendering easier.
  • To let Svelte render to other targets (terminals, native, WebGL), Svelte needs a pluggable "custom renderer" API that delegates element creation/updating/removal and event handling to user-provided adapters.
  • Two main design approaches were considered:
    • Shim/mock a DOM API around custom renderer objects (lots of DOM quirks; messy).
    • Swap/branch the internal runtime API Svelte compiles against so it calls a renderer-specific runtime (Paolo’s preferred approach).
  • Practical changes already made: Svelte compiler can emit a structured fragment representation (instead of innerHTML/template strings) — this helps avoid CSP/innerHTML problems and is essential for custom renderers.
  • Some features (hydration, certain DOM-specific bind behaviors) don’t map to non-DOM targets and will be disabled or require special handling in custom-rendered apps.
  • Paolo built POCs: a basic Lynx renderer (to-do app), and a simple terminal renderer demonstrating reactive updates.
  • The project is substantial and currently seeking sponsorship/funding to finish; Syntax (the podcast/app) is sponsoring some work.

Topics discussed

  • How React uses a diffing reconciler and separate renderers (React DOM, React Native, Ink) to enable different platforms.
  • How Svelte compiles components into DOM operations (showing compiled JS via the Svelte playground / VS Code command).
  • The template.innerHTML trick Svelte used to create DOM nodes from compiled HTML strings and why that blocks rendering to non-DOM targets.
  • The new compiler option that emits structured fragments (arrays of element shapes) instead of template strings.
  • Two renderer strategies: DOM-shim vs separate/custom runtime import.
  • Practical demos: Lynx renderer POC, terminal renderer POC, support for CSS in Lynx via Lynx’s CSS support.
  • Limitations (bindings, hydration, CSS translation work, many DOM edge-cases).
  • Community/funding model for finishing work; where to contact/sponsor.

Technical deep dive / notable insights

  • Svelte’s compiled output is highly readable in the playground. Svelte components compile to JS functions that directly call DOM APIs (e.g., createElement, setAttribute, setText).
  • Previously Svelte used template.innerHTML to convert the static HTML portion into real DOM nodes. That approach:
    • Is fast in the browser, but
    • Breaks CSPs and is impossible on non-DOM targets.
  • The compiler now supports emitting a fragments representation (an array/object tree describing nodes + props) so runtimes can instantiate nodes without parsing HTML strings.
  • Two implementation paths:
    • DOM shim: wrap custom-renderer nodes in objects that mimic DOM APIs (but the DOM has many subtle, performant quirks making this brittle).
    • Runtime swap/branch: compile Svelte to import a runtime module (SvelteInternalClient vs SvelteInternalCustom) and implement a renderer runtime that provides the lower-level APIs Svelte expects. Paolo initially built a custom runtime POC and a Lynx renderer.
  • Hydration is browser-specific — custom renderers can skip hydration logic and therefore simplify the runtime.
  • CSS in many non-web targets: Lynx supports a good subset of CSS so component-scoped styles can largely be preserved; other platforms will need CSS translation logic or platform-specific mappings.
  • Binding behaviors (e.g., bind:value) are DOM-centric; some bindings may be disabled or converted to event/listener patterns for custom targets.

Challenges & trade-offs

  • DOM is messy and full of quirks used for performance (e.g., textContent = '' clears children). Shimming these is error-prone and inefficient.
  • Maintaining two separate runtimes (client + custom) risks code duplication and ongoing maintenance burden. Paolo is moving toward a single runtime with branching for DOM shortcuts vs renderer calls.
  • Some Svelte features simply don’t make sense outside the web (hydration), so mapping Svelte’s full feature set across all renderers is not feasible — trade-offs are needed.
  • Funding/time: the work is large; Paolo worked full-time for a few months under sponsorship and now seeks more sponsors to finish.

Current status & timeline (practical status)

  • Paolo built POCs: fragments compiler output, a minimal custom runtime, a Lynx renderer POC (to-do app), and a basic terminal renderer.
  • The plan is to converge on a single runtime with branching to avoid duplicated maintenance while still supporting DOM-optimized shortcuts.
  • No firm release date. Progress depends on more sponsorship/funding and continued contributor time.
  • How to help/sponsor: visit svelte-custom-renderers.com to get in touch and explore sponsoring the effort. Syntax.fm indicated they will sponsor part of the work.

Action items / How you can engage

  • If your company needs Svelte-native or alternate-target apps (native, WebGL, terminal, etc.), consider sponsoring the custom renderers effort (svelte-custom-renderers.com).
  • Try the Svelte Playground and use the "JS output" tab and VS Code command ("Show compiled code") to learn how Svelte compiles components.
  • If you can contribute code or time: follow the Svelte repo and the custom-renderers discussions; review the fragments output and the runtime POC when available.
  • For Lynx/React Native-style projects, consider whether a renderer for your platform would be useful—this can justify sponsorship.

Notable quotes

  • “Svelte does not use a virtual DOM — it compiles to direct DOM operations.” — Paolo
  • “We had to change the compiler so it can output fragments (an array describing elements) rather than template.innerHTML strings — that was a required step.” — Paolo
  • “The DOM is just a mess… there are so many quirks.” — Paolo, explaining why a DOM-shim approach is brittle.

Picks & plugs (from Paolo)

  • Sick pick: OpenCode — Paolo uses it as his main AI tool because it’s configurable and model-agnostic.
  • Plugs:
    • svelte-custom-renderers.com — to sponsor or contact the team working on custom renderers.
    • Mainmatter blog: Paolo’s post “Why I choose (and continue to choose) Svelte” (mainmatter.com blog).
    • Paolo’s TMCP project — an alternative TypeScript Minecraft server SDK (lightweight, modular).
    • Valibot — Paolo’s preferred schema/validation library for small bundle size.

Resources mentioned

  • MadCSS tournament: madcss.com (promo at episode start)
  • Svelte Playground (JS output / source maps)
  • Svelte compiled-output command in VS Code (Show compiled code)
  • svelte-custom-renderers.com — contact/sponsor page for this initiative
  • Mainmatter blog — Paolo’s article on why he uses Svelte
  • Lynx (framework-agnostic/native alternative referenced in discussion)
  • Ink (React terminal UI renderer), React Native, Raycast (examples of non-browser React renderers)
  • Sentry sponsor mention: sentry.io/syntax (ad for episode)

If you want the most important single follow-up: check the Svelte Playground’s JS output to see how Svelte compiles to DOM calls, and if your team needs native or non-DOM targets for Svelte, consider reaching out via svelte-custom-renderers.com to sponsor the work.