MARKETPLACE
PLUGINS
RESOURCE BOOKING CALENDAR
Resource Booking Calendar logo

Resource Booking Calendar

Published August 2026
   •    Updated this month

Plugin details

A day-view calendar where the columns are your resources — treatment rooms, chairs, bays, courts, stylists, machines — and the hours run down the side.
The layout practice management and booking systems use, which a week or month
grid cannot replace.

Overlapping bookings are laid out side by side, each taking an equal share of
the column. Three appointments booked into the same chair at 9:15 are all
visible, at a third of the width each, instead of hiding one another.

Every time is printed in a timezone YOU specify, not the visitor's. Bubble
renders dates in the browser's zone, which is why a manager in one country sees
different hours than the clinic in another. Here the zone is a field.

WHY THIS PLUGIN EXISTS

Most calendar plugins for Bubble wrap FullCalendar, whose resource views are a
paid add-on: a premium licence starts at $480 per developer per year, and if you
hand clients editable source you need a licence for each of them. This element is
written from scratch and carries no such licence.

FEATURES

The grid
- Columns are resources, with an optional second line (the person on the room)
- One CSS grid draws the header and the cards, so a column and its bookings
 cannot drift apart when you scroll
- Time gutter stays put while resources scroll sideways
- The day sizes itself to the bookings it holds, never shorter than 8 hours
- A red line marks the current time, and moves on its own without refetching
- Bookings with no resource land in an "Unassigned" column at the end rather
 than disappearing

Blocked time
- Lunches, cleaning, maintenance and deliberately held-open time draw as hatched
 bands, not as bookings — no click, no edit icon
- They share the overlap engine, so a break drawn over a booking pushes it
 sideways instead of covering it

Colours and legend
- One status list drives both the card colours and the legend, so they can
 never disagree
- Any number of statuses, each with its own label and colour
- Mark a status "muted" to dim and strike through cancelled bookings
- The legend shows only the statuses present that day, or all of them if you
 prefer
- Background, cards, lines, text, accent and the now-line are all fields

Interaction
- Clicking a booking, or its edit icon, fires separate workflow events
- Clicking empty space returns the resource and the time, rounded to your slot
 step — the whole "tap to book" flow in one event
- States carry the clicked booking's id, title and full JSON, so you can pull
 any field of your own out of it

Setup
- Three JSON fields: resources, bookings, blocked time
- No external libraries, no API keys, no accounts
- Works with any data structure — nothing assumes a particular schema

Demo:   https://resource-scheduler.bubbleapps.io/version-test
Editor: https://bubble.io/page?id=resource-scheduler&test_plugin=1788169839014x195626234316062720_current&tab=Design&name=index

$49

One time

stars   •   0 ratings
0 installs  
This plugin does not collect or track your personal data.

Platform

Web

Contributor details

VibeBox logo
VibeBox
Joined 2022   •   4 Plugins
View contributor profile

Instructions

LIVE DEMO Preview: https://resource-scheduler.bubbleapps.io/version-test
Editor:  https://bubble.io/page?id=resource-scheduler&test_plugin=1788169839014x195626234316062720_current&tab=Design&name=index

SETUP

Step 1 — Place the element
Drop "ResourceScheduler" on your page and give it a height. The calendar fills
the height you set and scrolls inside it. 600px is a good start. Width can be
anything — resources scroll sideways with the time column pinned.

Step 2 — Set the date and the timezone
 date      the day to show, as YYYY-MM-DD
 timezone  an IANA name such as Europe/Kyiv, Asia/Dubai, America/New_York
Every time in the calendar is printed in that zone, not the visitor's browser
zone. Leave it empty and you get UTC.

Step 3 — Feed it your resources
Two ways to do this — use whichever fits your data.

 A) Straight from your database (recommended)
    columns          pick your resource data type (e.g. Room)
    columns_data     Search for [your type] — the list of resources to show
    column_title     which field is the resource's name
    column_subtitle  optional, e.g. the staff member's name
    column_sort      optional, controls the order
    column_key       optional — see the note below

 B) A JSON array, if your data doesn't come from a Bubble list
    columns_json:
      [{"id":"r1","title":"Room 1","subtitle":"Dr. Haddad","sort_key":"1"},
       {"id":"r2","title":"Room 2","subtitle":"Dr. Ivanova","sort_key":"2"}]

If both are filled in, the database list wins.

About column_key: bookings need to say which resource they belong to.
By default the plugin matches on the resource's unique id — fine if your
booking's resource field is a proper link to the resource. If you instead
store a short code on your resource (e.g. "R1") and reference that code
from your bookings, set column_key to the field holding that code, and use
the same code in event_column below. This is the easiest option when your
resource field is text rather than a link.

Step 4 — Feed it your bookings
 A) Straight from your database (recommended)
    events           pick your booking data type (e.g. Appointment)
    events_data      Search for [your type]
    event_column     which field points at the resource (a link, or a
                      text field matching column_key if you set one)
    event_start      which field holds the start date/time
    event_duration   optional, minutes — defaults to 60
    event_title      which field is the bold line on the card
    event_subtitle   optional, shown after the time
    event_status     optional, see step 6
    event_color      optional, overrides the status colour for this booking

 B) A JSON array
    events_json:
      [{"id":"a1","column_id":"r1","start":"2026-09-01T05:00:00Z",
        "duration_min":60,"title":"Marsh, Elena","subtitle":"Checkup",
        "status":"confirmed"}]

A booking whose resource doesn't match any column appears under
"Unassigned" at the end, rather than disappearing.

Step 5 — Add blocked time (optional)
Same two options as bookings, with a label instead of a title.
 A) blocks / blocks_data / block_column / block_start / block_duration /
    block_title
 B) blocks_json:
      [{"id":"b1","column_id":"r1","start":"2026-09-01T09:00:00Z",
        "duration_min":60,"label":"Lunch"}]
These draw as hatched bands and are not clickable.

Step 6 — Define your statuses (optional)
statuses_json drives the card colours AND the legend from one list.
 [{"key":"scheduled","label":"Scheduled","color":"#5cc8d6"},
  {"key":"confirmed","label":"Confirmed","color":"#4ec98a"},
  {"key":"cancelled","label":"Cancelled","color":"#e5646d","muted":true}]
 key     what you put in a booking's "status"
 label   what the legend shows
 color   the card's left edge
 muted   dims the card and strikes through the title
Leave the field empty to use five sensible defaults.

Step 7 — Handle clicks
Three workflow events:
 When ResourceScheduler A's event_clicked        a booking was clicked
 When ResourceScheduler A's event_edit_clicked   its pencil icon was clicked
 (separate from event_clicked, so you can open a read-only detail view on a
 card click and an edit form on the pencil — or route both to the same
 popup if you don't need the distinction)
 When ResourceScheduler A's slot_clicked         empty space was clicked
States available in workflows:
 clicked_event_id      the booking's id — use it to look the record up
 clicked_event_title   its title
 clicked_event_json    the whole booking as JSON, for your own fields
 clicked_column_id     which resource was clicked
 clicked_time          "9:15 am"
 clicked_minutes       555, minutes from midnight
 clicked_date          the day shown
 event_count           bookings currently drawn
 column_count          resources currently drawn
A typical booking flow: on slot_clicked, show a popup, and create your record
using clicked_column_id and clicked_minutes.

Step 8 — Actions
 Refresh          redraws the calendar, e.g. after saving a record
 Scroll to hour   scrolls the grid to a given hour, e.g. 9

BUILDING THE JSON (only if you're using option B above)

Bubble hands lists over as text, so use :format as text on a repeating group's
data source, with a comma as the delimiter, then wrap the result in square
brackets. Quotes inside your values will break the JSON — run names through
:find & replace on the " character first.

If you're feeding the calendar straight from your database (option A in
steps 3–5), you can skip this section entirely.

APPEARANCE

 bg_color      grid background
 card_color    booking cards and the header
 line_color    grid lines and borders
 ink_color     main text
 dim_color     secondary text, hour labels
 accent_color  fallback card colour and hover
 now_color     the current-time line
 block_color   the edge of blocked bands
 day_start     first hour to show on an empty day (default 7)
 day_end       last hour on an empty day (default 20)
 px_per_min    vertical zoom; raise it for longer cards (default 1.1)
 slot_step     rounding for empty-slot clicks, in minutes (default 15)
 show_now_line the red current-time line
 show_edit     the pencil icon on cards
 show_legend / legend_at_bottom / legend_show_all
 block_label   what an unlabelled blocked band is called

TROUBLESHOOTING

Times are off by several hours
 Check timezone. An empty field means UTC.

The calendar is empty but you have data
 If using database lists (option A): open the browser console — the plugin
 warns if a list is still loading, and prints a mismatch if a booking's
 resource field doesn't match any column's id (or its column_key, if set).
 If using JSON (option B): the console names the field whose JSON failed to
 parse. Unescaped quotes inside names are the usual cause.

Bookings sit side by side instead of on top of each other
 That is deliberate. Overlapping bookings split the column between them.

Nothing fits on a phone
 A day with four resources needs width. The grid scrolls sideways and the time
 column stays pinned.

Types

This plugin can be found under the following types:
Element   •   Event   •   Action

Categories

This plugin can be found under the following categories:
Calendar   •   Containers   •   Visual Elements

Resources

Support contact
Tutorial

Rating and reviews

No reviews yet

This plugin has not received any reviews.
Bubble