Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

81
Views
Rendering a Vue app in Shadow DOM using Custom Element

So I have a full featured (Routing etc) Vue 3 app that I need to embed on random page somewhere, but I don't want to use an iframe for various reasons so I'm looking to achieve this using Shadow DOM and Custom Element.

The way I thought it would work:

  • ✅ Build the Vue App
  • ✅ Publish the dist folder
  • ✅ Create a Custom Element
  • ✅ Create Shadow DOM in the constructor
  • ✅ Load dist/index.html
  • ❌ Vue App renders in Shadow DOM

... except it doesn't. Everything technically works but the Vue app doesn't render at all.

enter image description here

What am I missing?

My attempt: https://stackblitz.com/edit/javascript-7trm5i?devtoolsheight=33&file=index.html

Edited to add:

  • Same result even if I deploy the app to a service like Vercel and use it as src.
about 4 years ago · Juan Pablo Isaza
2 answers
Answer question

0

This is not a right way to do it. You are using fetch to get the HTML page. The HTML page has following script and style tags:

<!DOCTYPE html>
<html lang="en" data-theme="bumblebee">

<head>
  <meta charset="UTF-8" />
  <link rel="icon" href="/favicon.ico" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Vue Cart</title>
  <script type="module" crossorigin src="/assets/index.0f5a3d92.js"></script>
  <link rel="modulepreload" href="/assets/vendor.ab0eb239.js">
  <link rel="stylesheet" href="/assets/index.8fd7686d.css">
</head>

<body>
<div id="app"></div>

</body>
</html>

Look at this script: <script type="module" crossorigin src="/assets/index.0f5a3d92.js">.

When this tag is added via innerHTML, the browser will try to find the index.0f5a3d92.js file using the domain from which you are serving the main page. It is not asking the domain from where you are getting this HTML. Ideally, it should be this:

https://vue-cart-ayoc0w7nv-eozzy.vercel.app/assets/index.0f5a3d92.js

But it is making call to this:

https://stackblitz.com/assets/index.0f5a3d92.js

You will have to rewrite the src attribute before you add it to HTML.

On a side note, this is an anti-pattern on what you are trying to do. Web component is not a real substitute for iFrame. Plus, a page should have only one top level head tag. You are introducing multiple head and body tags here. Browser will silently handle it but it is still anti-pattern.

The right way to do this is to bundle your Vue application with custom element definition. Then upload this bundled script to some CDN. From there, add it as a script on the page and the element should render when browser encounters the custom element.

about 4 years ago · Juan Pablo Isaza Report

0

You can not have multiple html, body, head tags in single document, so the html parser ignores the html, head and body tags of the Vue app.

Before:

<!DOCTYPE html>
<html lang="en" data-theme="bumblebee">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue Cart</title>
    <script type="module" crossorigin src="https://vue-cart-ayoc0w7nv-eozzy.vercel.app/assets/index.0f5a3d92.js"></script>
    <link rel="modulepreload" href="https://vue-cart-ayoc0w7nv-eozzy.vercel.app/assets/vendor.ab0eb239.js">
    <link rel="stylesheet" href="https://vue-cart-ayoc0w7nv-eozzy.vercel.app/assets/index.8fd7686d.css">
  </head>
  <body>
    <div id="app"></div>
    
  </body>
</html>

After parsing:

    <meta charset="UTF-8" />
    <link rel="icon" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue Cart</title>
    <script type="module" crossorigin src="https://vue-cart-ayoc0w7nv-eozzy.vercel.app/assets/index.0f5a3d92.js"></script>
    <link rel="modulepreload" href="https://vue-cart-ayoc0w7nv-eozzy.vercel.app/assets/vendor.ab0eb239.js">
    <link rel="stylesheet" href="https://vue-cart-ayoc0w7nv-eozzy.vercel.app/assets/index.8fd7686d.css">
    <div id="app"></div>

So ultimately the html structure changes from the original one which Vue Virtual DOM can not work with.

So I don't think you can use Shadow DOM like an iframe.

about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!