diff --git a/editor/grida-canvas-react/viewport/surface.tsx b/editor/grida-canvas-react/viewport/surface.tsx
index efd80c416f..7436e63076 100644
--- a/editor/grida-canvas-react/viewport/surface.tsx
+++ b/editor/grida-canvas-react/viewport/surface.tsx
@@ -50,6 +50,7 @@ import { SnapGuide } from "./ui/snap";
import { Knob } from "./ui/knob";
import { cursors } from "../../components/cursor/cursor-data";
import { SurfaceTextEditor } from "./ui/surface-text-editor";
+import { TextSelectionOverlay } from "./ui/surface-text-selection-overlay";
import { SurfaceVectorEditor } from "./ui/surface-vector-editor";
import { SurfaceGradientEditor } from "./ui/surface-gradient-editor";
import { SurfaceImageEditor } from "./ui/surface-image-editor";
@@ -953,6 +954,18 @@ function SingleSelectionOverlay({
const { node, distribution, rotation, boundingSurfaceRect, size, object } =
data;
+ if (node.type === "tspan") {
+ return (
+
+
+
+
+
+
+
+ );
+ }
+
const padding =
node.type === "container" || node.type === "component"
? {
diff --git a/editor/grida-canvas-react/viewport/ui/surface-text-selection-overlay.tsx b/editor/grida-canvas-react/viewport/ui/surface-text-selection-overlay.tsx
new file mode 100644
index 0000000000..27230845b5
--- /dev/null
+++ b/editor/grida-canvas-react/viewport/ui/surface-text-selection-overlay.tsx
@@ -0,0 +1,31 @@
+"use client";
+
+import React from "react";
+import {
+ useBackendState,
+ useContentEditModeMinimalState,
+} from "@/grida-canvas-react/provider";
+
+/**
+ * Text-dedicated selection overlay (single selection only).
+ *
+ * Entry point for all text-specific selection overlay logic. When the selected
+ * node is a text node (tspan), SingleSelectionOverlay delegates here.
+ *
+ * Behavior:
+ * - **WASM + text edit mode:** Renders nothing. The outline is drawn by WASM
+ * via highlightStrokes; no DOM overlay avoids a stale rect as the user types.
+ * - **Otherwise:** Renders the frame (children, typically NodeOverlay).
+ */
+export function TextSelectionOverlay({
+ children,
+}: React.PropsWithChildren<{ node_id: string; readonly?: boolean }>) {
+ const backend = useBackendState();
+ const content_edit_mode = useContentEditModeMinimalState();
+
+ if (backend === "canvas" && content_edit_mode?.type === "text") {
+ return null;
+ }
+
+ return <>{children}>;
+}