From 2eb01db47376600426609fbb4eaa64933ad3b971 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Sat, 11 Jul 2026 19:38:31 -0700 Subject: [PATCH 1/2] fix(demo): preload the Cal.com booking embed while the visitor fills the form The embed script, booker iframe, and its assets only started downloading after the visitor pressed Continue, so the calendar took several seconds to appear. Warm the whole path on first form focus via the embed's documented preload instruction (hidden ?preload=true iframe caches the booker assets) plus a preconnect to app.cal.com. Nothing Cal.com-related loads at initial page load, so Lighthouse/LCP are untouched. --- .../components/demo-booking/demo-booking.tsx | 13 ++++++++++++- .../demo-scheduler/demo-scheduler.tsx | 18 ++++++++++++++++++ .../demo/components/demo-scheduler/index.ts | 2 +- 3 files changed, 31 insertions(+), 2 deletions(-) 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..e053d03729a 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,19 @@ 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: 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 none of it competes + * with initial page load, and finished long before the visitor submits. + */ +const preloadScheduler = () => 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 @@ -42,6 +51,8 @@ interface DemoBookingProps { * hides/shows. */ export function DemoBooking({ className }: DemoBookingProps) { + preconnect('https://app.cal.com') + const [lead, setLead] = useState(null) const [formHeight, setFormHeight] = useState() const formRef = useRef(null) @@ -75,7 +86,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..145704669f8 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,24 @@ 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. + */ +export function preloadCalEmbed(): void { + if (calEmbedPreloaded) return + calEmbedPreloaded = true + getCalApi({ namespace: CAL_NAMESPACE }).then((cal) => { + cal('preload', { calLink: CAL_LINK }) + }) +} + /** * 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' From 9e498460e15e466debb8c8122c2571b208626207 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Sat, 11 Jul 2026 19:47:03 -0700 Subject: [PATCH 2/2] fix(demo): retry embed warm-up on failure, preconnect only on first focus Reset the preload guard when embed.js fails to load so a later focus can retry, and move the app.cal.com preconnect from render into the focus-triggered preload path so initial page load makes zero Cal.com connections. --- .../components/demo-booking/demo-booking.tsx | 17 ++++++++++------- .../demo-scheduler/demo-scheduler.tsx | 14 ++++++++++---- 2 files changed, 20 insertions(+), 11 deletions(-) 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 e053d03729a..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 @@ -9,12 +9,17 @@ import { DemoForm, type DemoLead } from '@/app/(landing)/demo/components/demo-fo const importScheduler = () => import('@/app/(landing)/demo/components/demo-scheduler') /** - * Warm the entire booking path while the visitor fills the form: 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 none of it competes - * with initial page load, and finished long before the visitor submits. + * 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. */ -const preloadScheduler = () => importScheduler().then((m) => m.preloadCalEmbed()) +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 @@ -51,8 +56,6 @@ interface DemoBookingProps { * hides/shows. */ export function DemoBooking({ className }: DemoBookingProps) { - preconnect('https://app.cal.com') - const [lead, setLead] = useState(null) const [formHeight, setFormHeight] = useState() const formRef = useRef(null) 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 145704669f8..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 @@ -27,14 +27,20 @@ let calEmbedPreloaded = false * 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. + * 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 }) - }) + getCalApi({ namespace: CAL_NAMESPACE }) + .then((cal) => { + cal('preload', { calLink: CAL_LINK }) + }) + .catch(() => { + calEmbedPreloaded = false + }) } /**