vkurko/calendar keeps the view in a package of its own
Full-sized drag & drop JavaScript event calendar with resource & timeline views
At a glance
- What is it?
- Every view of this calendar ships as a separate npm package, so a booking screen that needs resource rows installs more than one. Option names track FullCalendar, the build emits an ES module and an IIFE bundle, and the 35kb brotli figure is tied to neither.
- Who is it for?
- Adopt this calendar when the booking screen needs resource rows, drag and drop, and a bundle you control end to end. Installation is one npm command plus one view package, and the option list covers the scheduling mechanics in real depth.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 21, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Resource rows turn a week grid into a booking screen
A booking screen rarely shows one column of appointments. Rooms, staff, vehicles or exam halls each get a lane, and the calendar has to place events inside that lane. That is the case vkurko/calendar targets: a week or day grid whose vertical axis can be split by resource. Layout is done with CSS Grid, which the README credits for keeping the DOM minimal, and the shipped bundle is described as 35kb brotli compressed with zero dependencies. The audience is product teams that want the grid itself rather than a scheduling backend, and the README states the component runs on over 70,000 websites, naming the WordPress plugin Bookly as one adopter.
Two kinds of resource layout exist. `ResourceTimeline` spreads resources as horizontal lanes across a time axis, which suits a day of bookings read left to right. `ResourceTimeGrid` keeps the familiar time grid and adds resource columns, which suits a week where each person has a stacked column. Neither is the default, and the split is the whole reason the packages exist separately.
One npm command, and the view is still missing
Installation starts with a single command, and the flag it carries deserves a close look:
npm install --save-dev @event-calendar/core`--save-dev` writes the package into devDependencies, the section a repository keeps build tooling in rather than the things its own pages load at runtime. For a library that your bundler inlines into an application that is a defensible choice, and it matches the repository root, which is marked `"private": true` so that root is never published under its own name. What reaches npm are scoped packages instead: `@event-calendar/core` for the calendar functions and view plugins, and `@event-calendar/build` for the built output, the name behind the jsDelivr badge at the top of the README.
After that install there is still no grid on the page. The core package carries no view of its own, and you must add at least one view plugin, currently `DayGrid`, `List`, `ResourceTimeline`, `ResourceTimeGrid` and `TimeGrid`. The README also documents a Svelte 5 component as a third consumption path next to the JavaScript module and the standalone bundle.
Two build passes, one headline size number
The repository root is a private monorepo with `svelte.config.js` and `vite.config.js` beside `packages/`, `scripts/` and `docs/`. Its `package.json` names the project `event-calendar`, carries the version 5.15.0 and runs two builds from a single script:
"build": "vite build && vite build --mode iife",
"preview": "vite preview",The second pass, run in `iife` mode, is what produces the standalone bundle that is documented as an alternative to the module. Build tooling comes to four packages, all development-only:
"@sveltejs/vite-plugin-svelte": "^7.3.0",
"svelte": "^5.57.0",
"vite": "^8.2.2",
"vite-plugin-banner": "^0.8.1"Svelte 5.57.0 and Vite 8.2.2 do that building, and both sit in the same development-only block. Zero dependencies describes what a consumer pulls in, not what this repository installs for itself. Nothing in that script list measures a file, and the 35kb brotli figure that opens the feature list is not attributed to either of the two outputs, so read it as a headline number rather than a measurement attached to an artifact you can check.
Option names grouped by the axis they turn
The option surface is the real interface, and the names cluster by what they change. Geometry of the time area comes down to `columnWidth`, `slotWidth`, `slotHeight`, `slotMinTime`, `slotMaxTime`, `slotDuration` and `scrollTime`. Which days exist at all, and the unit they step in, comes from `firstDay`, `hiddenDays`, `validRange`, `date`, `dateIncrement`, `duration` and `weekNumbers`. Labels are a group of their own: `slotLabelInterval`, `dayHeaderFormat`, `slotLabelFormat`, `monthHeaderFormat`, `eventTimeFormat`, `titleFormat`, `weekNumberContent`, `dayCellFormat`, `noEventsContent`, `allDayContent` and `moreLinkContent`.
Chrome sits in a group again. `height`, `theme`, `icons`, `locale`, `timeZone`, `buttonText`, `customButtons`, `headerToolbar`, `customScrollbars`, `nowIndicator` and `dayMaxEvents` decide how the grid looks around its events rather than the events themselves. Read across the list and the shape of a scheduler emerges: widths and slot lengths decide how much a screen can hold, format strings decide what a cell says, and the remainder are switches. Nothing in the grouping tells you the accepted value shape for any one of them, so the demo is where the types get pinned down.
Distance, delay and constraint for every drag
Every interaction has its own threshold option, and that density is what makes one build work on a mouse and on a finger. `eventDragMinDistance` sets how far an event travels before a drag is recognised, `selectMinDistance` does the same job for range selection, and `eventLongPressDelay`, `selectLongPressDelay` and `longPressDelay` set the hold time that stands in for dragging on touch. Constraints come in three separately named flavours: `dragConstraint` for moving, `resizeConstraint` for resizing and `selectConstraint` for a selected range, with `snapDuration` and `flexibleSlotTimeLimits` covering how a time range snaps inside a slot.
Editing rights are split in the same careful way, through `editable`, `selectable`, `eventStartEditable`, `eventDurationEditable` and `eventResizableFromStart`, so a read-only grid and a half-editable one are one flag apart rather than a fork. `slotEventOverlap` decides what two events sharing a slot do to each other. Persisting a change means implementing callbacks: `eventDragStart`, `eventDragStop`, `eventDrop`, `eventResize`, `eventResizeStart` and `eventResizeStop` hand you the new position, and `select`, `unselect` and `unselectCancel` cover the range case, with `unselectAuto` deciding whether a click elsewhere clears the selection by itself.
Events and resources parsed from plain objects
Data crosses the boundary as plain JavaScript objects, and each kind gets a parsing section of its own: an event object with a section on parsing an event from a plain object, a duration object with one on parsing duration from input values, a resource object with a matching section, and a view object. On the way in the options are `events`, `eventSources`, `resources` and `views`, with `resourceExpand` and `datesAboveResources` controlling how the resource axis is drawn.
The method list covers the rest of the lifecycle. `addEvent`, `updateEvent`, `removeEventById`, `getEventById` and `getEvents` read and mutate one calendar. `refetchEvents` and `refetchResources` ask the sources for data again, which is the hook a cache invalidator needs. `dateFromPoint` converts pixel coordinates into a date, useful when you place a marker by pointer position rather than by click target. Navigation runs through `gotoDate`, `prev`, `next` and `getView`, and `getOption` with `setOption` change a single value after construction, with a README heading devoted to modifying options after initialisation.
Fetching is governed by `lazyFetching` and the `loading` flag, while `refetchResourcesOnNavigate` decides whether the resource list is pulled again on every date change. That is the option to watch on a booking screen with many rooms: leave it on and each navigation costs a request, turn it off and a renamed room keeps its old row until you call `refetchResources` yourself.
Gaps in the documentation you hit early
A few things a scheduler needs are named in the documentation's table of contents and then left unstated in the body. A Browser support heading exists, and no browser or version numbers appear with it. A Theming heading and a `theme` option exist, and no theme names or CSS custom properties are listed beside them, so a dark mode built on a named theme is not something you can assemble from what is written. A Svelte 5 component is documented as a third way to consume the package, and no React or Vue wrapper appears anywhere in the documentation.
Set against those gaps, the code is MIT licensed and redistributable inside a product you ship, with no copyleft obligation attached to your own source. The last push landed on 2026-09-25, close enough to today that option names and plugin names are worth pinning in `package.json` rather than tracking from a floating range. The option names themselves are the other thing to verify early: there are far more of them than any README section explains one by one, so treat the demo at the published homepage as the specification.
FullCalendar option names, different packaging model
FullCalendar is named as the inspiration, and similar options are implemented rather than re-invented. The option list is full of names of that kind: `slotDuration`, `slotLabelFormat`, `allDaySlot`, `headerToolbar`, `hiddenDays`, `eventSources`, `lazyFetching`, `buttonText`, `customButtons`, `dateClick` and `viewDidMount`. An existing FullCalendar configuration is therefore the nearest thing to a migration path available, and the real difference in approach is packaging rather than naming.
Here a view is an install step that can be added one at a time, the standalone bundle comes out of a second build pass rather than a separate published entry point, and the root of the repository is a private workspace with its own Svelte and Vite setup that a consumer never sees. What you do not get is guidance: no compatibility table, no list of renamed or dropped options, and no statement that every name from the older library works unchanged. Diff your own option object against the list before planning a swap, because a familiar name is not the same thing as identical behaviour.
Editorial conclusion
Adopt this calendar when the booking screen needs resource rows, drag and drop, and a bundle you control end to end. Installation is one npm command plus one view package, and the option list covers the scheduling mechanics in real depth. Pass on anything that needs a documented migration path, named themes or stated browser support, because none of those appear. Check first that the view plugin you need still matches the version you pin in `package.json`, then wire `dateClick` and `eventDrop` to your own persistence before you ship.
Frequently asked questions
Which npm packages do I need to install vkurko/calendar?
Install `@event-calendar/core` with `npm install --save-dev @event-calendar/core`, then add at least one view plugin, since the core package contains no view of its own. The documented plugins are `DayGrid`, `List`, `ResourceTimeline`, `ResourceTimeGrid` and `TimeGrid`.
Can I change options after the vkurko/calendar instance is created?
Yes. `setOption` changes a single value by name after construction and `getOption` reads one back, and the documentation has a heading on modifying options after initialisation covering the same ground.
How do I add, update or remove an event in vkurko/calendar?
The method list gives `addEvent`, `updateEvent` and `removeEventById` for changes, with `getEventById` and `getEvents` for reading. `refetchEvents` and `refetchResources` exist for pulling data from your sources again after a change is stored.
How large is the vkurko/calendar bundle?
The README describes the shipped bundle as 35kb brotli compressed with zero dependencies, built on a minimal DOM structure thanks to CSS Grid. That figure is not attributed to a specific output, and the repository builds both an ES module and an IIFE bundle.
Does vkurko/calendar need a framework?
No. Three consumption paths are documented: the JavaScript module from `@event-calendar/core`, a standalone bundle, and a Svelte 5 component. No React or Vue wrapper appears in the documentation.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/vkurko-calendar)Was this useful?