Calendar#

An inline calendar for single-date or date-range selection.

Calendar — light theme Calendar — dark theme

Usage#

Basic#

The calendar is always visible — it is not a popup. Pass a datetime.date, datetime.datetime, or an ISO string as value=.

from datetime import date

bs.Calendar()                            # today's month, nothing selected
bs.Calendar(value=date(2026, 5, 15))     # specific date pre-selected
bs.Calendar(value="2026-05-15")          # ISO string accepted

Range selection#

Set selection_mode='range' to select a start–end span. The calendar shows two months side by side. The first click sets the start date; the second click sets the end date.

bs.Calendar(
    selection_mode="range",
    start_date=date(2026, 5, 20),
    end_date=date(2026, 6, 10),
)
Calendar range selection — light theme Calendar range selection — dark theme

Accent colors#

accent= controls the color of selected dates and range highlights.

bs.Calendar(accent="primary")    # default
bs.Calendar(accent="success")
bs.Calendar(accent="danger")

Min / max bounds#

Dates outside the bounds are disabled and month navigation is blocked at the limits.

bs.Calendar(
    min_date=date(2026, 5, 1),
    max_date=date(2026, 5, 31),
)

min_date and max_date are also live properties — assign to them to move the selectable window as availability changes (set either to None to clear that bound):

cal = bs.Calendar()
cal.min_date = date.today()        # no dates before today

Disabled dates#

Individual dates can be disabled regardless of the min/max range.

bs.Calendar(disabled_dates=[date(2026, 5, 4), date(2026, 5, 11)])

disabled_dates is a live property too — assign a new iterable to replace the disabled set at runtime (for example as booked dates load in):

cal.disabled_dates = booked_dates

Week numbers#

Display ISO 8601 week numbers in the leftmost column.

bs.Calendar(value=date(2026, 5, 15), show_week_numbers=True)
Calendar week numbers — light theme Calendar week numbers — dark theme

First weekday#

Override the locale default for the first column (0=Monday, 6=Sunday).

bs.Calendar(first_weekday=6)   # week starts on Sunday

Events#

on_select() fires after every click. In range mode it fires twice — once when the start is set (end is None) and again when the end is set.

cal = bs.Calendar()

# Single mode
cal.on_select(lambda e: print("selected:", cal.value))

# Range mode — wait for a complete range
cal = bs.Calendar(selection_mode="range")
def _on_select(e):
    start, end = cal.range
    if end is not None:
        print(f"range: {start}{end}")
cal.on_select(_on_select)

# As a Stream
cal.on_select().listen(lambda e: refresh(cal.value))

Programmatic control#

cal = bs.Calendar()

cal.value = date(2026, 6, 1)        # set selected date
cal.get()                            # → date(2026, 6, 1)
cal.set(date(2026, 6, 15))          # set without firing event

# Range mode
cal.range = (date(2026, 6, 1), date(2026, 6, 14))
cal.get_range()                      # → (date(...), date(...))
cal.set_range(date(2026, 6, 1), date(2026, 6, 14))

Widget sizing#

All widgets accept self-placement kwargs via **kwargs. The parent container determines which options apply — stack-based parents use stack kwargs, grid-based parents use grid kwargs. Unrecognised keys are silently ignored.

Stack#

Used inside VStack, HStack, App, and other stack containers.

fill

Fill direction: 'x', 'y', 'both', or 'none'.

expand

Grow to consume extra space in the parent. True or False.

anchor

Alignment when the widget does not fill the available slot: 'n', 's', 'e', 'w', 'center', 'nw', etc.

margin

External spacing in pixels. Accepts an integer (equal on all sides), a 2-tuple (horizontal, vertical), or a 4-tuple (left, top, right, bottom).

margin_x

Horizontal external spacing (left and right). Accepts an integer or a 2-tuple (left, right) for asymmetric spacing. Overrides the horizontal component of margin=.

margin_y

Vertical external spacing (top and bottom). Accepts an integer or a 2-tuple (top, bottom) for asymmetric spacing. Overrides the vertical component of margin=.

Grid#

Used inside a Grid container.

row / column

Zero-based row and column indices.

rowspan / columnspan

Number of rows or columns to span.

sticky

Alignment and fill within the grid cell. Any combination of 'n', 's', 'e', 'w' — e.g. 'ew' stretches horizontally, 'nsew' fills the entire cell.

margin

External spacing in pixels. Accepts an integer, a 2-tuple (horizontal, vertical), or a 4-tuple (left, top, right, bottom).

margin_x

Horizontal external spacing. Accepts an integer or (left, right).

margin_y

Vertical external spacing. Accepts an integer or (top, bottom).

API#

The complete reference for Calendar lives on the Widgets API page. At a glance:

Calendar

An inline calendar for single-date or date-range selection.

Full Example#

 1
 2with bs.App(title="Calendar Demo", padding=20, gap=16) as app:
 3
 4    # Single mode
 5    bs.Label("Single Select", font="heading-sm")
 6    bs.Calendar(value=date(2026, 5, 15))
 7
 8    # Range mode
 9    bs.Label("Range Select", font="heading-sm")
10    bs.Calendar(
11        selection_mode="range",
12        start_date=date(2026, 5, 8),
13        end_date=date(2026, 5, 22),
14    )
15
16app.run()