Coming Soon to AppExchange - 2026

Tips & How-To

Practical setup recipes for the patterns admins ask about most - blocking, capacity, status colors, selection limits, and public guest-facing pages. Each is a configuration of the existing components; no code required.

Blocking dates & times

Grey out dates or time ranges so they can't be booked - from a separate "closure" object you control.

Point the component at any object that holds your closures (e.g. Office_Closure__c) with the blocked-dates properties. Each closure record becomes unavailable in the picker.

PropertyWhat it does
blockedDatesSourceObjectThe object holding closures (e.g. Office_Closure__c).
blockedDatesDateFieldThe Date field on that object.
blockedDatesStartTimeField / blockedDatesEndTimeFieldOptional Time fields. Set both for partial-day blocks; leave them off for whole-day.
blockedDatesResourceFieldOptional lookup that narrows an existing closure to one resource. Leave it blank and each closure applies to all resources (region-wide). Blocking itself is switched on by blockedDatesSourceObject, not by this field.
blockedDatesFilterField / blockedDatesFilterValueOptional. Scope closures by a field value (e.g. Region__c = North), or by the parent record.
Whole-day vs partial-day - how each shows: a closure record without start/end times is whole-day → it greys out the whole calendar date. A closure with a start and end time is partial-day → it greys only those time slots in the grid (visible once a date is selected). Rule of thumb: if you're using the time grid, you want partial-day blocks; whole-day only makes sense for date-only pickers.

A whole-day closure disables the date even if it already has bookings - a closed date isn't bookable. To let customers create closures themselves (e.g. an owner blocking their own time), drop a Date Time component pointed at the closure object on an internal page or Experience site.

Capacity & availability

Let more than one booking share a slot, with live "X of Y" badges and conflict handling.

PropertyWhat it does
capacityFieldA Number field on the resource (e.g. Capacity__c = 5). Slots show "3 of 5 left" and stop accepting bookings once full.
showAvailabilityCountTurn the X/Y badges on.
capacityAggregationCombined (sum across resources) or Distinct (count one logical event booked across several resources as one).
conflictBehaviorblock (a taken slot can't be selected), skip (save the open slots, skip the taken), or allow (overbook past capacity).
Tip: capacity needs the booking start/end time fields configured - without them there are no slots to count against. If a capacity grid shows nothing, check that the time fields are mapped.

Status colors & filtering

Color-code bookings by your own picklist values, hide some statuses, and limit what end users can set.

PropertyWhat it does
statusFieldA picklist on the booking record (e.g. Status__c = Confirmed / Tentative / Cancelled).
statusColorsMap each value to a color. Leave blank to auto-assign a palette by picklist position.
statusColorDisplayWhere colors show: calendar, grid, or both. (Leave empty for no colors - e.g. a clean customer view.)
hideBookingsWithStatusAdmin-side: hide records with these statuses entirely (e.g. No-Show,Cancelled) - from the grid and capacity counts.
selectableStatusesLimit which statuses an end user can choose (e.g. clients can only set Requested), while staff see the full set.
Two treatments, one object: a clean customer page can use statusColorDisplay="" + selectableStatuses=Requested + hideBookingsWithStatus=No-Show,Cancelled, while a back-office page on the same data turns colors on and exposes every status.

Choosing a view

Five layouts read the same records through the same field mappings and save the same way. Pick the default per placement and decide which ones users can switch to.

PropertyWhat it does
viewThe layout the component opens on: calendar, timeline, week, utilization or agenda. Defaults to calendar.
availableViewsWhich views the switcher offers, e.g. calendar,timeline,agenda. Blank offers all five. Your default is always included, so this only needs the extras.
timelineWindowDaysDays on screen at once in Timeline. Defaults to 14, because a full month squeezes each booking to about 20 pixels and clips every label. Use 0 for the whole month.
resourceViewFilterall or booked. booked hides resources with nothing in view, which is the right way to shorten a long list because an empty row carries no information.
maxViewResourcesCaps how many resource rows are drawn, busiest first. Everything is still counted. 0 shows all, and that is the default.
agendaFieldsExtra fields on each Agenda row. Blank reuses hoverFields. Date, time and name fields are skipped since the row already shows them.
Utilization and Agenda are read only. They answer a question rather than take a booking, so setting either as the view on a page whose job is booking removes its primary action. List them in availableViews and default to a writable view instead.
Schedule people, not just rooms: point resourceObjectApiName at User and Timeline becomes a roster, Week a shift grid, and Utilization a team capacity dashboard. Add a custom capacity field on User for percentages, and map businessHoursStartField to a User field so each person's own hours bound the week grid.

Limiting what users can select

Cap how much a single user can pick - by dates or by time slots.

PropertyWhat it does
maxSelectionsMaximum number of dates a user can select. 0 = unlimited.
maxTimeSlotSelectionMaximum number of time slots a user can select in total, across all dates. 0 = unlimited. (Use 1 for "one appointment per person.")
Note: maxSelections caps dates, not slots - a user with maxSelections=1 could still pick several time slots on that one date. Add maxTimeSlotSelection when you need to cap the actual number of bookings.

Public / guest-facing pages (guest access)

Expose a component on an Experience Cloud site so anyone can use it without logging in. Here's the full checklist to get it working for guests.

  1. Object permissions - give the site Guest User Read (and Create, if guests submit) on each object. Guest users can't Edit or Delete records at all - that's a Salesforce guest-license restriction (a platform limitation, not a MultiDatePick or configuration setting), so it can't be granted around no matter how you set up permissions. Any edit, cancel, or delete flow is unavailable to anonymous guests - reserve those for authenticated (logged-in) users. For a guest, an "edit/manage" view is effectively read-only.
  2. Field-level security on every field the component touches - including resource business-hours fields and closure time fields. A missing FLS makes the resource/blocked-date query fail.
  3. Sharing rules - a guest sharing rule (Read) so the Guest User can actually see the records. Object + field access alone isn't enough; "Secure guest user record access" forces this. Two things trip people up here: (a) the object's external org-wide default must be Private for a guest sharing rule to take effect - on a Public Read/Write object the rule is a silent no-op; and (b) a guest sharing rule deployed via metadata doesn't auto-recalculate - click Recalculate once in Setup → Sharing Settings (rules created in the UI recalc on their own). If object + FLS + Apex access all look right but the guest still sees an empty calendar, it's almost always the recalc.
  4. Apex class access to MultiDatePickController on the guest profile or permission set - the component loads its data through it.
  5. Activate the site - publishing and activating are separate steps; make sure the site is activated so guests get the live experience.
Tip: complete all five before testing as a guest. Apex class access (step 4) and site activation (step 5) are the two most commonly overlooked.
Combining components: you can place several components on one page (e.g. an owner "block times" calendar above a customer booker). They don't live-sync, so add a "refresh" prompt or button between sections so a block made above shows as greyed below.
Which record do guests see? On a public page you set a fixed staticRecordId, so every guest shares the same record - the right fit when everyone books against one parent (e.g. a single office, location, or resource). For per-user records (each person seeing only theirs) you'd drive the record from a Flow instead - see the next section.

Permissions & field-level security

The components run Apex in the user's own context (USER_MODE), so whoever uses the calendar must have the right object, field, and record access - on a public page that's the site Guest User; on a record page it's the logged-in user. This is the most common setup snag.

In a permission set (or profile) for those users, grant:

  • Object access - Read + Create on the booking/schedule object, plus any resource, closure, or child objects the component queries.
  • Field-level security - mind read vs. edit: Read on every field the calendar displays (the date, status, and each hover field); Edit on every field it writes - the date, start/end time, status, any second status/category field, the record-name field, and the relationship lookup to the parent. A field the component writes but the user can't edit will fail the save, even when reads look fine.
  • Access to referenced records - saving sets lookups (the parent via the relationship field, and the resource). The user needs at least Read on those target records, or the insert throws INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY.
  • Apex class access to MultiDatePickController.
Error you seeWhat's missing
Record won't open / insufficient privilegesObject Read (or record-level sharing) on the parent object.
"No read access to field X on object Y"Read FLS on a field the calendar displays (a status, hover, or date/time field).
"…time group(s) failed to save" / "Failed to create bookings: Insert failed"Edit FLS on a field the save writes - often a status, second-status, or time field added later.
INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITYNo access to a referenced record - the parent the lookup points to, or the resource.
Even System Administrators need FLS on custom fields. The calendar enforces field security in the running user's context, so a brand-new custom field is invisible to everyone - admins included - until a permission set grants it. If a field seems to "not exist" at save time, it's almost always FLS, not a missing field.
Guests + standard objects (Contact / Account) are hard. Guest access to Contacts is heavily restricted (Contact is usually "Controlled by Parent," and an account-less Contact can't be shared to a guest at all). If a public page's booking links to a Contact or Account, expect cross-reference errors. Prefer a custom object for the parent your guests book against - or drive the page from an authenticated Flow (next section), where the logged-in user already has the access.

Which record the component uses - staticRecordId vs. Flow

How the calendar knows which parent record to load and save against - and when to hardcode it versus resolve it dynamically.

The component chooses its parent record in this order:

SourceWhen it applies
recordId (automatic)On a Lightning record page, Salesforce injects the record being viewed. Nothing to configure - the calendar hangs off that record.
staticRecordId (hardcoded)On surfaces with no record context - App Pages, Home Pages, Experience site pages. You enter one fixed record Id, and every visitor sees the same record.
A Flow (dynamic)When the record must be chosen at runtime - per logged-in user, from a URL parameter, or from an on-screen selection. The Flow resolves the Id and passes it into the component.

When staticRecordId is the right call

  • A public booking page - a guest-facing "request an appointment" page where every booking attaches to the same parent record (one office, location, or resource). A hardcoded Id is exactly right.
  • A single, org-wide calendar - one "Company Closures" record, or one shared resource everyone books, that never changes per user.
  • A fixed anchor on an App or Home page - a dashboard-style calendar that always points at the same master record.

Rule of thumb: reach for staticRecordId when the answer to "whose record is this?" is "the same one for everybody."

When to use a Flow instead

  • Per-user records - each person should see only their own (e.g. every contractor lands on their own PTO calendar). A Screen Flow does Get Records where the owner or a linked Contact = {!$User.Id}, stores the Id, and passes it to the component.
  • URL-driven - a Flow input variable reads ?id=… from the link, so one page serves many records (works for guests too - no login needed).
  • Pick-first flows - screen 1 lets the user search or choose (an employee, a room, a client); screen 2 shows that record's calendar.
How the Flow passes it in: drop the component on a Flow screen and every property - including recordId - becomes a Flow input. Bind recordId to the variable your Flow resolved. Same component, same field mappings; the only difference from staticRecordId is that the Id is computed at runtime instead of typed in once.
Guest sites & per-user records: on a no-login public site, "the logged-in user" is a single shared guest, so $User-based per-user resolution doesn't apply. For a public site, either share one record via staticRecordId (simplest) or drive it from a URL parameter through a Flow. True per-user calendars need authenticated users.

More recipes

Browse ready-to-import configurations for dozens of scheduling use cases, or get notified when MultiDatePick goes live.

Browse the Config Library Notify Me at Launch