Scripts

Calendly is a scheduling tool that lets visitors book time on your calendar without back-and-forth emails. The Calendly embed widget renders the booking flow inline, in a popup, or behind a floating badge button.

Nuxt Scripts provides a registry script composable useScriptCalendly() and a headless <ScriptCalendlyInlineWidget> component to integrate it in your Nuxt app.

Calendly

View source

Nuxt Config Setup

Add this to your nuxt.config.ts to load Calendly globally. Alternatively you can use the useScriptCalendly composable for more control.

export default defineNuxtConfig({
  scripts: {
    registry: {
      calendly: {
        trigger: 'onNuxtReady',
      }
    }
  }
})

This config automatically enables first-party mode (bundle + proxy). See below to customise.

useScriptCalendly()

The useScriptCalendly composable lets you have fine-grain control over when and how Calendly is loaded on your site.

const { proxy } = useScriptCalendly()

Please follow the Registry Scripts guide to learn more about advanced usage.

First-Party Mode: Privacy Focused Proxy

No extra config needed. The script is bundled from your domain (faster loads, no extra DNS lookup) and runtime requests are reverse-proxied through your server with automatic anonymisation (user IPs stay hidden from Calendly, works with ad blockers). Learn more.

Mode
Bundle Proxy Partytown
Privacy
User IP addresses are anonymised. Other request data passes through.
export default defineNuxtConfig({
  scripts: {
    // ✅ First-party mode: bundled + proxied
    registry: {
      calendly: {
        trigger: 'onNuxtReady',
      },
    },
  },
})

Example

Using Calendly in a component with the proxy to send events .

<script setup lang="ts">
const { proxy } = useScriptCalendly()

// noop in development, ssr
// just works in production, client
function handleAction() {
  // use proxy methods here
}
</script>

<template>
  <div>
    <button @click="handleAction">
      Send Event
    </button>
  </div>
</template>

The composable comes with the following defaults:

  • Trigger: Client Script will load when Nuxt is hydrating.
  • Stylesheet: Inline The widget stylesheet (and its close-icon SVG) is inlined on first use, so no IP leak to assets.calendly.com on render.

You can access the Calendly global as a proxy directly or await onLoaded to use it. Recommended to use the proxy for void calls; onLoaded is convenient when you need a stable DOM reference.

const { proxy } = useScriptCalendly()
function openBooking() {
  proxy.Calendly.initPopupWidget({
    url: 'https://calendly.com/your-name/30min',
  })
}

<ScriptCalendlyInlineWidget>

The <ScriptCalendlyInlineWidget> component wraps useScriptCalendly() for the most common embed shape: an inline booking flow mounted into a host element you control.

It's optimized for performance by using Element Event Triggers, only loading the Calendly widget script once the host element comes into view. By default the trigger is 'visible'.

<script setup lang="ts">
const ready = ref(false)
</script>

<template>
  <ScriptCalendlyInlineWidget
    url="https://calendly.com/your-name/30min"
    @ready="ready = true"
  />
</template>

Above-the-fold loading

If the widget is above the fold and you want it to start loading on hydration rather than on visibility, set above-the-fold (adds a preconnect to calendly.com) and override the trigger.

<ScriptCalendlyInlineWidget
  url="https://calendly.com/your-name/30min"
  above-the-fold
  trigger="onNuxtReady"
/>

Prefill, UTM, and page settings

<ScriptCalendlyInlineWidget
  url="https://calendly.com/your-name/30min"
  :prefill="{ name: 'Ada Lovelace', email: '[email protected]' }"
  :utm="{ utmSource: 'website', utmMedium: 'cta', utmCampaign: 'launch' }"
  :page-settings="{ hideEventTypeDetails: true, hideGdprBanner: true }"
/>

Slots

The component exposes loading, awaitingLoad, and error slots for placeholder UX while the script trigger waits or the script load fails. The default loading slot renders an accessible spinner.

Popup and badge modes have no host element, so they're driven from the composable directly:

const { proxy } = useScriptCalendly()
function open() {
  proxy.Calendly.initPopupWidget({
    url: 'https://calendly.com/your-name/30min',
  })
}

Prefilling invitee details and UTM parameters

All four widget initialisers (initInlineWidget, initPopupWidget, initBadgeWidget, initPopupWidgetWithText) accept prefill and utm options to pre-populate the booking form and tag the booking with marketing attribution.

<script setup lang="ts">
const { proxy } = useScriptCalendly()

function bookFromCampaign(user: { name: string, email: string }) {
  proxy.Calendly.initPopupWidget({
    url: 'https://calendly.com/your-name/30min',
    prefill: {
      name: user.name,
      email: user.email,
    },
    utm: {
      utmSource: 'website',
      utmMedium: 'cta',
      utmCampaign: 'launch',
    },
  })
}
</script>
urlstring

The Calendly event URL to embed. Required for inline, popup, and badge widgets when called via the composable.

prefillobject

Pre-fill invitee fields on the booking form.

utmobject

UTM parameters appended to the booking URL for marketing attribution.

pageSettingsobject

Theme and layout overrides applied to the booking page.

parentElementstring

CSS selector for the element that hosts the inline widget. Required when the widget is initialised inline; the element should have a minimum height of around 700px so the booking iframe is fully visible.

Example

Loading Calendly through app.vue when Nuxt is ready, with the inline widget rendered on a booking page.

app.vue
<script setup lang="ts">
useScriptCalendly({
  scriptOptions: {
    trigger: 'onNuxtReady',
  },
})
</script>