Calendly is gone. We vibe-coded a replacement in one evening.
Point four from the SaaS post, dissected: the booking page on this site. Date picker, Google Calendar availability, Meet links, confirmation emails. And what 'vibe coding' actually looked like.
I wrote that I'm killing small SaaS subscriptions and replacing them with code. Point four was Calendly: "a Next.js page with a date picker." Here's that point, opened up.
If you've ever booked a call with me, you've used it: the Book a slot page on this site. No Calendly, no Cal.com, no embedded iframe phoning home. One route, in the same repo as everything else. The same one I mentioned in passing in the SaaS post.
What the SaaS actually did
Strip Calendly down. It does four things:
- Shows the visitor a calendar of days and free slots.
- Knows when I'm busy, so it never double-books me.
- Creates the meeting on both calendars, with a video link.
- Sends a confirmation email both people can add to their calendar.
That's it. The 5% I ever used. Routing logic, payments, team round-robin, branded pages: none of it. I built exactly those four things.
1. The page and the date picker
One route, /book: a month grid built on react-day-picker, a column of slots beside it, a name-and-email form, a confirmation screen.
I described the flow to Claude in plain English. It produced the component. A few rounds of "selected day should invert, not outline" and "tighten the slot grid" and it looked right.
2. Availability: config plus a free/busy query
This is what makes it not a toy. Two inputs decide which slots exist.
First, a config file. Mine: timezone Europe/Moscow, working hours 07:00–23:00, 30-minute slots, 15-minute buffer between calls, 4 hours of lead time before the earliest bookable slot, 30-day horizon, list of public holidays to skip.
Second, my real calendar. The server calls Google Calendar's freebusy API for the visible window, gets back busy intervals, and subtracts them from the config-generated grid. What's left gets offered.
freebusy is the right call here, not the full events list. It returns busy intervals only: no titles, no attendees. The booking page learns "blocked 14:00–15:00" and nothing else about my day.
3. Booking: validate, then create the event
The submit hits one API route. zod validates the payload: real email, name within length limits, slot string that parses. Junk gets rejected before anything else runs.
Then it re-checks free/busy (the slot could have been taken while the form was open), creates the event in my Google Calendar via googleapis, and lets Google attach a Meet link. The visitor is added as an attendee, so the meeting lands on their calendar too.
4. Confirmation: an email with a real .ics
The confirmation goes out through Resend. It carries a proper .ics attachment (generated by hand), so "Add to calendar" works in Apple Mail, Outlook, anywhere. Plus a one-click Google Calendar link for everyone else.
This is what people miss when they say "a booking page is easy." The page is easy. The calendar interop (timezones, .ics fields, the Meet link, the holiday list) is the part that used to eat the whole evening. With Claude it took about twenty minutes.
The one annoying part: Google OAuth
To read and write my calendar, the server needs a Google OAuth refresh token. That means registering an app in Google Cloud Console, configuring a consent screen, choosing scopes, running a one-time exchange flow. Claude wrote the /api/oauth/start route that does the exchange. The clicking around in Google's console is on you, and that UI changes often enough that no model has it memorized.
Budget half an hour of tedium. Once the refresh token is in your env, you never touch it again.
What "vibe coding" actually meant
Not "Claude wrote an app while I watched." Closer to this:
- I knew the four things the system had to do. I'd thought through the config (buffers, lead time, holidays) before typing a single prompt. That thinking doesn't outsource.
- Claude wrote essentially all the code: the slot math, the free/busy merge, the .ics builder, the React component, the email templates.
- I read every diff. I caught a timezone bug (slots drifting by an hour near a DST boundary) by reading, not by running.
- I made the product calls. Should a held slot expire? Should the email carry a reschedule link? Those are mine.
The model is a fast, well-read pair programmer who has built a hundred booking pages. Not a product owner.
The SaaS sold me the boring 95%. Claude builds that for free now, so I only pay for the 5% that was ever mine.
Was it worth it
A Calendly team plan is $15–30 a month. The page took an evening: three hours, most of it OAuth tedium and polishing the confirmation screen. The build-it-in-an-evening argument, made literal.
Pays for itself in a few months. The real win is different. The booking flow is now four small files I can read, in the repo I already deploy, with data on my own calendar. When I want it to behave differently, I change the code instead of hunting for a toggle in someone's settings panel.
The proof is live. Book a slot on the very page this post is about.