A comprehensive calendar and scheduling component library for Phoenix LiveView.
PhoenixLiveCalendar provides server-rendered calendar views (month, week, day, year, N-day, agenda, timeline, resource) with optional JavaScript hooks for drag interactions, optional PubSub for real-time sync, and optional Ecto persistence.
Quick start
# In your LiveView template
<.live_component
module={PhoenixLiveCalendar.CalendarComponent}
id="my-calendar"
events={@events}
on_date_select={fn date -> send(self(), {:date_selected, date}) end}
on_event_click={fn id -> send(self(), {:event_clicked, id}) end}
/>Architecture
PhoenixLiveCalendar is built in layers, each optional:
- Layer 0 — Pure Elixir/HEEx views (zero JS required)
- Layer 1 — JS hooks for drag-to-select, drag-to-move, resize
- Layer 2 — PubSub for real-time multi-user updates
- Layer 3 — Booking constraints (availability, slots, buffers)
- Layer 4 — Ecto persistence (optional, Oban-style migrations)
Views
| View | Module | Description |
|---|---|---|
| Month | PhoenixLiveCalendar.Views.MonthGrid | Traditional 42-cell grid |
| Week | PhoenixLiveCalendar.Views.WeekGrid | 7 days with time axis |
| Day | PhoenixLiveCalendar.Views.DayView | Single day time grid |
| N-day | PhoenixLiveCalendar.Views.NDayView | Configurable day count |
| Year | PhoenixLiveCalendar.Views.YearView | 12 mini-months |
| Agenda | PhoenixLiveCalendar.Views.Agenda | Chronological list |
| Timeline | PhoenixLiveCalendar.Views.Timeline | Horizontal time, resource rows |
| Resource | PhoenixLiveCalendar.Views.ResourceView | Resource columns |
View maturity
All eight views render server-side and are usable today. Month is the most polished — it is the primary view and the one tuned for small screens. The remaining views (week, day, N-day, year, agenda, timeline, resource) are functional but less refined; in particular the time-grid views are not yet optimised for phone widths. Expect them to gain polish in later releases.
Data structures
PhoenixLiveCalendar.Event— Calendar eventsPhoenixLiveCalendar.Resource— Rooms, people, equipmentPhoenixLiveCalendar.Availability— Business hours, available windowsPhoenixLiveCalendar.BookingConfig— Slot constraints and rules
CSS setup
Add to your assets/css/app.css:
@source "../../deps/phoenix_live_calendar";JS hooks setup (optional)
Add to your assets/js/app.js:
import "../../deps/phoenix_live_calendar/priv/static/assets/phoenix_live_calendar.js"
let liveSocket = new LiveSocket("/live", Socket, {
hooks: { ...window.PhoenixLiveCalendarHooks, ...Hooks }
})
Summary
Functions
Creates an availability window.
Creates a booking configuration.
Creates a day marker (date annotation).
Creates a new event struct.
Returns whether PhoenixLiveCalendar CSS integration has been wired up.
Creates a new resource struct.
Converts a list of items to events using the Eventable protocol.
Functions
@spec availability([integer()] | Date.t(), Time.t(), Time.t(), keyword()) :: PhoenixLiveCalendar.Availability.t()
Creates an availability window.
Examples
# Monday through Friday, 9am to 5pm
PhoenixLiveCalendar.availability([1, 2, 3, 4, 5], ~T[09:00:00], ~T[17:00:00])
# Specific date override
PhoenixLiveCalendar.availability(~D[2026-04-15], ~T[10:00:00], ~T[14:00:00])
@spec booking_config(keyword()) :: PhoenixLiveCalendar.BookingConfig.t()
Creates a booking configuration.
Examples
PhoenixLiveCalendar.booking_config(duration: 30, buffer_after: 5, min_notice: 60)
@spec day_marker(term(), String.t(), Date.t(), keyword()) :: PhoenixLiveCalendar.DayMarker.t()
Creates a day marker (date annotation).
Examples
# Holiday
PhoenixLiveCalendar.day_marker("xmas", "Christmas Day", ~D[2026-12-25], type: :holiday, available: false)
# Multi-day notice
PhoenixLiveCalendar.day_marker("winter", "Winter Hours", ~D[2026-12-20],
end_date: ~D[2027-01-05],
type: :notice,
color: "bg-info/10",
description: "Reduced hours: 10am-3pm"
)
@spec event(term(), Date.t() | DateTime.t() | NaiveDateTime.t(), keyword()) :: PhoenixLiveCalendar.Event.t()
Creates a new event struct.
Examples
PhoenixLiveCalendar.event("1", ~D[2026-04-01], title: "My Event")
PhoenixLiveCalendar.event("2", ~U[2026-04-01 10:00:00Z], title: "Meeting", end: ~U[2026-04-01 11:00:00Z])
@spec installed?() :: boolean()
Returns whether PhoenixLiveCalendar CSS integration has been wired up.
Looks for the package name in the common app.css locations and in any
assets/css/*.css — so it also recognises a generated Tailwind sources file
(e.g. PhoenixKit's _phoenix_kit_sources.css, which @sources the package
automatically). Used at compile time to warn developers who haven't run
mix phoenix_live_calendar.install and aren't wiring it some other way.
@spec resource(term(), String.t(), keyword()) :: PhoenixLiveCalendar.Resource.t()
Creates a new resource struct.
Examples
PhoenixLiveCalendar.resource("room-a", "Conference Room A")
PhoenixLiveCalendar.resource("dr-smith", "Dr. Smith", type: :person, color: "bg-accent")
@spec to_events([term()]) :: [PhoenixLiveCalendar.Event.t()]
Converts a list of items to events using the Eventable protocol.
Items that already are PhoenixLiveCalendar.Event structs pass through unchanged.