Quickstart
By the end of this page a guest can open /book on your Next.js site, pick a slot, and book a meeting that shows up under Meetings in your clndr.pro dashboard. You need a clndr.pro account and a Next.js 14+ app using the App Router.
Set your hours
In the dashboard, open Availability and set the hours you take meetings. Slots only exist inside these hours, so a page with no availability shows no times at all.
Connect Google Calendar when the dashboard asks. clndr.pro then keeps your existing events off the slot list and can add Meet links to bookings. It works without Google too: bookings still land in Meetings.
Create a booking page
Open Booking pages, choose New booking page, and note its Link, the slug at the end of its URL. A page at
www.clndr.pro/ada/intro-callhas the slugintro-call.Leave Confirmation on Instant for now, so test bookings confirm immediately. Needs approval is covered in How booking works.
Create a publishable key
Open API keys, choose Create API Key, pick Publishable key and give it a name like "Marketing site". Copy the key: it starts with
clndr_pk_and is shown once.A publishable key is meant to sit in browser code. It can read your booking pages and slots and create bookings, nothing else. API keys explains the two key types.
Install the React package
npm install @clndr-pro/reactThen put the key in
.env.local:.env.local NEXT_PUBLIC_CLNDR_PUBLISHABLE_KEY=clndr_pk_...Use
@clndr-pro/react0.2.1 or later. Earlier versions callhttps://clndr.pro, which redirects towwwand loses the key on the way, so every request fails with401 Missing API key. If you're stuck on an older version, passbaseUrl="https://www.clndr.pro"toClndrProvider.Add the provider
ClndrProviderholds the API client every component and hook uses. It's a client component, so give it its own file instead of turning your root layout into one:app/providers.tsx 'use client'; import { ClndrProvider } from '@clndr-pro/react'; export function Providers({ children }: { children: React.ReactNode }) { return ( <ClndrProvider publishableKey={process.env.NEXT_PUBLIC_CLNDR_PUBLISHABLE_KEY!}> {children} </ClndrProvider> ); }app/layout.tsx import { Providers } from './providers'; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="en"> <body> <Providers>{children}</Providers> </body> </html> ); }Render the booking form
app/book/page.tsx import { BookingWidget } from './booking-widget'; export const metadata = { title: 'Book a call' }; export default function BookPage() { return ( <main className="mx-auto max-w-xl px-4 py-12"> <BookingWidget /> </main> ); }app/book/booking-widget.tsx 'use client'; import { BookingInline } from '@clndr-pro/react'; export function BookingWidget() { return ( <BookingInline slug="intro-call" onBooked={(bookingId) => console.log('booked', bookingId)} /> ); }Replace
intro-callwith your slug. The page itself stays a Server Component; only the widget runs in the browser.Book a test meeting
Run
npm run dev, open localhost:3000/book, pick a date and a time, and confirm with your own email address. You'll get the confirmation email, and the meeting appears under Meetings.
Make it look like your site
The form ships with plain, unstyled markup. Hand it your own Button, Input and Label and it renders with them:
'use client';
import { BookingInline } from '@clndr-pro/react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
export function BookingWidget() {
return (
<BookingInline
slug="intro-call"
components={{ Button, Input, Label }}
classNames={{
root: 'space-y-6',
heading: 'text-2xl font-semibold',
muted: 'text-sm text-muted-foreground',
slotGrid: 'mt-4 grid grid-cols-3 gap-2',
}}
/>
);
}Styling and slots lists every slot and class name. To design the whole flow yourself (a calendar, grouped time slots, your own form), use the hooks instead: the shadcn booking widget tutorial builds one.
Next
- Keep the guest's details off the browser-to-clndr.pro path with a server action.
- Approve or decline requests from your own admin screen: Approve bookings in your admin.
- Something not working? Troubleshooting covers the usual suspects:
401, empty slot lists, and dates a day off.