diff --git a/client/src/components/Markdown/Sections/Elements/PluginContainer.vue b/client/src/components/Markdown/Sections/Elements/PluginContainer.vue
deleted file mode 100644
index 270e2bf09ef1..000000000000
--- a/client/src/components/Markdown/Sections/Elements/PluginContainer.vue
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-
-
-
-
-
diff --git a/client/src/components/Markdown/Sections/Elements/PluginWrapper.ts b/client/src/components/Markdown/Sections/Elements/PluginWrapper.ts
new file mode 100644
index 000000000000..e56404f0c457
--- /dev/null
+++ b/client/src/components/Markdown/Sections/Elements/PluginWrapper.ts
@@ -0,0 +1,61 @@
+import { getAppRoot } from "@/onload/loadConfig"
+
+class PluginWrapper extends HTMLElement {
+ constructor() {
+ super();
+ }
+
+ connectedCallback() {
+ const pluginName = this.getAttribute("plugin-name");
+ const pluginData = this.getAttribute("plugin-data") || "";
+
+ const pluginPath = `${getAppRoot()}static/plugins/visualizations/${pluginName}/static/dist/index`;
+ const pluginSrc = `${pluginPath}.js`;
+ const pluginCss = `${pluginPath}.css`;
+
+ if (!pluginSrc) {
+ console.error("Plugin source not provided!");
+ return;
+ }
+
+ // Create and append the iframe
+ const iframe = document.createElement("iframe");
+ iframe.style.border = "none";
+ iframe.style.width = "100%";
+ iframe.style.height = this.getAttribute("height") || "100%";
+
+ // Load content into the iframe once it's ready
+ iframe.onload = () => {
+ const iframeDocument = iframe.contentDocument;
+ if (!iframeDocument) {
+ console.error("Failed to access iframe document.");
+ return;
+ }
+
+ // Create the container for the plugin
+ const container = iframeDocument.createElement("div");
+ container.id = "app";
+ container.setAttribute("data-incoming", pluginData);
+ iframeDocument.body.appendChild(container);
+
+ // Inject the script tag for the plugin
+ const script = iframeDocument.createElement("script");
+ script.type = "module";
+ script.src = pluginSrc;
+ iframeDocument.body.appendChild(script);
+
+ // Add a CSS link to the iframe document
+ const link = iframeDocument.createElement('link');
+ link.rel = 'stylesheet';
+ link.href = pluginCss;
+ iframeDocument.head.appendChild(link);
+ };
+
+ this.appendChild(iframe);
+ }
+}
+
+// Register the custom element
+customElements.define("plugin-wrapper", PluginWrapper);
+
+export default PluginWrapper;
diff --git a/client/src/components/Markdown/Sections/MarkdownVitessce.vue b/client/src/components/Markdown/Sections/MarkdownVitessce.vue
index 09e723be0585..19c8fe0e88f0 100644
--- a/client/src/components/Markdown/Sections/MarkdownVitessce.vue
+++ b/client/src/components/Markdown/Sections/MarkdownVitessce.vue
@@ -1,19 +1,20 @@