How to integrate Acuity scheduling widget in Next.js application

I’m working on adding an Acuity scheduling widget to my Next.js app but running into some issues. I want to add it to just one specific page instead of putting it in the main app files.

Here’s my current attempt:

import Script from 'next/script';
import React from 'react';

const BookingPage = () => {
    AcuityScheduling.initBookingWidget({
        url: 'https://acuityscheduling.com/myaccount/consultation?date=2022-06'
    });

    return (
        <div>
            <Script src="https://embed.acuityscheduling.com/js/embed.js" />

            <div
                className="acuity-booking-widget"
                style={{minWidth: '350px', height: '600px'}}
                data-auto-load="false"></div>
        </div>
    );
};

export default BookingPage;

The problem is I don’t know where to properly place the initialization code:

AcuityScheduling.initBookingWidget({
    url: 'https://acuityscheduling.com/myaccount/consultation?date=2022-06'
});

The widget doesn’t load and I think it’s because the script timing is wrong. Where should I put this initialization function so it runs after the external script loads?

Both solutions work, but here’s a cleaner approach I use in production. Skip the state management and put everything in the onLoad callback:

import Script from 'next/script';
import React from 'react';

const BookingPage = () => {
    return (
        <div>
            <Script 
                src="https://embed.acuityscheduling.com/js/embed.js"
                onLoad={() => {
                    window.AcuityScheduling.initBookingWidget({
                        url: 'https://acuityscheduling.com/myaccount/consultation?date=2022-06'
                    });
                }}
                strategy="lazyOnload"
            />
            
            <div
                className="acuity-booking-widget"
                style={{minWidth: '350px', height: '600px'}}
                data-auto-load="false">
            </div>
        </div>
    );
};

I switched to lazyOnload since you only need this on one page. It loads when users actually visit instead of during initial page load.

We had similar widget issues at work and this pattern solved all our timing problems. The onLoad fires exactly when the external script’s ready - no extra state or effects needed.

You’re hitting this because AcuityScheduling isn’t available when your component first renders. I ran into the exact same issue with my booking system last year. Use the onReady callback from Next.js Script component instead of useEffect: javascript import Script from 'next/script'; import React from 'react'; const BookingPage = () => { const handleScriptLoad = () => { if (typeof window !== 'undefined' && window.AcuityScheduling) { window.AcuityScheduling.initBookingWidget({ url: 'https://acuityscheduling.com/myaccount/consultation?date=2022-06' }); } }; return ( <div> <Script src="https://embed.acuityscheduling.com/js/embed.js" onReady={handleScriptLoad} strategy="afterInteractive" /> <div className="acuity-booking-widget" style={{minWidth: '350px', height: '600px'}} data-auto-load="false"> </div> </div> ); }; The onReady callback fires when the script’s fully loaded and executed - that’s exactly what you need. The strategy prop helps with timing too.

Indeed, the timing issue you’re experiencing is a common one with external scripts in Next.js. To ensure that the Acuity script fully loads before you initialize the widget, you can place your initialization code inside a useEffect hook that responds to changes in a state variable indicating whether the script has loaded. Here’s a revised version of your code that does just that: javascript import Script from 'next/script'; import React, { useEffect, useState } from 'react'; const BookingPage = () => { const [scriptLoaded, setScriptLoaded] = useState(false); useEffect(() => { if (scriptLoaded && window.AcuityScheduling) { window.AcuityScheduling.initBookingWidget({ url: 'https://acuityscheduling.com/myaccount/consultation?date=2022-06' }); } }, [scriptLoaded]); return ( <div> <Script src="https://embed.acuityscheduling.com/js/embed.js" onLoad={() => setScriptLoaded(true)} /> <div className="acuity-booking-widget" style={{minWidth: '350px', height: '600px'}} data-auto-load="false"></div> </div> ); }; This ensures that the widget only initializes once the Acuity script is fully loaded.

Manual script loading and timing issues are exactly why I automated our booking system at work. Every time we tried manually handling widget loading, we’d hit edge cases.

I ditched script timing completely and built a Latenode workflow for all our Acuity integrations. It connects our Next.js app to Acuity through webhooks and API calls - no widget needed.

When users hit our booking page, Latenode pulls available slots from Acuity and shows them in our custom UI. Someone books? It creates the appointment in Acuity and fires off confirmation emails. Zero external scripts, zero timing issues, zero widget headaches.

Best part? You can customize everything. Custom fields, routing services to different calendars, sending booking data to your CRM - Latenode handles it all in one workflow.

We went from constant widget bugs to a rock solid booking system that fits our design. Loads way faster too since there’s no external JavaScript.