Back to Blog
Programming code displayed on a computer screen in a dark environment
Framework
September 20, 2026
10 min read

How to Add a Calendar to a Django Template (No jQuery, No Admin Hacks)

By SimpleCalendarJS Team

SimpleCalendarJS~18 KB gzipped · Zero dependencies · Any framework

You're building a Django app and you need a calendar in one of your templates. You search "Django calendar" and the results fall into three buckets: Django's admin date widget (a date picker, not a calendar), Python's HTMLCalendar (a static table with no interactivity), and django-scheduler (a full application with models, migrations, and FullCalendar bundled via CDN). None of these give you what you actually want — a lightweight, interactive event calendar you can drop into any Django template. Here's how to get there.

The Django calendar landscape

Django is a batteries-included framework, but the battery it ships for calendars is a small one. The ecosystem breaks down into three tiers:

Django's built-in calendar widget (AdminDateWidget) lets users pick a date in a form field. It lives in django.contrib.admin.widgets, depends on Django's bundled jQuery, and requires {{ form.media }} to inject the admin CSS and JS. It's a date picker for forms — it cannot display events, show week or day views, or handle click interactions on a grid.

Python-side calendar rendering (calendar.HTMLCalendar) generates a static HTML table from Python's standard library. Some developers extend it with Django template tags to render month grids server-side. This works for displaying a read-only month view, but there's no interactivity — no event click handlers, no view switching, no async data loading.

Full Django calendar apps like django-scheduler (1,300+ GitHub stars) provide models, views, recurring event support, and a FullCalendar 6 frontend. django-scheduler is the most feature-rich option on Django Packages with multi-calendar support, timezone handling, and iCal export. But it's an entire application — it adds models to your database, requires migrations, and its bundled templates load Bootstrap 5, Bootstrap Icons, and FullCalendar 6 via CDN, totalling over 200 KB of frontend assets before your own code runs.

ApproachWhat It DoesFrontend WeightInteractivity
AdminDateWidgetDate picker for forms~30 KB (admin JS/CSS)Date selection only
HTMLCalendarStatic month table0 KB (server-rendered)None
django-schedulerFull calendar app~200+ KB (CDN: Bootstrap + FullCalendar)Full — but tightly coupled
Vanilla JS libraryDrop-in calendar component~14 KB (SimpleCalendarJS)Full — decoupled from backend

Most Django developers don't need a calendar application with its own models. They need a calendar component that renders events from their existing models in an interactive month/week/day view.

The FullCalendar-in-Django path

FullCalendar is the most common answer to "add a calendar to Django." At roughly 680,000 weekly npm downloads, it's the dominant JavaScript calendar library. Here's what the integration looks like:

<!-- templates/calendar.html --> {% load static %} <!DOCTYPE html> <html> <head> <link href="https://cdn.jsdelivr.net/npm/fullcalendar@6.1/index.global.min.css" rel="stylesheet"> <script src="https://cdn.jsdelivr.net/npm/fullcalendar@6.1/index.global.min.js"></script> </head> <body> <div id="calendar"></div> <script> document.addEventListener('DOMContentLoaded', function () { var calendar = new FullCalendar.Calendar( document.getElementById('calendar'), { initialView: 'dayGridMonth', events: '/api/events/', } ); calendar.render(); }); </script> </body> </html>

This works. But note the trade-offs:

  • ~43 KB gzipped minimum for the core bundle — and that's just dayGridMonth. Adding week and day views (timeGridWeek, timeGridDay) requires the timegrid plugin, pushing it higher
  • CDN-loaded by default, which means your production app depends on an external service
  • If you self-host the files via Django's static/ directory, you're managing a multi-file JavaScript distribution alongside your Python code
  • Advanced features like resource scheduling require a premium license starting at $480/developer

The vanilla JS path (recommended)

A vanilla JavaScript calendar library needs nothing from Django except a <div> and a <script> tag. No special template tags, no form widgets, no admin CSS — just standard HTML that Django renders like any other template.

Here's how to add a calendar to a Django template using SimpleCalendarJS — ~14 KB gzipped, zero dependencies:

Step 1: Install and collect static files

npm install simple-calendar-js

Copy the dist files into your Django app's static directory:

mkdir -p myapp/static/myapp/vendor/simple-calendar-js cp node_modules/simple-calendar-js/dist/simple-calendar-js.min.js myapp/static/myapp/vendor/simple-calendar-js/ cp node_modules/simple-calendar-js/dist/simple-calendar-js.min.css myapp/static/myapp/vendor/simple-calendar-js/

Step 2: Create the Django view

# myapp/views.py from django.shortcuts import render def calendar_view(request): return render(request, 'myapp/calendar.html')
# myapp/urls.py from django.urls import path from . import views urlpatterns = [ path('calendar/', views.calendar_view, name='calendar'), ]

Step 3: Build the template

<!-- myapp/templates/myapp/calendar.html --> {% extends "base.html" %} {% load static %} {% block extra_css %} <link rel="stylesheet" href="{% static 'myapp/vendor/simple-calendar-js/simple-calendar-js.min.css' %}"> {% endblock %} {% block content %} <div id="calendar"></div> {% endblock %} {% block extra_js %} <script src="{% static 'myapp/vendor/simple-calendar-js/simple-calendar-js.min.js' %}"></script> <script> document.addEventListener('DOMContentLoaded', function () { var calendar = new SimpleCalendarJs(document.getElementById('calendar'), { defaultView: 'month', locale: 'en-US', enabledViews: ['month', 'week', 'day'], fetchEvents: async function (start, end) { var response = await fetch( '/api/events/?from=' + start.toISOString() + '&to=' + end.toISOString() ); return response.json(); }, onEventClick: function (event) { console.log('Event clicked:', event); }, onSlotClick: function (date) { console.log('Slot clicked:', date); }, }); }); </script> {% endblock %}

That's it. Standard Django template inheritance, standard {% static %} tags, standard JavaScript. No admin imports, no jQuery, no CDN dependency.

What this gives you

  • Month, week, and day views with a built-in toolbar for switching
  • Async event fetching — fetchEvents fires with the visible date range on every navigation, so you only load what's on screen
  • Click handlers — onEventClick for existing events, onSlotClick for empty time slots (wire it to a modal for creating events)
  • 34+ locales built in — pass locale: 'pt-BR' or locale: 'ja-JP' and the calendar renders in that language
  • No collectstatic surprises — two files in your static/ directory, both cache-friendly

Feeding Django model data to the calendar

The calendar needs events. In Django, you've probably got a model like this:

# myapp/models.py from django.db import models class Event(models.Model): title = models.CharField(max_length=200) start = models.DateTimeField() end = models.DateTimeField() description = models.TextField(blank=True)

Option A: JSON API view (recommended for dynamic calendars)

Create a simple Django view that returns events as JSON. No Django REST Framework required — JsonResponse handles it:

# myapp/views.py from django.http import JsonResponse from .models import Event def event_api(request): start = request.GET.get('from') end = request.GET.get('to') events = Event.objects.filter( start__gte=start, end__lte=end, ).values('id', 'title', 'start', 'end', 'description') return JsonResponse(list(events), safe=False)
# myapp/urls.py urlpatterns = [ path('calendar/', views.calendar_view, name='calendar'), path('api/events/', views.event_api, name='event-api'), ]

The calendar's fetchEvents calls /api/events/ with the visible date range. Django queries only the events in that window and returns JSON. No full-page reload, no template re-rendering.

Option B: Inline data via json_script (for static or pre-loaded calendars)

If your events don't change often, you can pass them directly from the view context using Django's json_script filter:

# myapp/views.py from django.shortcuts import render from .models import Event import json def calendar_view(request): events = list(Event.objects.all().values('id', 'title', 'start', 'end')) return render(request, 'myapp/calendar.html', {'events': events})
<!-- In the template --> {{ events|json_script:"event-data" }} <script> var events = JSON.parse( document.getElementById('event-data').textContent ); var calendar = new SimpleCalendarJs(document.getElementById('calendar'), { defaultView: 'month', events: events, }); </script>

The json_script filter safely serializes Python data into a <script type="application/json"> tag — no XSS risk from user-generated content, no manual JSON escaping.

Theming the calendar to match your Django app

SimpleCalendarJS uses CSS custom properties. Override them in your stylesheet or in a <style> block:

.uc-calendar { --cal-primary: #092e20; --cal-primary-dark: #0c3b2e; --cal-today-bg: #e8f5e9; --cal-font-size: 14px; }

Four lines and your calendar matches Django's signature green. For dark mode:

@media (prefers-color-scheme: dark) { .uc-calendar { --cal-bg: #1a1a2e; --cal-text: #e0e0e0; --cal-border: #2a2a3e; --cal-today-bg: #2a2a3e; } }

No deeply nested selector overrides, no !important flags, no fighting with admin CSS specificity.

Bundle size: what you're shipping to the browser

Django templates are server-rendered HTML — every kilobyte of JavaScript you add is payload the client downloads and parses. In an ecosystem where developers choose Django specifically because it's server-first and efficient, bloated frontend libraries undercut the architecture.

SetupGzipped SizeFiles to ManagejQuery Required
Django AdminDateWidget~30 KB0 (built-in)Yes (bundled)
FullCalendar 6 (CDN)~43 KB+0 (CDN)No
FullCalendar 6 (self-hosted)~43 KB+3+ JS/CSS filesNo
django-scheduler (full)~200+ KBCDN deps (Bootstrap + FC)No
SimpleCalendarJS~14 KB2 files (JS + CSS)No

SimpleCalendarJS delivers month, week, and day event views at ~14 KB gzipped — roughly one-third the size of FullCalendar's core and a fraction of django-scheduler's full frontend stack. Two files in your static/ directory, zero external dependencies, zero framework coupling.

When to use django-scheduler instead

django-scheduler is the right choice when you need a complete calendar application, not just a calendar component:

  • Recurring events with RRULE support — django-scheduler handles recurrence at the model level with an Occurrence API
  • Multi-calendar support — separate calendars for different teams or categories, managed through Django models
  • iCal export — built-in feed generation for subscribing from Google Calendar or Outlook
  • Admin integration — manage events through Django's admin interface with no custom views
  • Timezone handling — per-event timezone support baked into the models

If you need these backend features, django-scheduler's 1,300+ stars and active maintenance make it a solid choice. But if you already have your own event model and just need to display those events in an interactive calendar, adding an entire Django app with its own migrations is overhead you don't need.

Summary

  • Django's built-in calendar tools are either date pickers (AdminDateWidget) or static HTML tables (HTMLCalendar) — neither provides interactive event views
  • django-scheduler is a full calendar application with models, migrations, and 200+ KB of CDN-loaded frontend assets — powerful but heavy for simple use cases
  • FullCalendar works in Django templates but ships ~43 KB+ gzipped and requires managing multiple JS/CSS files or CDN dependencies
  • A vanilla JS calendar in a Django template needs only a <div>, a {% static %} tag, and a DOMContentLoaded listener — standard patterns every Django developer already knows
  • SimpleCalendarJS provides month, week, and day event views in ~14 KB gzipped with zero dependencies — two files in your static/ directory, a JSON view for events, and you're done
  • Use Django's json_script filter or a simple JsonResponse view to feed model data to the calendar — no Django REST Framework required

Sources & Further Reading

Research & References

Image Credits

All images free to use under the Pexels License.

Frequently Asked Questions

What is the best JavaScript calendar library for Django?

It depends on what you need. django-scheduler (1,300+ GitHub stars) bundles FullCalendar 6 and handles recurring events on the backend, but it pulls in Bootstrap 5, Bootstrap Icons, and FullCalendar via CDN — over 200 KB of frontend assets. For a lightweight event calendar with month, week, and day views, SimpleCalendarJS adds ~14 KB gzipped with zero dependencies and works in any Django template with a script tag and a div.

Can I use Django's built-in calendar widget outside the admin?

Technically yes — you can import AdminDateWidget from django.contrib.admin.widgets and use {{ form.media }} to inject the required JavaScript and CSS. But it's a date picker designed for admin forms, not an event calendar. It requires admin static files, depends on Django's bundled jQuery, and has no month/week/day event views.

Do I need jQuery for a calendar in Django?

No. Django's admin widgets depend on jQuery, and older versions of FullCalendar required it, but modern calendar libraries like SimpleCalendarJS are written in vanilla JavaScript with zero dependencies. You include the JS file, point it at a div, and it works.

How do I pass Django context data to a JavaScript calendar?

Use Django's built-in json_script template filter. In your view, pass a queryset serialized to a list of dicts. In the template, use {{ events|json_script:'event-data' }} to safely inject it as a JSON script tag. Then in JavaScript, read it with JSON.parse(document.getElementById('event-data').textContent).

Should I use CDN links or Django static files for calendar assets?

For production Django apps, use static files. Download or npm-install the library, place the dist files in your app's static/ directory, reference them with {% static %} tags, and run collectstatic for deployment. CDN links are fine for prototyping but add external dependencies, CORS considerations, and potential availability issues to production.

Is SimpleCalendarJS free to use in a Django project?

SimpleCalendarJS is free for personal and open-source projects. Commercial projects require a paid license — $49/year or $199 lifetime per project.

Add a calendar to your app today

Free for personal projects. $49/year or $199 lifetime per commercial project.