Custom rendering <code-island> element: Root element not found

Hi Webflow!

I am currently testing code components, and I really like it so far! I think Webflow has done a great job at implementing this.

However, I am running into the following issue. Please note that I am aware, that what I am about to describe is very advanced and was likely not considered when designing code components. Webflow if you are reading this, you should probably get a developer from the code components team to have a look at this. I want to highlight that I believe this is an issue in Webflow’s source code.

What I am trying to do: Inserting / rendering code components with client-side javascript.

Context: Webflow uses a custom element for rendering code-components into a shadow dom.

The problem: Upon inserting a element (see reproduction steps below), I am running into the error shown in the screenshot attached. It says that the root element was not found.

The cause:

  • See the constructor of the custom element (copied directly from Webflow’s source code at “webflow.schunk.cfec223be9662d34.js”):
constructor() {
  super(),
  this.shadowRoot || this.attachShadow({
    mode: "open"
  })
}
  • The following case is not handled properly: when this.shadowRoot is not present, this.attachShadow({ mode: "open" }) is not sufficient to initialize the code-island’s shadow root. Here’s an article documenting how it should be done: Declarative Shadow DOM  |  web.dev

Reproduction steps (I hope the code-components.website-files.com/*** build is still available):

  1. Go to sanavita-ag.webflow.io/tv/design
  2. Paste the following code into the browser console:
const codeIslandString = `<code-island data-loader="{&quot;tag&quot;:&quot;FEDERATION&quot;,&quot;val&quot;:{&quot;clientModuleUrl&quot;:&quot;https://code-components.website-files.com/6973d23f4b312fb0f9d6078d%2Fmodule%2Fwf-manifest.json&quot;,&quot;moduleId&quot;:&quot;_6973d23f4b312fb0f9d6078d&quot;,&quot;submoduleId&quot;:&quot;WeatherWidget&quot;,&quot;exportPath&quot;:&quot;default&quot;,&quot;serverModuleUrl&quot;:&quot;_&quot;}}" data-props="{&quot;variant&quot;:&quot;horizontal&quot;,&quot;visibility&quot;:true,&quot;days&quot;:4,&quot;showMinMaxTemp&quot;:true,&quot;weatherDelay&quot;:10,&quot;forecastDelay&quot;:60,&quot;prod&quot;:true}" data-slots="[]" data-hydrate="true" data-webflow-context="{&quot;mode&quot;:&quot;publish&quot;,&quot;interactive&quot;:true,&quot;locale&quot;:&quot;de-CH&quot;}" data-interactive="true" style="display:contents" data-wf--sanavita-components--weather-widget--style-variant="horizontal"><template shadowrootmode="open"><link rel="stylesheet" type="text/css" href="https://code-components.website-files.com/6973d23f4b312fb0f9d6078d%2Fmodule%2F4000ebd49df38fad663c.css"/><div data-root="true" style="display:contents"></div></template></code-island>`;

document.body.insertAdjacentHTML("afterBegin", codeIslandString);
  1. Find the inserted code-island (first element inside the body), open the shadow root
  2. Notice how the element is missing
  3. Check the following properties of the inserted code-island element and notice how they were not initialized:
  • rootElement (should be <div data-root="true style=“display:contents;”>)
  • root (should be the react root, can be created via codeIsland.renderer.mount(…))
  • props
  • slots
  • webflowContext

My current fix:

export async function initCodeIsland(island: HTMLElement): Promise<HTMLCodeIslandElement> {
  // Prevent re-initialization
  if (isCodeIsland(island)) return island;

  // Ensure island is an unparsed <code-island> element
  if (!isCodeIslandUnparsed(island)) {
    throw new TypeError(`Parameter island has to be of type HTMLCodeIslandElement.`);
  }

  // Wait for the constructor of <code-island> to be called
  await awaitCodeIslandUpgrade(island as HTMLCodeIslandElement);

  // Ensure the <code-island> construction was successful
  if (!isCodeIsland(island)) {
    throw new Error(`Something went wrong initializing the code-island.`);
  }

  // Here comes the actual fix
  const newRootElement = document.createElement("div");
  newRootElement.setAttribute("data-root", "true");
  newRootElement.style.display = "contents";

  island.shadowRoot.appendChild(newRootElement);
  island.rootElement = newRootElement;

  let newInternalRoot = island.renderer.mount(newRootElement);
  island.root = newInternalRoot;

  const dataset = island.parseDataset();
  island.props = dataset.props;
  island.slots = dataset.slots;
  island.webflowContext = dataset.webflowContext;

  return island;
}

See the missing helpers of this fix here: github.com/peakpointch/peakflow/blob/main/src/webflow/code-components.ts

All the best!
Lukas