cal.com icon indicating copy to clipboard operation
cal.com copied to clipboard

[CAL-3893] Error after build: iframe doesn't exist. `createIframe` must be called before `doInIframe`

Open fabriziospadaro opened this issue 1 year ago • 8 comments

Bug Report: iframe doesn't exist. createIframe must be called before doInIframe

Issue Summary

When embedding an iframe in a Next.js page, an error occurs after building the page: iframe doesn't exist. createIframe must be called before doInIframe. This issue does not occur during development (npm run dev). Note that the iframe embed still works, and all the functionalities are preserved.

Steps to Reproduce

  1. Create a Next.js page and embed an iframe using @calcom/embed-react.
  2. Use the following code snippet in your component:
    "use client"
    
    import Cal, { getCalApi } from "@calcom/embed-react";
    import { useEffect } from "react";
    
    export default function CalWidget({ calLink }) {
      useEffect(() => {
        (async function () {
          const cal = await getCalApi();
          cal("ui", {
            "theme": "light",
            "styles": { "branding": { "brandColor": "#2B5ADC" } },
            "hideEventTypeDetails": false,
            "layout": "month_view",
          });
        })();
      }, [])
    
      return (
        <Cal
          id="book"
          onScrollCapture={(e) => {
            e.stopPropagation();
          }}
          calLink={calLink}
        />
      )
    }
    
  3. Run the development server using npm run dev and verify that the page works as expected.
  4. Build the project using npm run build and then start the server using npm start.
  5. Navigate to the page and observe the error message.

Actual Results

  • After building the page and starting the server, the error message iframe doesn't exist. createIframe must be called before doInIframe is displayed.
  • The iframe fails to load correctly.

Expected Results

  • The iframe should load without any errors, similar to the behavior during development (npm run dev).
  • The embedded content should be displayed as expected.

CAL-3893

fabriziospadaro avatar Jun 06 '24 15:06 fabriziospadaro

Hey @fabriziospadaro! I'm here to help you out with any bugs, questions, or contributions you have. Let's tackle this together!

To resolve the error iframe doesn't exist. createIframe must be called before doInIframe, you need to ensure that the createIframe method is called before the doInIframe method. This error occurs because the doInIframe method requires the iframe to be created and ready before it can execute its instructions.

Here is a simplified example of how you can ensure that createIframe is called before doInIframe:

// Ensure that createIframe is called first
const cal = new Cal('your-namespace', []);
cal.createIframe({
  calLink: 'your-cal-link',
  queryObject: {},
  calOrigin: 'your-cal-origin'
});

// Now you can safely call doInIframe
cal.doInIframe({
  method: 'yourMethod',
  arg: 'yourArgument'
});

In the context of using @calcom/embed-react, make sure that the iframe is created during the component's lifecycle before any operations that require the iframe are performed. For example, you can use the useEffect hook in a React component to ensure the iframe is created when the component mounts:

import React, { useEffect } from 'react';
import { Cal } from '@calcom/embed-react';

const YourComponent = () => {
  useEffect(() => {
    const cal = new Cal('your-namespace', []);
    cal.createIframe({
      calLink: 'your-cal-link',
      queryObject: {},
      calOrigin: 'your-cal-origin'
    });

    // Now you can safely call doInIframe
    cal.doInIframe({
      method: 'yourMethod',
      arg: 'yourArgument'
    });
  }, []);

  return (
    <div>
      {/* Your component JSX */}
    </div>
  );
};

export default YourComponent;

Additionally, ensure that the iframe is properly initialized and set up. According to the embed-iframe-init.ts file from Cal.com, the EmbedInitIframe function is responsible for setting up the iframe and ensuring it is ready for interaction. This function checks for the namespace in the query parameters or window.name, sets the isEmbed flag, and applies CSS variables for the theme. This initialization is crucial for the iframe's readiness.

By ensuring that createIframe is called before doInIframe and that the iframe is properly initialized using the EmbedInitIframe function, you can avoid the error and ensure that the iframe is properly set up before any operations are performed on it [1][2][3].

To continue talking to Dosu, mention @dosu.

dosubot[bot] avatar Jun 06 '24 15:06 dosubot[bot]

I'm facing the same issue and I'm not able to solve. any help here?

alisayed95 avatar Jun 13 '24 10:06 alisayed95

@dosu the proposed solution is not working

alisayed95 avatar Jun 13 '24 10:06 alisayed95

@dosu @dosubot you left code that is irrelevant to the example provided. the snippet we are given as developers is as the OP mentioned:

  useEffect(() => {
    (async function () {
      const cal = await getCalApi();
      cal("ui", {
        "theme": "light",
        "styles": { "branding": { "brandColor": "#2B5ADC" } },
        "hideEventTypeDetails": false,
        "layout": "month_view",
      });
    })();
  }, [])

there are no createIframe or doInIframe methods being used or provided in the snippet that cal.com gives us.

wallawe avatar Jul 14 '24 22:07 wallawe

Any update for this? My sentry is going crazy 😮‍💨

fabriziospadaro avatar Oct 16 '24 10:10 fabriziospadaro

To address the issue with the error message "iframe doesn't exist. createIframe must be called before doInIframe" in the context of using the @calcom/embed-react package, it's important to ensure that the iframe is fully initialized before any operations are performed on it. The getCalApi function is designed to handle this initialization process.

Here's a refined approach using the getCalApi function:

  1. Ensure Initialization: The getCalApi function should be called to initialize the Cal API, which will handle the iframe setup. Make sure this is done before any UI operations.

  2. Check for Readiness: Use the event-driven approach to ensure the iframe is ready. The package listens for the __iframeReady event to confirm the iframe is ready for operations.

Here's how you can adjust your code:

import Cal, { getCalApi } from "@calcom/embed-react";
import { useEffect } from "react";

export default function CalWidget({ calLink }) {
  useEffect(() => {
    (async function () {
      const cal = await getCalApi();
      // Ensure the iframe is ready before performing UI operations
      cal.on("__iframeReady", () => {
        cal("ui", {
          "theme": "light",
          "styles": { "branding": { "brandColor": "#2B5ADC" } },
          "hideEventTypeDetails": false,
          "layout": "month_view",
        });
      });
    })();
  }, []);

  return (
    <Cal
      id="book"
      onScrollCapture={(e) => {
        e.stopPropagation();
      }}
      calLink={calLink}
    />
  );
}

This approach ensures that the UI operations are only executed once the iframe is fully initialized and ready, preventing the error from occurring.

To continue talking to Dosu, mention @dosu.

dosubot[bot] avatar Oct 16 '24 10:10 dosubot[bot]

@dosu The correct api to use should be:

cal("on", {
        action: "__iframeReady",
        callback: (e) => {
          cal("ui",
            {
              "theme": "light",
              "styles": {
                "branding": { "brandColor": "#2B5ADC" },
              },
              "layout": "month_view",
            });
        }
      });

but this still doesn't fix the issue.

fabriziospadaro avatar Oct 16 '24 10:10 fabriziospadaro

To address the issue with the error message "iframe doesn't exist. createIframe must be called before doInIframe" when using the @calcom/embed-react package, it's crucial to ensure that the iframe is fully initialized before performing any operations on it. The __iframeReady event is key to confirming the iframe's readiness.

Here's how you can adjust your code to ensure the iframe is ready before executing UI operations:

import Cal, { getCalApi } from "@calcom/embed-react";
import { useEffect } from "react";

export default function CalWidget({ calLink }) {
  useEffect(() => {
    (async function () {
      const cal = await getCalApi();
      // Use the correct API to listen for the iframe readiness
      cal("on", {
        action: "__iframeReady",
        callback: () => {
          cal("ui", {
            "theme": "light",
            "styles": {
              "branding": { "brandColor": "#2B5ADC" },
            },
            "layout": "month_view",
          });
        }
      });
    })();
  }, []);

  return (
    <Cal
      id="book"
      onScrollCapture={(e) => {
        e.stopPropagation();
      }}
      calLink={calLink}
    />
  );
}

This setup ensures that the UI configuration is applied only after the __iframeReady event is triggered, indicating that the iframe is fully initialized and ready for interaction. If the issue persists, double-check that the getCalApi function is correctly initializing the API and that the event listener is properly set up to handle the __iframeReady event.

To continue talking to Dosu, mention @dosu.

dosubot[bot] avatar Oct 16 '24 10:10 dosubot[bot]

I was getting this error both in dev and the built server but with all functionality working. Looking at the iframe after rendering, I see the the following:

<iframe class="cal-embed" name="cal-embed=" .../>

with the name attribute looking like there's something missing on the name=.

Not even sure this is the relevant code but it looks like it should be set to namespace.

So I added the namespace parameter to my code and the error went away:

useEffect(() => {
    (async function () {
      const cal = await getCalApi({ namespace: "nynamespace" });
      cal("ui", {
        theme: "light",
        styles: {
          branding: { brandColor: "white" },
        },
      });
    })();
  }, []);

Buuuuuuut the iframe still has name="cal-embed=".

@dosu what do you think?

hershaw avatar Nov 06 '24 07:11 hershaw

Correct, adding the namespace in the initialization, and adding it also to the jsx element fixed the error!

fabriziospadaro avatar Jan 06 '25 09:01 fabriziospadaro

The issue you're encountering with the name attribute of the iframe being set to cal-embed= suggests that the namespace parameter might not be properly applied. The namespace parameter in the getCalApi function is intended to uniquely identify instances of the Cal class, and it should affect the iframe initialization by setting the name attribute to include the namespace, formatted as cal-embed=<namespace> [1].

Here are a few things to check:

  1. Ensure Correct Usage: Make sure that the namespace parameter is correctly passed to the getCalApi function. It seems like you've done this, but double-check that the parameter is being used as expected.

  2. Inspect the Implementation: Verify that the getCalApi function and the createIframe method are correctly using the namespace parameter. The createIframe method should set the name attribute of the iframe using the namespace.

  3. Debugging: Add some console logs or breakpoints in the createIframe method to ensure that the namespace is being correctly passed and used when setting the name attribute.

If the namespace is correctly passed and used, the name attribute should reflect this as cal-embed=nynamespace. If the issue persists, there might be a bug in the implementation that needs further investigation.

To continue talking to Dosu, mention @dosu.


Help Dosu learn! Give it feedback: Great Response | Irrelevant Answer | Incorrect Sources | Too Verbose | Hallucination | Other

dosubot[bot] avatar Jan 06 '25 09:01 dosubot[bot]