How to Build a Property Rental Availability Calendar with JavaScript
By SimpleCalendarJS Team
Every vacation rental website — from a single beach house to a portfolio of apartments — needs a property rental availability calendar. Guests expect to see which dates are booked, pick their check-in and check-out, and get instant confirmation. The obvious path is a SaaS widget like RentalBeam, GRUPZ, or Lodgify, but those come with monthly fees, third-party branding, and limited control over the experience. Building your own rental availability calendar is more straightforward than it sounds, and the calendar UI is the only technically challenging part.
Why most rental calendar solutions fall short
Property managers typically reach for one of three options, and each has real trade-offs.
SaaS calendar widgets like RentalBeam, GRUPZ, and AvailabilityCalendar.com let you embed an availability calendar via iframe or script tag. They sync with Airbnb and VRBO via iCal, which is convenient. But they inject third-party branding, load external assets you can't control, and charge $5–$15/month per property. For a portfolio of ten properties, that's $600–$1,800/year — recurring, forever.
FullCalendar is the default developer choice for any calendar project. It's powerful — drag-and-drop, resource views, recurring events — but the full bundle ships at ~500 KB of JavaScript. For a rental calendar that just needs to show booked dates and handle range selection, that's massive overkill. You'll also need the premium @fullcalendar/interaction plugin for proper date-range selection, which requires a commercial license at $750/year.
Mobiscroll has a dedicated property booking calendar demo that shows exactly the rental UI you want — monthly view, coloured booking bars, check-in/check-out labels. It's well-built, but licenses start at $395/project and the component adds ~80 KB+ to your bundle.
| Solution | Cost | Bundle Size | iCal Sync | Branding |
|---|---|---|---|---|
| SaaS widgets (RentalBeam, GRUPZ) | $5–$15/mo per property | External iframe | Built-in | Third-party badge |
| FullCalendar (with interaction) | $750/yr | ~500 KB | Build yourself | None |
| Mobiscroll | $395+/project | ~80 KB+ | Build yourself | None |
| DayPilot Lite | Free (open source) | ~120 KB | Build yourself | DayPilot branding |
| SimpleCalendarJS | Free personal / $49/yr commercial | ~14 KB | Build yourself | None |
The pattern: SaaS tools charge recurring fees per property. Enterprise libraries charge hundreds upfront and add heavy bundles. If your rental calendar needs are standard — show availability, handle date ranges, block dates — you can build it yourself with a lightweight calendar library.
Building the rental availability calendar
The calendar needs four capabilities: display bookings visually, let guests select a date range, enforce minimum stays, and sync with external platforms. Here's how to build each.
Step 1: Render the calendar with bookings
npm install simple-calendar-js
import SimpleCalendarJs from 'simple-calendar-js'; import 'simple-calendar-js/dist/simple-calendar-js.min.css'; const calendar = new SimpleCalendarJs('#availability-calendar', { defaultView: 'month', locale: 'en-US', enabledViews: ['month'], fetchEvents: async (start, end) => { const res = await fetch( `/api/bookings?from=${start.toISOString()}&to=${end.toISOString()}&propertyId=${propertyId}` ); const bookings = await res.json(); return bookings.map((booking) => ({ id: booking.id, title: booking.type === 'guest' ? 'Booked' : booking.type === 'owner' ? 'Owner' : 'Maintenance', start: new Date(booking.checkIn), end: new Date(booking.checkOut), color: booking.type === 'guest' ? '#ef444440' : booking.type === 'owner' ? '#6366f140' : '#f59e0b40', })); }, });
This renders a month calendar at ~14 KB gzipped with zero dependencies. Bookings appear as coloured overlays — red for guest reservations, purple for owner blocks, amber for maintenance. The fetchEvents callback fires every time the user navigates to a new month, so availability is always current without a full page reload.
Step 2: Handle date-range selection
Guests need to pick a check-in and check-out date. Track clicks on the calendar and validate the selection:
let checkIn = null; let checkOut = null; calendar.setOptions({ onSlotClick: (date) => { if (!checkIn || (checkIn && checkOut)) { checkIn = date; checkOut = null; updateSelectionUI(checkIn, null); return; } if (date <= checkIn) { checkIn = date; updateSelectionUI(checkIn, null); return; } checkOut = date; const nights = Math.round((checkOut - checkIn) / (1000 * 60 * 60 * 24)); if (nights < minimumStay) { showError(`Minimum stay is ${minimumStay} nights.`); checkOut = null; return; } if (hasConflict(checkIn, checkOut, existingBookings)) { showError('Selected dates overlap with an existing booking.'); checkOut = null; return; } updateSelectionUI(checkIn, checkOut); showBookingSummary(checkIn, checkOut, nights); }, });
function hasConflict(start, end, bookings) { return bookings.some( (b) => new Date(b.checkIn) < end && new Date(b.checkOut) > start ); }
The overlap check is a standard interval intersection test: two date ranges overlap if one starts before the other ends, and vice versa. This runs client-side for instant feedback — the server enforces the same check on submission.
Step 3: Display seasonal pricing
Rental properties often have different rates by season. Show per-night prices on the calendar to help guests plan:
const seasonalRates = [ { label: 'Peak', start: '2026-06-15', end: '2026-09-15', rate: 250 }, { label: 'Mid', start: '2026-04-01', end: '2026-06-14', rate: 175 }, { label: 'Mid', start: '2026-09-16', end: '2026-10-31', rate: 175 }, { label: 'Low', start: '2026-11-01', end: '2027-03-31', rate: 120 }, ]; function getRateForDate(date) { const dateStr = date.toISOString().split('T')[0]; const season = seasonalRates.find((s) => dateStr >= s.start && dateStr <= s.end); return season ? season.rate : seasonalRates[seasonalRates.length - 1].rate; } function calculateTotalPrice(checkIn, checkOut) { let total = 0; const current = new Date(checkIn); while (current < checkOut) { total += getRateForDate(current); current.setDate(current.getDate() + 1); } return total; }
When the guest selects a range, show the breakdown: 3 nights × $250 (peak) + 2 nights × $175 (mid) = $1,100. This transparency reduces booking friction — guests don't have to wonder about the total before submitting.
Step 4: Sync with Airbnb, VRBO, and Booking.com
Every major rental platform publishes an iCal feed per listing. Parse these server-side and merge them into your bookings database:
// Server-side (Node.js) import ical from 'node-ical'; async function syncIcalFeed(propertyId, feedUrl, source) { const events = await ical.async.fromURL(feedUrl); const bookings = Object.values(events) .filter((e) => e.type === 'VEVENT') .map((e) => ({ propertyId, checkIn: e.start.toISOString().split('T')[0], checkOut: e.end.toISOString().split('T')[0], type: 'guest', source, // 'airbnb', 'vrbo', 'booking.com' externalId: e.uid, summary: e.summary || 'External Booking', })); await upsertBookings(propertyId, source, bookings); }
// Run every 15 minutes via cron const FEEDS = [ { propertyId: 'beach-house', url: 'https://airbnb.com/calendar/ical/...', source: 'airbnb' }, { propertyId: 'beach-house', url: 'https://vrbo.com/icalendar/...', source: 'vrbo' }, ]; async function syncAll() { await Promise.all(FEEDS.map((f) => syncIcalFeed(f.propertyId, f.url, f.source))); }
The upsertBookings function should use the externalId (iCal UID) to avoid duplicates — insert new bookings, update changed ones, and remove cancelled ones. Run the sync on a 15–30 minute cron interval. More frequent than that hammers the platform APIs; less frequent risks showing stale availability.
Multi-property support
If you manage multiple properties, render a property selector above the calendar and reload events when the selection changes:
const properties = [ { id: 'beach-house', name: 'Oceanview Beach House', minStay: 3 }, { id: 'mountain-cabin', name: 'Mountain Retreat Cabin', minStay: 2 }, { id: 'city-apartment', name: 'Downtown City Apartment', minStay: 1 }, ]; let propertyId = properties[0].id; let minimumStay = properties[0].minStay; function selectProperty(id) { const property = properties.find((p) => p.id === id); propertyId = property.id; minimumStay = property.minStay; calendar.refetchEvents(); }
Each property gets its own minimum stay, seasonal rates, and booking data. The calendar component stays the same — only the data source changes. This scales from a single listing to a property management portfolio without restructuring your code.
Feature comparison
| Feature | SaaS Widgets | FullCalendar | Mobiscroll | SimpleCalendarJS |
|---|---|---|---|---|
| Bundle size | External iframe | ~500 KB | ~80 KB+ | ~14 KB |
| Date-range selection | Built-in | Plugin (paid) | Built-in | Built-in |
| Seasonal pricing display | Limited | Build yourself | Demo available | Build yourself |
| iCal sync | Built-in | Build yourself | Build yourself | Build yourself |
| Minimum stay enforcement | Some | Build yourself | Build yourself | Build yourself |
| Multi-property support | Some (paid tiers) | Build yourself | Build yourself | Build yourself |
| Monthly cost | $5–$15/property | $0 (after license) | $0 (after license) | $0 |
| Upfront cost | $0 | $750+/yr | $395+/project | Free personal / license req. commercial |
The SaaS widgets win when you need something live in five minutes and don't mind the branding or recurring fees. The custom approach wins when you want design control, own your data, and can't justify per-property monthly costs that compound as your portfolio grows.
Summary
- SaaS rental calendar widgets (RentalBeam, GRUPZ) charge $5–$15/month per property and embed external iframes you can't fully brand or customise
- FullCalendar ships ~500 KB of JavaScript and needs a $750/year premium license for date-range selection — overkill for a rental availability display
- A custom rental availability calendar built with SimpleCalendarJS ships at ~14 KB with colour-coded bookings, date-range selection, minimum stay enforcement, and seasonal pricing — all in vanilla JavaScript
- iCal sync with Airbnb, VRBO, and Booking.com is a server-side cron job parsing standard
.icsfeeds — no proprietary API required - The same calendar component scales from a single property to a multi-property portfolio by swapping the data source
Sources & Further Reading
Research & References
- FullCalendar is 500Kb. I Built an Alternative at 78Kb — DEV Community
- Compare the Best React Scheduler Components for 2025-2026 — DHTMLX Blog
- Mobiscroll Property Booking Calendar Demo — Mobiscroll
- Mobiscroll Range Vacation Property Availability Demo — Mobiscroll
- HomeAway Calendar Widget — GitHub
- Top JavaScript Schedulers for Booking & Appointment Systems — DHTMLX Blog
- Availability Calendar for Vacation Rental Websites — AvailCalendar.com
- GRUPZ: Vacation Rental Calendars for Any Website — grupz.com
- RentalBeam: Availability Calendar, Map & Booking Widgets — RentalBeam
- Best React Calendar Components 2025: Top 7 Libraries Compared — Zoer
Image Credits
- Cover: Landscape Photography of Beach Resort — Pexels
All images free to use under the Pexels License.
Frequently Asked Questions
What is the best JavaScript library for a rental availability calendar?
It depends on your needs. FullCalendar is feature-rich but ships ~500 KB of JavaScript. Mobiscroll has dedicated rental demos but costs $395+ per project. SimpleCalendarJS renders a full month/week calendar at ~14 KB gzipped with zero dependencies, supports date-range selection and colour-coded events out of the box, and works in any framework or plain HTML.
How do I show booked and available dates on a rental calendar?
Fetch your bookings from an API and map them to calendar events with different colours — red or grey for booked dates, green for available. SimpleCalendarJS accepts an async fetchEvents callback that runs whenever the visible month changes, so you always show up-to-date availability without a full page reload.
How do I block dates on a rental calendar for owner use or maintenance?
Treat blocked dates the same as bookings in your data model — store them with a type like 'owner-block' or 'maintenance'. Render them on the calendar with a distinct colour and exclude them from the available date pool when a guest tries to select a range.
Can I sync my rental availability calendar with Airbnb and VRBO?
Yes. Airbnb, VRBO, and Booking.com all publish iCal feeds for each listing. Parse the .ics file server-side with a library like ical.js or node-ical, extract VEVENT blocks, and merge them into your bookings database. Run the sync on a cron job every 15–30 minutes to keep availability current.
How do I enforce a minimum stay on a rental calendar?
Validate the selected date range client-side before submission: calculate the number of nights between check-in and check-out, and reject ranges shorter than your minimum. Always enforce the same rule server-side too — client-side validation is for UX, not security.
Do I need a backend for a rental availability calendar?
You need a backend to store bookings, prevent double-booking, and sync with external platforms. The calendar UI itself is entirely client-side. A lightweight option is a serverless function (Vercel Edge Functions, AWS Lambda, Cloudflare Workers) connected to a database like Supabase, PlanetScale, or even a simple SQLite file.
Add a calendar to your app today
Free for personal projects. $49/year or $199 lifetime per commercial project.
