Skip to main content

skinnycal

A lightweight Plotly Dash wrapper around @fullcalendar/react, with the FullCalendar Premium (Scheduler) plugins statically bundled so resource views work out of the box.

Forked from dash-fullcalendar. See PREMIUM_FORK_CHANGES.md for the full rationale and diff notes.


Installation

pip install skinnycal

PyPI distribution and Python import name are both skinnycal.


Quick start

from dash import Dash, html
import skinnycal as dcal

app = Dash(__name__)

app.layout = html.Div([
    dcal.FullCalendar(
        id="cal",
        initialView="dayGridMonth",
        editable=True,
        selectable=True,
        events=[
            {"title": "Audit", "date": "2025-08-01"},
            {"title": "Go-Live", "date": "2025-08-10"},
        ],
    )
])

if __name__ == "__main__":
    app.run(debug=True)

Open http://127.0.0.1:8050 in your browser.

Premium (Scheduler) views

Pass a valid schedulerLicenseKey and request a resource view — the premium plugins are already in the bundle, no async chunk fetch:

dcal.FullCalendar(
    id="cal",
    schedulerLicenseKey="GPL-My-Project-Is-Open-Source",  # or your commercial key
    plugins=["resourceTimeline", "interaction"],
    initialView="resourceTimelineWeek",
    resources=[{"id": "a", "title": "Room A"}, {"id": "b", "title": "Room B"}],
    events=[{"resourceId": "a", "title": "Kickoff", "start": "2025-08-01"}],
)

Per-event decoration

Every event block gets data-event-id="<event id>" on its .fc-event root, so your own JS can address one block without reaching into FullCalendar internals. Per-block CSS classes and data-* attributes are driven by data — no JS callbacks needed:

dcal.FullCalendar(
    id="cal",
    events=[{
        "id": "bead-42",
        "title": "Module 3",
        "start": "2025-08-01",
        "extendedProps": {
            "classNames": ["is-conflict-hard"],   # -> classes on this block
            "trainer": "AB",                      # -> data-trainer, see below
        },
    }],
    eventDataAttributes=["trainer"],   # mirror these extendedProps keys as data-*
)
document.querySelector('[data-event-id="bead-42"]').dataset.trainer  // "AB"

Then style it however you like:

.fc-event.is-conflict-hard { border-color: #c0392b; }

Because FullCalendar re-evaluates event classes when an event's data changes, you can toggle state by returning an updated events list from a callback — no page reload, and no need to re-render the whole calendar yourself. eventDataAttributes keys are kebab-cased (courseCodedata-course-codeel.dataset.courseCode) and are applied when a block mounts; see PREMIUM_FORK_CHANGES.md for the caveat about imperative command mutations. usage.py is a runnable demo of all of this.


Repository layout

Path Purpose
skinnycal/ Python package published to PyPI. Contains generated Dash component classes and pre-compiled JS assets (_js_dist).
src/ Raw React source for the wrapper.
package.json, webpack.config.js JS build pipeline (npm run build).
usage.py Minimal Dash demo.
tests/ Integration tests with dash[testing] & pytest.
.github/workflows/ CI workflow that builds and publishes to PyPI on push to main.

Development

  1. Clone and install dependencies

    git clone https://github.com/PepijnWissing/skinnyCal.git
    cd skinnyCal
    npm install
    python -m venv .venv && . .venv/Scripts/activate   # POSIX: source .venv/bin/activate
    pip install -r requirements.txt
    
  2. Build and run the example

    npm run build        # webpack + dash-generate-components
    pip install -e .
    python usage.py      # open http://localhost:8050
    
  3. Run tests

    pytest -q
    

Releasing

Publishing is automated: any push to main triggers .github/workflows/publish.yml, which builds an sdist + wheel and uploads to PyPI via Trusted Publishing (OIDC). Pushes that don't bump the version are no-ops (skip-existing: true).

To cut a release, bump the version in all three places (they must stay in sync — __version__ is read from package-info.json at import time):

  • pyproject.toml[project].version
  • skinnycal/package-info.jsonversion
  • package.jsonversion

Then commit and push to main.


License

MIT © Scott Kilgore (upstream wrapper). Bundled @fullcalendar premium plugin code is governed by FullCalendar's own license — commercial production use requires a purchased Scheduler license.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

skinnycal-0.3.0.tar.gz (509.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

skinnycal-0.3.0-py3-none-any.whl (511.5 kB view details)

Uploaded Python 3

File details

Details for the file skinnycal-0.3.0.tar.gz.

File metadata

  • Download URL: skinnycal-0.3.0.tar.gz
  • Upload date:
  • Size: 509.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for skinnycal-0.3.0.tar.gz
Algorithm Hash digest
SHA256 0c396d57b785a79c22a64558ff9b3bfeb6be3efac502dda875776050e5f57a13
MD5 63a4fdabbf1cc45cf528bad98540ee33
BLAKE2b-256 db205dd3d94c28d7e090c2115a061c57f6c40e5b1b57f74da5ef7f044472e2e9

See more details on using hashes here.

Provenance

The following attestation bundles were made for skinnycal-0.3.0.tar.gz:

Publisher: publish.yml on PepijnWissing/skinnyCal

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file skinnycal-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: skinnycal-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 511.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for skinnycal-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f12c71e7ae81729f8a4cac54a0a864f8cdad0f71c4118906c1208780528dc714
MD5 4962e94fed815d4d4b9505d9248172f6
BLAKE2b-256 9113f85d2e86dbcc0a3475cfe76c920f07ef91e627a2139eed6902c03e412c44

See more details on using hashes here.

Provenance

The following attestation bundles were made for skinnycal-0.3.0-py3-none-any.whl:

Publisher: publish.yml on PepijnWissing/skinnyCal

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.4.0

2 files

This release

0.3.0 This release

2 files

0.2.0

2 files

0.1.7

2 files

0.1.6

2 files

0.1.5

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page