How to fetch HTML and CSS files for web components when hosted on a different domain than the application itself?

Viewed 713

I am looking for a clean solution to split web components into JS, HTML and CSS files and host them on a CDN. I try to avoid the webpack html and css-loader as they dont allow me to export my web component as a plain ES module.

The goal is to use a web component from any frontend app just by importing it from a spcified URL. Thereby seperation of concerns should be preserved. Individual files for style, markup and logic also allow for syntax highlighting.

In a local dev environment I found the following to work great:

WebComponent.js:

export default class WebComponent extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: "open" });

    const style = new CSSStyleSheet();
    const template = document.createElement("template");

    fetch("./WebComponent.css").then((res) =>
      res.text().then((css) => {
        style.replaceSync(css);
        this.shadowRoot.adoptedStyleSheets = [style];
      })
    );

    fetch("./WebComponent.html").then((res) =>
      res.text().then((html) => {
        template.innerHTML = html;
        this.shadowRoot.appendChild(template.content.cloneNode(true));
      })
    );
  }
}

WebComponent.css:

button {
    /* Some styling */
}

WebComponent.html:

<button>Custom buttom</button>

I can import the component by using browser native ES module imports:

index.html:

<!DOCTYPE html>
<html>
  <body>
    <web-component></web-component>

    <script type="module">
      import WebComponent from "./WebComponent";
      customElements.define("web-component", WebComponent);
    </script>
  </body>
</html>

This works until I move the web component files to a different location (a google cloud storage bucket) than my index.html and import WebComponent.js from there.

<!DOCTYPE html>
<html>
  <body>
    <web-component></web-component>

    <script type="module">
      import WebComponent from "https://storage.googleapis.com/storage-bucket/WebComponent.js";
      customElements.define("web-component", WebComponent);
    </script>
  </body>
</html>

WebComponent.js gets imported correctly but it then tries to fetch WebComponent.css and WebComponent.html from a URL relative to localhost where index.html is served. However it should fetch from a URL relative to where it is hosted (https://storage.googleapis.com/storage-bucket/).

Any ideas how something like that can be achieved? Without hard coding the url into both fetch calls. That's not an option as the url can change automatically from time to time.

2 Answers

You are having issue with linking resources in the JS web page for which :

  • local component is working
    • import WebComponent from "./WebComponent";
  • remote component is failing
    • import WebComponent from "URL";

It might be that for this to work you should try this :

<script type="module" src="https://storage.googleapis.com/storage-bucket/WebComponent.js">
      customElements.define("web-component", WebComponent);
</script>

References :

JavaScript file paths are relative to the displayed page. So the behavior you are observing is expected.

You can use a JavaScript variable with a simple js declaration like below and use this variable across whenever you assign URLs dynamically:

<script type="text/javascript">
   var webComponentPath = 'https://storage.googleapis.com/storage-bucket/';
</script>
Related