10

Calendly provides this embed code that gets added to the page and displays calendar options to choose from.

<div class="calendly-inline-widget" data-url="https://calendly.com/username" style="min-width:320px;height:580px;"></div>
<script type="text/javascript" src="https://assets.calendly.com/assets/external/widget.js"></script>

I can't figure out the way to embed this code into the component. What's the best way to do so here?

import React, { Component} from "react";

class Calendly extends Component {
  ComponentDidMount( )

  render(){
    return (
      <div>
        <div id="schedule_form">

        </div>
      </div>
    );
  }
};

export default Calendly;
Max T
  • 1,365
  • 4
  • 23
  • 38

7 Answers7

20

You need to create a container DOM element that won't rerender, and you need to load the script after the DOM node exists.

This is a simple component (not tested) that renders the target div. In componentDidMount() it generates the script tag, and adds it to the head element of the page. You should clear the widget in componentWillUnmount(), so the component can remove itself when needed.

class Calendly extends React.Component {
  componentDidMount() {
    const head = document.querySelector('head');
    const script = document.createElement('script');
    script.setAttribute('src',  'https://assets.calendly.com/assets/external/widget.js');
    head.appendChild(script);
  }

  componentWillUnmount() {
    // whatever you need to cleanup the widgets code
  }

  render(){
    return (
      <div>
        <div id="schedule_form">
          <div 
            className="calendly-inline-widget"
            data-url="https://calendly.com/username"
            style={{ minWidth: '320px', height: '580px' }} />
        </div>
      </div>
    );
  }
}
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
  • Thanks for this. Could you possibly elaborate on why/how we need to clear the widget on `componentWillUnmount()`? – Jed Jun 06 '19 at 14:07
  • Because React is managing the DOM, and if you don't clear the widget, React might remove the containers parents, and you'll a detached node (memory leak). – Ori Drori Jun 06 '19 at 15:05
  • This was what I was missing `...DOM element that won't rerender,` I was setting state in my componentDidMount which was causing it not to appear – rotimi-best Jun 03 '20 at 17:30
  • What type of clean up code should we put in `componentWillUnmount()`? – Tomiwa Apr 14 '21 at 01:01
  • wow, absolutely horrible syntax... this is what React turned into? – Jim Dec 17 '21 at 16:39
  • How do we do the following? "clear the widget in componentWillUnmount(), so the component can remove itself when needed." – Tomiwa Sep 25 '22 at 13:09
11

Suggested, possibly simpler, alternative:

import React from 'react';

const Calendly = () => {
  return (
    <div style={{ height: "800px" }}>
      <iframe
        src="https://calendly.com/{USERNAME}/{OPTIONALEVENTNAME}"
        width="100%"
        height="100%"
        frameborder="0"
      ></iframe>
    </div>
  );
};

export default Calendly;

Adjust height as required (though I found 800px to be good). Ultimately, the script that they provide creates an iFrame anyway, might as well skip the entire step and load the iFrame directly. Plus you have better control over the styling (I found with other solutions that the embedded calendar would only be 150px height for some reason, even when the container was bigger and the iFrame is set to be 100%)

Hemal
  • 1,617
  • 2
  • 12
  • 21
11

This is how you do it with React hooks:

import React, { useEffect } from 'react';

const Calendly = ({ minWidth, height, url }) => {
  useEffect(() => {
    const head = document.querySelector('head');
    const script = document.createElement('script');
    script.setAttribute(
      'src',
      'https://assets.calendly.com/assets/external/widget.js'
    );
    head.appendChild(script);
  }, []);

  return (
    <div
      className="calendly-inline-widget"
      data-url={url}
      style={{ minWidth, height }}
    />
  );
};

Yoandry Collazo
  • 1,122
  • 9
  • 16
Eric Wallen
  • 651
  • 7
  • 7
  • Only thing that's left to be desired is an event trigger when `widget.js` has been fully loaded as I'm looking to display a loader component while waiting on 3rd party script. This other [S.O. answer](https://stackoverflow.com/a/57572107/1731759) looks like a good solution for this, although I haven't tested personally. – GFargo Apr 17 '20 at 15:43
  • Nop, the script.onload is not working, I think the time to spare is not before the script is loaded, is once the script is loaded and the widget is loaded too. – Yoandry Collazo Jun 24 '20 at 00:00
  • In the earlier answer they mention something about "You should clear the widget in componentWillUnmount(), so the component can remove itself when needed." How do we do that? – Tomiwa Sep 25 '22 at 13:09
  • @Tomiwa when you are using React hooks you do not use the React lifecycle methods such as componentWillUnmount(). – Eric Wallen Nov 07 '22 at 17:51
3

You can use this package react-calendly and just pass in the calendly url

import { InlineWidget } from 'react-calendly';

const App = () => {
return <InlineWidget url="yourcalendlyurl"/>
}

export default App

and you're good to go.

Albert Dugba
  • 85
  • 1
  • 9
0

Combining @EricWallen's answer using React Hooks and Ori Dori's suggestion of cleanup in componentWillUnmount.

This is particularly useful if you need to navigate to different components and your calendar is only visible on the first render. Adding this snippet fixes this.

import React, { useEffect } from 'react';

const calendlyWidgetScript = 'calendlyWidgetScript';
const calendarDivId = 'calendarForm';

const Calendly = ({ minWidth, height, url }) => {
  useEffect(() => {
    const head = document.querySelector('head');
    const script = document.createElement('script');
    script.setAttribute('id',  calendlyWidgetScript);
    script.setAttribute(
      'src',
      'https://assets.calendly.com/assets/external/widget.js'
    );
    head.appendChild(script);

    return () => {
      if (document.getElementById(calendlyWidgetScript) && head) {
        const script = document.getElementById(calendlyWidgetScript);
        script?.remove();
        const calendar = document.getElementById(calendarDivId);
        calendar?.remove();
    }
    }

  }, []);

  return (
  <div id={calendarDivId}>

    <div
      className="calendly-inline-widget"
      data-url={url}
      style={{ minWidth, height }}
    />
  </div>
  );
};
Tomiwa
  • 840
  • 12
  • 14
0

Working Vue 3 example for those looking for it:

<template>
    <div>
        <div class="calendly-inline-widget" data-url="YOUR_CALENDLY_URL_HERE" style="min-width: 320px; height: 630px"></div>
    </div>
</template>

<script setup lang="ts">
    onMounted(() => {
        const head = document.querySelector('head');
        const script = document.createElement('script');
        script.setAttribute('src', 'https://assets.calendly.com/assets/external/widget.js');
        head!.appendChild(script);
    });
</script>

<style scoped></style>
Germain
  • 658
  • 7
  • 19
-2

Stick the script file itself before the </body> tag in the index.html

import React, { Component} from "react";

class Calendly extends Component {
  ComponentDidMount( )

  render(){
    return (
      <div>
        <div id="schedule_form">
          <div class="calendly-inline-widget" data-url="https://calendly.com/username" style="min-width:320px;height:580px;"></div>
        </div>
      </div>
    );
  }
};

export default Calendly;
Dadsquatch
  • 566
  • 5
  • 16
  • But you're missing the part that loads a script from Calendly (https://assets.calendly.com/assets/external/widget.js) that actually creates an iframe inside `.calendly-inline-widget` – Dmitry Pashkevich Feb 05 '20 at 21:40
  • Nope. Re-read. I mention paste it in your main html file where you would put other scripts like bootstrap or custom css files. It will load site-wide that way (which has it's own downfalls). – Dadsquatch Feb 06 '20 at 00:07
  • I see. It may work in some scenarios, but not guaranteed to always work, because with such setup you can't guarantee that `
    ` will always render first, and widget.js will render after that (imagine the Calendly component only renders when a user clicks something in your app).
    – Dmitry Pashkevich Feb 06 '20 at 02:29