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.
| Property | What it does |
|---|---|
blockedDatesSourceObject | The object holding closures (e.g. Office_Closure__c). |
blockedDatesDateField | The Date field on that object. |
blockedDatesStartTimeField / blockedDatesEndTimeField | Optional Time fields. Set both for partial-day blocks; leave them off for whole-day. |
blockedDatesResourceField | Optional 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 / blockedDatesFilterValue | Optional. Scope closures by a field value (e.g. Region__c = North), or by the parent record. |
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.
| Property | What it does |
|---|---|
capacityField | A Number field on the resource (e.g. Capacity__c = 5). Slots show "3 of 5 left" and stop accepting bookings once full. |
showAvailabilityCount | Turn the X/Y badges on. |
capacityAggregation | Combined (sum across resources) or Distinct (count one logical event booked across several resources as one). |
conflictBehavior | block (a taken slot can't be selected), skip (save the open slots, skip the taken), or allow (overbook past capacity). |
Status colors & filtering
Color-code bookings by your own picklist values, hide some statuses, and limit what end users can set.
| Property | What it does |
|---|---|
statusField | A picklist on the booking record (e.g. Status__c = Confirmed / Tentative / Cancelled). |
statusColors | Map each value to a color. Leave blank to auto-assign a palette by picklist position. |
statusColorDisplay | Where colors show: calendar, grid, or both. (Leave empty for no colors - e.g. a clean customer view.) |
hideBookingsWithStatus | Admin-side: hide records with these statuses entirely (e.g. No-Show,Cancelled) - from the grid and capacity counts. |
selectableStatuses | Limit which statuses an end user can choose (e.g. clients can only set Requested), while staff see the full set. |
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.
| Property | What it does |
|---|---|
view | The layout the component opens on: calendar, timeline, week, utilization or agenda. Defaults to calendar. |
availableViews | Which views the switcher offers, e.g. calendar,timeline,agenda. Blank offers all five. Your default is always included, so this only needs the extras. |
timelineWindowDays | Days 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. |
resourceViewFilter | all 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. |
maxViewResources | Caps how many resource rows are drawn, busiest first. Everything is still counted. 0 shows all, and that is the default. |
agendaFields | Extra fields on each Agenda row. Blank reuses hoverFields. Date, time and name fields are skipped since the row already shows them. |
view on a page whose job is booking removes its primary action. List them in availableViews and default to a writable view instead.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.
| Property | What it does |
|---|---|
maxSelections | Maximum number of dates a user can select. 0 = unlimited. |
maxTimeSlotSelection | Maximum number of time slots a user can select in total, across all dates. 0 = unlimited. (Use 1 for "one appointment per person.") |
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.
- 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.
- 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.
- 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.
- Apex class access to
MultiDatePickControlleron the guest profile or permission set - the component loads its data through it. - Activate the site - publishing and activating are separate steps; make sure the site is activated so guests get the live experience.
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 see | What's missing |
|---|---|
| Record won't open / insufficient privileges | Object 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_ENTITY | No access to a referenced record - the parent the lookup points to, or the resource. |
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:
| Source | When 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 Recordswhere 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.
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.$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.