diff --git a/apps/sim/app/(landing)/demo/components/demo-booking/demo-booking.tsx b/apps/sim/app/(landing)/demo/components/demo-booking/demo-booking.tsx index 4b7a499704d..4597437ae31 100644 --- a/apps/sim/app/(landing)/demo/components/demo-booking/demo-booking.tsx +++ b/apps/sim/app/(landing)/demo/components/demo-booking/demo-booking.tsx @@ -3,10 +3,24 @@ import { type CSSProperties, useEffect, useRef, useState } from 'react' import { chipBorderShadowRing, cn } from '@sim/emcn' import dynamic from 'next/dynamic' +import { preconnect } from 'react-dom' import { DemoForm, type DemoLead } from '@/app/(landing)/demo/components/demo-form' const importScheduler = () => import('@/app/(landing)/demo/components/demo-scheduler') +/** + * Warm the entire booking path while the visitor fills the form: preconnect to + * app.cal.com, then load the scheduler chunk, Cal.com's embed.js, and the + * booker iframe assets (via the embed's `preload` instruction). Fired on first + * form focus so nothing Cal.com-related competes with initial page load — the + * connection handshake overlaps the chunk import, and it all finishes long + * before the visitor submits. + */ +function preloadScheduler() { + preconnect('https://app.cal.com') + return importScheduler().then((m) => m.preloadCalEmbed()) +} + /** * Lazy-loaded so the Cal.com embed never enters the initial landing bundle - it * loads only once a visitor reaches the booking step. `loading: () => null` (no @@ -75,7 +89,7 @@ export function DemoBooking({ className }: DemoBookingProps) {
void importScheduler()} + onFocusCapture={() => void preloadScheduler()} >
diff --git a/apps/sim/app/(landing)/demo/components/demo-scheduler/demo-scheduler.tsx b/apps/sim/app/(landing)/demo/components/demo-scheduler/demo-scheduler.tsx index c86d0ea9fbf..c10cc3ea103 100644 --- a/apps/sim/app/(landing)/demo/components/demo-scheduler/demo-scheduler.tsx +++ b/apps/sim/app/(landing)/demo/components/demo-scheduler/demo-scheduler.tsx @@ -19,6 +19,30 @@ interface DemoSchedulerProps { lead: DemoLead } +let calEmbedPreloaded = false + +/** + * Warm the Cal.com embed before the scheduler mounts. Loads `embed.js` and + * issues the embed's `preload` instruction, which fetches the booker in a + * hidden `?preload=true` iframe so its assets are already cached when the real + * embed renders on submit. Without this, nothing Cal.com-related starts + * downloading until the visitor presses Continue, which is why the calendar + * used to take several seconds to appear. Idempotent — repeat calls no-op + * while a warm-up is in flight or done, but a failed embed.js load resets the + * flag so a later focus can retry. + */ +export function preloadCalEmbed(): void { + if (calEmbedPreloaded) return + calEmbedPreloaded = true + getCalApi({ namespace: CAL_NAMESPACE }) + .then((cal) => { + cal('preload', { calLink: CAL_LINK }) + }) + .catch(() => { + calEmbedPreloaded = false + }) +} + /** * Step 2 of the booking card - the Cal.com scheduler, prefilled from the form's * {@link DemoLead}. Rendered inside the card chrome owned by {@link DemoBooking} diff --git a/apps/sim/app/(landing)/demo/components/demo-scheduler/index.ts b/apps/sim/app/(landing)/demo/components/demo-scheduler/index.ts index 72290c7861f..a280b21282f 100644 --- a/apps/sim/app/(landing)/demo/components/demo-scheduler/index.ts +++ b/apps/sim/app/(landing)/demo/components/demo-scheduler/index.ts @@ -1 +1 @@ -export { DemoScheduler } from './demo-scheduler' +export { DemoScheduler, preloadCalEmbed } from './demo-scheduler'