# Calendar

> A month grid to pick a day, a range or several days, always on screen. A Pyzed component for Python desktop GUIs, with examples and its API.

Source: https://albakore.github.io/pyzed/library/calendar/

:::tip[In plain words]
A month on screen, like a wall calendar, where you click days to choose them.
:::

`ui.Calendar` shows one or more months. `mode` picks a single day (`"single"`), a range (`"range"`, two clicks) or any number of days (`"multiple"`, each click toggles one). The header switches to a grid of months and then of years to jump far, and the arrows of the keyboard move the focused day (Page Up/Page Down by month, Enter picks). The pickers on this page open one of these in a popup.

`ui.Calendar` · Category: [Dates and times](https://albakore.github.io/pyzed/library/#dates-and-times) · [Full reference →](https://albakore.github.io/pyzed/reference/ui/#calendar)

**Code:**

```python
pz.row([
    ui.Calendar(value="2026-10-22", today_button=True, on_change=on_change),
    ui.Calendar(value=("2026-10-05", "2026-10-14"), mode="range", disabled=["weekend"], on_change=on_change),
], spacing=24)
```

:::note[New]
This component was added recently. Tell us how it works for you in the [issue tracker](https://github.com/albakore/pyzed/issues).
:::

## When to use

- In a side panel or a dashboard, where the calendar stays visible.
- `first_weekday` (`"monday"`, `"sunday"`...) and `week_numbers=True` follow local customs.
- Inside a form field, use [Date picker](https://albakore.github.io/pyzed/library/date-picker/) instead.

## Usage

```python
import pyzed as pz
from pyzed import ui

class Planner(pz.App):
    def __init__(self):
        self.days = []

    def view(self):
        return pz.row([
            ui.Calendar(value=self.days, mode="multiple", week_numbers=True,
                        on_change=lambda e: setattr(self, "days", e.value)),
            pz.column([ui.label(ui.format_date(d, "ddd D MMM")) for d in self.days], spacing=4),
        ], spacing=16)
```

## Variants

:::note[About these captures]
Each capture is recorded by pyzed itself, off-screen at 2x, in One Dark or One Light to match this site's theme, and cropped to the component so it shows at its real size. The pointer hovers, presses or types into it; a component that does not react to the pointer cycles through a few themes. With reduced motion you see the first frame. Open **Code** under a capture to see the snippet that draws it; free names such as `on_save` stand for your own callbacks.
:::

### Mode

```python
ui.Calendar(value="2026-10-22", on_change=on_change)
```

```python
ui.Calendar(value=("2026-10-05", "2026-10-14"), mode="range", on_change=on_change)
```

```python
ui.Calendar(value=["2026-10-06", "2026-10-13", "2026-10-20", "2026-10-27"], mode="multiple", on_change=on_change)
```

### Options

```python
ui.Calendar(value="2026-10-22", week_numbers=True, first_weekday="monday", on_change=on_change)
```

```python
ui.Calendar(value="2026-10-22", first_weekday="sunday", on_change=on_change)
```

```python
ui.Calendar(value="2026-10-22", min="2026-10-08", max="2026-10-28", disabled=["weekend"], on_change=on_change)
```

```python
ui.Calendar(value="2026-10-22", show_outside=False, today_button=True, on_change=on_change)
```

### Views

A click on the title goes to the months, and another one to the years.

```python
ui.Calendar(value=("2026-10-26", "2026-11-04"), mode="range", months=2, on_change=on_change)
```

```python
ui.Calendar(value="2026-10-22", on_change=on_change)
```

```python
ui.Calendar(value="2026-10-22", on_change=on_change)
```

## Events

Calendar has **2 events of its own**, listed below. Like every widget it also has the **18 common events** (`click`, `double_click`, `right_click`, `mouse_enter`, `drag`, `key_down`, `resize`…), attached with `.on(...)`: see [Events](https://albakore.github.io/pyzed/guides/events/) and [Events of every component](https://albakore.github.io/pyzed/library/events/).

It is a `pz.Component`, so it also raises `on_mount` and `on_unmount`, and its handlers get a `pz.Event` whose `value` holds the data.

| Event | Handler gets | When |
| --- | --- | --- |
| `on_change` | `(event)` | Receives `event.value` (a date, a tuple or a list). |
| `on_month` | `(event)` | Receives `event.value`, the `(year, month)` shown. |

**Handler gets** is what Pyzed passes to your callable: `()` means nothing. Any handler can also be a message (a value that is not callable), which goes to `update()`.

### Handling the events

The same events written three ways. **Imperative**: methods of the app change `self`. **Declarative**: the handlers produce messages and one `update()` decides. **Functional**: a pure function draws an immutable state and asks for changes with `set_state(...)`. See [Imperative, declarative and functional](https://albakore.github.io/pyzed/guides/code-styles/).

**Imperative:**

```python
class Planner(pz.App):
    def __init__(self):
        self.days = []

    def changed(self, event):            # on_change: the list of days ("multiple" mode)
        self.days = event.value

    def view(self):
        return pz.row([
            ui.Calendar(value=self.days, mode="multiple", on_change=self.changed),
            ui.label(f"{len(self.days)} days"),
        ], spacing=16)
```

**Declarative:**

```python
from dataclasses import dataclass

@dataclass
class Day:
    value: object

class Planner(pz.App):
    day = None

    def update(self, message):
        match message:
            case Day(value):
                self.day = value

    def view(self):
        return ui.Calendar(value=self.day, today_button=True, on_change=lambda e: Day(e.value))
```

**Functional:**

```python
from dataclasses import dataclass

@dataclass(frozen=True)
class State:
    stay: tuple = ("2026-10-05", "2026-10-09")

def view(state, set_state):
    return ui.Calendar(value=state.stay, mode="range", months=2, on_change=lambda e: set_state(stay=e.value))
```

## Properties and API

### `ui.Calendar`

```python
class Calendar
```

A component with a month to pick a day on (`mode="single"`), a range (`"range"`: the first click starts it, the second ends it) or several days (`"multiple"`). The title opens a grid of months, then of years. Once clicked it takes the keyboard: arrows move, Enter picks, Page Up/Down change month. See the [Calendar](https://albakore.github.io/pyzed/library/calendar/) page.

| Property | Default | Description |
| --- | --- | --- |
| `value` | `None` | A date (`datetime.date` or `"2026-10-22"`), a `(start, end)` pair or a list of dates, by `mode`. |
| `mode` | `"single"` | `"single"`, `"range"` or `"multiple"`. |
| `min` | `None` | The earliest day that can be picked. |
| `max` | `None` | The latest day that can be picked. |
| `disabled` | `()` | Days that cannot be picked: dates, weekday names (`"sunday"`) or `"weekends"`. |
| `first_weekday` | `"monday"` | `"monday"`, `"sunday"` or `"saturday"`. |
| `months` | `1` | Months shown side by side. |
| `week_numbers` | `False` | Shows the ISO week number of each row. |
| `show_outside` | `True` | Shows the days of the months around. |
| `today_button` | `False` | Adds a Today link. |

Events: `on_change`, `on_month` ([events](#events)).

[Full reference →](https://albakore.github.io/pyzed/reference/ui/#calendar)

## Related

- [Date picker](https://albakore.github.io/pyzed/library/date-picker/): A date field with a calendar that drops down, in the format you choose.
- [Date range picker](https://albakore.github.io/pyzed/library/date-range-picker/): Two dates in one field, with two months side by side and quick ranges.
