Frameworks
Microsoft Bookings widget for Next.js
Next.js uses the React package, @bookingsxp/react. The component is marked "use client", renders the custom element during server rendering, and loads the script in the browser, so your booking page can stay a server component with just the widget as a client island.
- Plan
- Plan: FreeWorks on every plan, including Free ($0, no card).
- In short
- Use @bookingsxp/react in the App Router: server-rendered element, client-side loader, redirect to your thank-you route.
- Microsoft Bookings
- Stays your system of record. Bookings land in Outlook and Teams and Microsoft sends the invites and reminders.
How it works
What happens with Next.js
- @bookingsxp/react ships with the "use client" directive, so you can import it straight into a server component page.
- The server HTML contains <bookingsxp-widget …>; the loader then draws into its shadow root, which React does not reconcile, so hydration stays clean.
- redirectUrl accepts relative paths, resolved against the current page, and fills {booking_id}, {service} and {start} after a confirmed booking.
- Client-side navigation is fine: each mounted component registers with the loader, which is loaded once per page.
Setup
Set up Next.js
4 steps. Names of menus and buttons are as they appear today; vendors rename things, so look for the closest match.
Step 1: Install the React package
There is no separate Next.js package.
Shell npm i @bookingsxp/react # npm release pending — until then use the script embedStep 2: Add a booking route
The page can remain a server component; the widget is a client component.
app/book/page.tsx import { BookingsXPWidget } from "@bookingsxp/react"; export const metadata = { title: "Book a call" }; export default function BookPage() { return ( <main> <h1>Book a call</h1> <BookingsXPWidget widget="w_8fk2m1qz" redirectUrl="/thank-you?ref={booking_id}" /> </main> ); }Step 3: Read the reference on the thank-you page
The redirect happens 600 ms after confirmation so pixels can flush. The reference is URL-encoded in the query.
app/thank-you/page.tsx export default async function ThankYou({ searchParams }: { searchParams: Promise<{ ref?: string }> }) { const { ref } = await searchParams; return <p>You're booked. Your reference is {ref}.</p>; }Step 4: Allow the script in your CSP
If you send a Content-Security-Policy, allow bookingsxp.com in script-src and frame-src.
Text script-src 'self' https://bookingsxp.com; frame-src https://bookingsxp.com;
Outcomes and caveats
What you get
The result
- A booking page that stays mostly server-rendered.
- No hydration mismatches from the embed.
- A typed onBooked callback and a thank-you route with the booking reference.
Good to know
Limits and caveats
- Put callbacks (onBooked, onEvent) in a client component; functions cannot be passed from a server component.
- If you load GTM with next/script, make sure it loads on the booking route too, or the dataLayer events have no listener.
- Static export works; the widget needs no server code in your app.
FAQ
Next.js questions
Something not covered? Email hello@bookingsxp.com.
Start free, no card
One widget, three templates, GTM and GA4 events and attribution in every booking. No card, no time limit.