Integrate Miro and CometChat in a Daily video call with our new Prebuilt Integrations API

In our last post, Jon introduced you to our brand new Prebuilt Integrations API. Daily Prebuilt is our full-featured video call component that lets you embed a Daily-powered video call into your app with just a few lines of code.

In this post, I’ll go through the core functionality of the API. I’ll do this by presenting two practical examples of integrating third-party services into a Daily Prebuilt video call:

  1. A Miro virtual whiteboard. Miro is an online platform that allows participants to learn or collaborate within the context of virtual whiteboards. It’s often used for lectures and teaching, or for planning and brainstorming together,
  2. A CometChat chat widget. CometChat is a platform that allows you to easily embed advanced chat functionality into your web app.

We’ll do this with an extremely basic demo—no bells and whistles or complexity, just Daily's new Prebuilt Integrations API!

GIF of opening the Miro integration and typing in the CometChat integration

Running the demo

If you want to see the code and demo right away, check it out on Glitch.

You can either remix the Glitch project above or join your own Daily room with the deployed version of the app. To do so, follow the steps below:

  • Create a Daily account and a Daily room
  • Copy the room URL of your Daily room (it will be https://YOUR_DOMAIN.daily.co/YOUR_ROOM_NAME)
  • Navigate to the deployed demo with your room URL as a query parameter: https://daily-prebuilt-integrations.glitch.me/?roomURL=YOUR_ROOM_URL

Once you’ve done that, you should see a Daily Prebuilt call frame load in. Upon joining the call, you’ll see an “Apps” button in the Prebuilt tray menu at the bottom. This is where the Miro button lives. Click on it and you’ll see a Miro board load into the main call view.

Animation showing opening the Miro integration in Daily Prebuilt's main call view

To open up the CometChat integration, click on “People” in the bottom tray and then click “CometChat” at the top. Or, alternatively, click the “Chat” icon in the Prebuilt tray:

Animation showing opening up the CometChat sidebar view integration in Daily Prebuilt

In this demo, I am showing the CometChat integration with an example chat with Iron Man. If you’d like to use this in production with real cross-participant chat, sign up for CometChat and create your own chat widget.

💡
CometChat requires access to local storage, which is sometimes blocked by default in Incognito mode! I suggest running this demo in a non-Incognito window, or enabling local storage in your Incognito browser settings.

Now that we’ve familiarized ourselves with the demo, let’s dig into the Miro “main view” integration.

Miro + Daily Prebuilt with a “main view” integration

In this integration, you’re going to see an “Apps” menu in your embedded Daily Prebuilt iframe. When clicked, a “Miro” button appears:

Miro button in Daily Prebuilt

If you click to activate Miro, the main call view (where the active speaker would normally be shown) will be replaced with a Miro board. I’ve provided a default read-only board, or you can replace the URL with your own Miro embed URL.

If you join the same room from another instance of the demo, you’ll see that Miro is opened for other participants as well, without them clicking the Miro control in the tray menu to toggle it.

We’re going to accomplish all of the above in under 15 lines of code with Daily’s new customIntegrations call frame property.

You can check out the code for the demo above on Glitch and play around with all the properties, but for the sake of brevity here’s a condensed version:

  const callFrame = window.DailyIframe.createFrame({
    customIntegrations: {
      miro: {
        controlledBy: "*",
        iconURL:
          "https://cdn.glitch.global/fe2fb078-9d31-4e90-8e78-c9cda05cd705/miroIcon.png?v=1680080952367",
        label: "Miro",
        location: "main",
        shared: true,
        src: "https://miro.com/app/live-embed/uXjVPrAq36w=/?moveToViewport=-2043,-1039,4081,2023&embedId=660028222598",
      },
    },
    // Additional code unrelated to the Miro integration below, we’ll go through this in the subsequent CometChat section...
  });

Above, I’m adding a new customIntegrations property into the object passed into our createFrame() factory function. The property is an object containing integration configurations—in this case, just Miro. I decided to call the Miro integration object miro, but you could make this anything.

Within the miro object, I define some Integration-specific configuration. Here’s what each one means (refer to our documentation for a complete list of properties):

  • controlledBy tells Daily who can start and stop this integration. In this case, ”*" means that it can be controlled by any user.
  • iconURL specifies which image to show next to the associated Miro button Prebuilt will display in the call frame.
  • label is the text that will appear next to the icon as part of the Miro button in the call frame.
  • locationspecifies that this integration, when activated, will be displayed in the main call view of the application (where the active speaker would otherwise be shown)
  • shared dictates whether the state of this information will be shared with other participants who have it configured. In this case, the state is shared. In practice, this means when a local participant opens or closes the Miro integration view, it will also be opened or closed for other participants who joined through our demo app.
  • src specifies the embed URL of my default, read-only Miro board.

There are other possible options you can set for an integration which I haven’t used here. Be sure to check out our reference docs for a comprehensive list of integration properties.

That’s it! Daily handles the rest, so this is all the code we need to be able to open a Miro board directly through the Daily Prebuilt call frame.

Now, let’s take a look at a slightly different subset of Prebuilt Integrations API features: configuring a sidebar chat integration after the user joins a Daily call.

CometChat + Daily Prebuilt with a “sidebar” integration

Some integrations are straightforward enough to be configured pre-call-join, like Miro. But what if you have an integration that you want to enable after the user is in a Daily room, or during some other part of the call life cycle? I’ll show an example of this with the CometChat sidebar integration.

For the CometChat integration, I’ll be using their client-side library to embed a chat widget into a separate HTML page. I’ll then be embedding that HTML page as a Prebuilt integration. Daily’s session_id for each participant will be used to create their CometChat user ID, so we’ll need to have that available before the integration can be enabled. The local user can activate the integration through either their “People” tray button or a custom button in their Daily Prebuilt call frame. The result looks like this:

Daily Prebuilt with a call participant in the main view and CometChat in the sidebar

Check out comet-chat.html to see how I embed the CometChat widget into an HTML page in the project. I won’t go through that in detail now since that’s not actually Integrations related. The critical point is, we’ll be embedding this page into Daily Prebuilt as a sidebar integration. That’s what I’ll focus on here.

Since we need to have the Daily session ID for the local participant to pass to CometChat, we cannot actually configure this integration until after the user joins the call. This is why in my Integrations API demo, I instead configure the integration using our new setCustomIntegrations() call frame instance method after receiving Daily’s "joined-meeting" event:

  callFrame.on("joined-meeting", async (e) => {
    const localParticipant = e.participants.local;
    enableCometChatIntegration(callFrame, localParticipant.session_id);
  });

That enableCometChatIntegration() function is where the magic happens, so let’s take a look:

function enableCometChatIntegration(callFrame, sessionID) {
  const integrations = callFrame.customIntegrations();
  integrations.cometChat = getCometChatIntegration(sessionID);
  callFrame.setCustomIntegrations(integrations);
  setupCometChatButton(callFrame);
}

The function above takes two parameters: the Daily Prebuilt call frame and the local participant’s session ID (whose session ID we’ll be using to register a CometChat user).

The first thing I do in the function body above is retrieve all existing integrations with the new customIntegrations() call frame instance method. In this case, this will be the Miro integration we set up earlier!

setCustomIntegrations() overrides any existing integrations with the new given object. By retrieving the existing integrations and adding the new CometChat integration to that object, we ensure that we keep all integrations intact instead of overwriting anything.

Finally, I call the setCustomIntegrations() call frame instance method with the modified integrations object, which results in the new CometChat integration being added.

I then also set up a custom button for some additional control, which I’ll go through shortly. But let’s go through the integration configuration itself first.

The CometChat integration object is very similar to the Miro one we set up above, with a couple of key differences:

function getCometChatIntegration(sessionID) {
  const location = window.location;
  const baseURL = `${location.protocol}//${location.host}${location.pathname}`;
  const src = `${baseURL}comet-chat.html?userID=${sessionID}`;
  return {
    controlledBy: "*",
    label: "CometChat",
    location: "sidebar",
    shared: false,
    src: src,
  };
}

The first difference is that instead of a third-party embed URL like a Miro board, I specify the integration src property to be an absolute path to the comet-chat.html file served on the demo domain. I also include a userID query parameter in the URL and use it to pass the participant’s session ID to the CometChat logic.

The second key difference is that I set the location property to ”sidebar". This will result in the integration to be accessible through the Daily Prebuilt sidebar instead of the main call view.

Finally, we also set the shared property to false here–this means any user can toggle the CometChat integration locally without having that state propagated to each other user.

Once enabled, the code above results in a “CometChat” button being added to the Daily Prebuilt “People” tab. When clicked, it opens up a CometChat widget in the sidebar view:

CometChat tab in the Daily Prebuilt sidebar

But for something like chat functionality, having the control to open it being tucked away in the sidebar doesn’t really seem like enough. What if we also want to add a big “Chat” button to the Prebuilt tray and have it open the CometChat sidebar integration when clicked? We can do that, too!

Manipulating the sidebar view with a custom button

That’s where the setupCometChatButton() function I mentioned earlier comes in. By using Daily Prebuilt’s custom tray button feature in conjunction with the new setSidebarView() call frame instance method, we can add a Chat button to the tray and have it toggle our new sidebar CometChat integration when clicked:

// setupCometChatButton() sets up a custom tray button to toggle
// the CometChat integration
function setupCometChatButton(callFrame) {
  const viewAndBtnID = "cometChat";
  callFrame.on("custom-button-click", (ev) => {
    const buttonID = ev.button_id;
    if (buttonID !== viewAndBtnID) return;
    callFrame.getSidebarView().then((view) => {
      if (view !== viewAndBtnID) {
        callFrame.setSidebarView(viewAndBtnID);
        return;
      }
      callFrame.setSidebarView(null);
    });
  });

  // Set up CometChat custom tray button
  callFrame.updateCustomTrayButtons({
    cometChat: {
      iconPath:
        "https://cdn.glitch.global/fe2fb078-9d31-4e90-8e78-c9cda05cd705/cometChatIcon.png?v=1680166335558",
      label: "Chat",
      tooltip: "Advanced chat with CometChat",
    },
  });
}

Alright, let’s take this step by step. Above, I first create a handler for when our upcoming custom button will be clicked, and then add the custom button to the tray. I chose this order to avoid timing issues: I want to make sure that by the time the custom button is added to the tray, the handler is already set up and guaranteed to run.

The handler above uses Daily’s "custom-button-click" event. This event will fire whenever the local user clicks a custom button in the tray.

Inside the handler, I check the ID of the button being clicked (which will be ”cometChat” as you can see by the viewAndBtnID const defined above).

After verifying that the custom button that was clicked is indeed our CometChat button, I use the getSidebarView() call frame instance method to retrieve the current state of the sidebar.  If the sidebar is not already displaying the CometChat integration, I use setSidebarView() to activate it. Otherwise, I use setSidebarView() to close the integration.

After defining the handler for our upcoming button click above, I use Daily’s updateCustomTrayButtons() call frame instance method to configure a cometChat button with my preferred icon path and labels.

And that’s it! We now have a new button in the Daily Prebuilt tray which toggles our new CometChat integration on and off:

CometChat button in the Daily Prebuilt bottom tray menu

Conclusion

I hope this post has served to give you an idea of what’s possible with our new Prebuilt Integrations API.

As you can see, the API is very flexible in how and when you enable your integrations. It opens up a world of possibilities to easily embed not just third-party apps directly into Daily Prebuilt, but also your own custom functionality. Watch out for more content around using the Prebuilt Integrations API in conjunction with other Daily features to build unique experiences within your Daily-powered video applications.

If you have any questions or feedback on the Prebuilt Integrations API, please reach out to our support team or find us at peerConnection, our WebRTC community.

Never miss a story

Get the latest direct to your inbox.