Svelte’s onMount only ever runs in the browser — in SvelteKit that holds true even for server-rendered routes — so mounting @maildeno/editor is a straightforward bind:this plus onMount/onDestroy pair.

Installation

npm install @maildeno/editor

The editor component

<!-- Editor.svelte -->
<script lang="ts">
  import { onMount, onDestroy } from "svelte";
  import type { EditorHandle } from "@maildeno/editor/init";

  let container: HTMLDivElement;
  let handle: EditorHandle | undefined;

  onMount(async () => {
    const { init } = await import("@maildeno/editor/init");

    handle = await init({
      container,
      capabilities: { export: ["html", "mjml", "json"] },
      onSave: (payload: { templateId: string | null }) => {
        console.log("Saved", payload);
        const target = handle?.getHtml(); // handle?.getReactEmail() or handle?.getMjml()
        console.log(target);
      },
      onSendTestEmail: async ({ to, subject, html }) => {
        await fetch("/api/send-test", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ to, subject, html }),
        });
      },
    });
  });

  onDestroy(() => handle?.destroy());
</script>

<div bind:this={container}/>

The dynamic import() inside onMount keeps @maildeno/editor/init — and its shadow-root/DOM usage — out of SvelteKit’s server-side module graph. In a client-only Svelte app (no SvelteKit) a static top-level import works fine too, since nothing evaluates it until onMount calls init().

SvelteKit route handler for test email

// src/routes/api/send-test/+server.ts
import { json } from "@sveltejs/kit";
import type { RequestHandler } from "./$types";
import { POSTMARK_SERVER_TOKEN } from "$env/static/private";

export const POST: RequestHandler = async ({ request }) => {
  const { to, subject, html } = await request.json();

  const res = await fetch("https://api.postmarkapp.com/email", {
    method: "POST",
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json",
      "X-Postmark-Server-Token": POSTMARK_SERVER_TOKEN,
    },
    body: JSON.stringify({
      From: "Your App <preview@yourapp.com>",
      To: to,
      Subject: subject,
      HtmlBody: html,
      MessageStream: "outbound",
    }),
  });

  if (!res.ok) {
    return json({ success: false, error: await res.text() }, { status: 502 });
  }

  const data = await res.json();
  return json({ success: true, messageId: data.MessageID });
};

$env/static/private keeps the token server-only and fails the build if it’s referenced from client code — a useful guard rail beyond just "don’t put it in `.env.PUBLIC_*`".

Where to go next