Load the Calendly scheduler only after visitors accept functionality cookies

Tag the Calendly widget.js script with the functionality category, and put a fallback link where the scheduler appears. Calendly draws its inline scheduler from that script. By default, TermsFeed Free Cookie Consent holds the script until the visitor accepts functionality, so the scheduler loads nothing from Calendly before the answer.

A booking calendar is a service the visitor uses on purpose, so it belongs in functionality. Visitors who decline still need a way to book. Give them a plain link to your Calendly page in the scheduler area, because the link sends nothing to Calendly until the visitor clicks it.

The fallback link sits inside the scheduler container, and a short script tagged with functionality removes it before the scheduler loads. Visitors who decline keep the link. Visitors who accept see the scheduler in its place, on the same page and with no reload.

The fallback holds a button with the class open-cookie-preferences. Add .open-cookie-preferences to the open_preferences_center_selector list already in your run() call, as in "#open_preferences_center, .open-cookie-preferences", so the button opens the Preferences Center. Set that option up with Block Embeds and Iframes Until Consent.

Tag the Calendly inline embed

Keep the calendly-inline-widget element, put the fallback link inside it, and tag the Calendly widget.js script. Add hide_gdpr_banner=1 to the Calendly URL, so Calendly does not show its own consent banner inside the scheduler next to yours.

Calendly says that it "uses cookies even if the banner is hidden", according to the Calendly article on cookie management. The same article asks sites to "wait to load Calendly until users accept performance cookies". Cookie Consent has no category of that name, and its "Tracking cookies" tab describes tracking and performance cookies. A site that follows the Calendly wording to the letter writes tracking in place of functionality in the embed code.

Replace YOUR_LINK in this tagged inline embed with your scheduling link, in both places. The embed holds the fallback removal script above the tagged widget.js script.

<div class="calendly-inline-widget" data-url="https://calendly.com/YOUR_LINK?hide_gdpr_banner=1" style="min-width:320px;height:700px;">
  <p class="cc-fallback" data-cc-category="functionality">Book a time on <a href="https://calendly.com/YOUR_LINK">our Calendly page</a>, or accept functionality cookies to book here. <button type="button" class="open-cookie-preferences">Change my cookie preferences</button></p>
</div>
<script type="text/plain" data-cookie-consent="functionality">
  (function () {
    var fallbacks = document.querySelectorAll('.cc-fallback[data-cc-category="functionality"]');
    for (var i = 0; i < fallbacks.length; i++) {
      fallbacks[i].parentNode.removeChild(fallbacks[i]);
    }
  })();
</script>
<script type="text/plain" data-cookie-consent="functionality" src="https://assets.calendly.com/assets/external/widget.js" async></script>

Cookie Consent runs the tagged scripts of one category in page order, and the inline removal script runs at once, so the link is gone before the Calendly script arrives. Keep one copy of the removal script and of widget.js on a page with several schedulers.

The Calendly script draws every inline scheduler on the page when it runs, even when it arrives after the page has loaded. Keep async on the tagged script, so it does not hold any other functionality script of the page while it loads.

The checks assume express consent with page_load_consent_levels left out or at the generator value ["strictly-necessary"], and the "I decline" button shown. Publish the change and clear any page cache before the check. Run the check in Chrome. Checking one page takes about 3 minutes.

  1. Close every private window and open a new one, which does not hold an earlier answer. Open DevTools and select the Network tab.
  2. Type calendly in the Network filter box and open the page with the scheduler.
  3. Confirm that no request appears for the scheduler, and that the fallback link shows in its place.
  4. Click the "I agree" button and confirm that requests to assets.calendly.com appear and the scheduler replaces the fallback, with no page reload.
  5. Close every private window, open a new one, open DevTools on the Network tab filtered on calendly, load the page, and click the "I decline" button.
  6. Confirm that the Network tab does not show a request and that the fallback link stays on the page and opens your Calendly page.

Run the check on every template that carries a scheduler, such as the contact page and the pricing page.

Common mistakes with Calendly

A Calendly scheduler that shows before consent, or never shows, points to the page source, the embed code, or the run() options. Check each of them for every mistake.

  • An untagged copy of the script. Requests to assets.calendly.com appear before you answer. A page builder block, a WordPress plugin, or an older embed loads the same script without a tag. Search the page source for assets.calendly.com, and tag or remove every copy.
  • An iframe embed. The scheduler shows before you answer. Some tools embed a Calendly page as an <iframe>, which Cookie Consent never blocks. Search the page source for an <iframe with a calendly.com address. Replace it with the tagged widget.js embed, or hold it with the placeholder pattern of Block Embeds and Iframes Until Consent.
  • The fallback without its class or attribute. A fallback that lacks the cc-fallback class or the data-cc-category="functionality" attribute stays on the page next to the scheduler, because the tagged script removes only elements that carry both. Search the page source for cc-fallback, and check the attribute on each match. Keep both on the fallback.
  • The Calendly consent banner left on. The scheduler shows the Calendly consent banner to a visitor who accepted yours. Search the page source for calendly-inline-widget and look for hide_gdpr_banner=1 in the data-url of each match. Add hide_gdpr_banner=1 to the data-url of every scheduler on the page.
  • The category missing from notice_banner_purposes_levels. The tagged widget.js script never runs while functionality is missing from that option, so the scheduler loads for no visitor. With "debug": true, the Console shows JavaScript script of the category "functionality" never runs. Keep functionality in the option on every page that carries a scheduler.