Built with Vue 3, TypeScript, Tailwind CSS v4, and Reka UI
- ποΈ Multiple Calendar Views: Month, Week, Day, and Agenda views
- β° Hourly Time Slots: Detailed scheduling with hour-by-hour time slots in Week and Day views
- π Event Management: Full event CRUD operations with props-based event handling
- π¨ Modern Styling: Built with Tailwind CSS v4 and accessible Reka UI components
- π§ TypeScript Support: Full type safety with comprehensive type definitions
- π± Responsive Design: Optimized for desktop, tablet, and mobile devices
- π High Performance: Efficient rendering for large event datasets
- π§ͺ Well Tested: Comprehensive Storybook stories and examples
- π― Smart Agenda View: Automatically filters and shows only dates with events
- π¨ Customizable Events: Support for multiple colors and event categories
- οΏ½ NPM Ready: Easy installation and integration into existing Vue 3 projects
npm install vue3-event-calendar
# or
yarn add vue3-event-calendar
# or
pnpm add vue3-event-calendar<template>
<div class="calendar-container">
<EventCalendar
:events="events"
:view="currentView"
:editable="true"
@event-click="handleEventClick"
@date-click="handleDateClick"
@event-created="handleEventCreated"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { EventCalendar } from 'vue3-event-calendar'
import type { CalendarEvent } from 'vue3-event-calendar'
// Import the CSS
import 'vue3-event-calendar/dist/index.css'
const currentView = ref<'month' | 'week' | 'day' | 'agenda'>('month')
const events = ref<CalendarEvent[]>([
{
id: 1,
title: 'Team Meeting',
start: new Date(2025, 9, 7, 10, 0), // October 7, 2025 at 10:00 AM
end: new Date(2025, 9, 7, 11, 0), // October 7, 2025 at 11:00 AM
color: 'blue',
description: 'Weekly team sync meeting',
},
{
id: 2,
title: 'Project Deadline',
start: new Date(2025, 9, 8),
allDay: true,
color: 'red',
description: 'Final submission deadline',
},
])
const handleEventClick = (eventData) => {
console.log('Event clicked:', eventData.event)
}
const handleDateClick = (dateData) => {
console.log('Date clicked:', dateData.date)
}
const handleEventCreated = (newEvent) => {
events.value.push(newEvent)
}
</script>Perfect for getting an overview of the entire month.
- Traditional calendar grid layout
- Multiple events displayed per day
- Easy month-to-month navigation
- Event overflow indicators for busy days
Detailed weekly planning with hourly precision.
- 7-day horizontal layout with hourly time slots
- Events positioned by exact start/end times
- Drag and drop support for event scheduling
- Perfect for detailed weekly planning
Focus on a single day with maximum detail.
- Hourly timeline from 12 AM to 11 PM
- Full event information display
- Ideal for daily planning and time blocking
- Click time slots to create new events
Clean, list-style view of upcoming events.
- Chronological event listing
- Shows only dates that have events
- Event details with times and descriptions
- Great for event overview and planning
interface CalendarEvent {
id: string | number // Unique identifier
title: string // Event title
start: Date // Start date and time
end?: Date // End date and time (optional)
allDay?: boolean // All-day event flag
color?: string // Event color theme
description?: string // Event description
category?: string // Event category/type
}blue- Default professional bluered- Important/urgent eventsgreen- Success/completed eventsyellow- Warning/pending eventspurple- Personal/creative eventsgray- Default/neutral events
const sampleEvents = [
{
id: 'meeting-1',
title: 'Daily Standup',
start: new Date(2025, 9, 7, 9, 0),
end: new Date(2025, 9, 7, 9, 30),
color: 'blue',
description: 'Daily team synchronization',
category: 'meeting',
},
{
id: 'deadline-1',
title: 'Project Submission',
start: new Date(2025, 9, 10),
allDay: true,
color: 'red',
description: 'Final project deliverable due',
category: 'deadline',
},
{
id: 'break-1',
title: 'Lunch Break',
start: new Date(2025, 9, 7, 12, 0),
end: new Date(2025, 9, 7, 13, 0),
color: 'yellow',
description: 'Team lunch at the new restaurant',
},
]interface CalendarProps {
events?: CalendarEvent[] // Array of events to display
view?: 'month' | 'week' | 'day' | 'agenda' // Current calendar view
date?: Date // Currently displayed date
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 // First day of week (0 = Sunday)
selectable?: boolean // Enable date selection
editable?: boolean // Enable event editing
eventLimit?: number // Max events per day in month view
height?: string | number // Calendar container height
}// Available event emitters
@date-click="(event: DateClickEvent) => void" // User clicks on a date
@event-click="(event: EventClickEvent) => void" // User clicks on an event
@event-created="(event: CalendarEvent) => void" // New event created
@event-updated="(event: CalendarEvent) => void" // Event modified
@event-deleted="(id: string | number) => void" // Event deleted
@view-change="(view: string) => void" // Calendar view changed
@date-change="(date: Date) => void" // Displayed date changed<template>
<div class="calendar-app">
<!-- View Controls -->
<div class="view-controls mb-4">
<button
v-for="view in views"
:key="view.value"
@click="currentView = view.value"
:class="[
'px-4 py-2 rounded-lg font-medium transition-all duration-200',
currentView === view.value
? 'bg-blue-500 text-white shadow-md'
: 'bg-gray-100 text-gray-700 hover:bg-gray-200',
]"
>
{{ view.label }}
</button>
</div>
<!-- Calendar -->
<EventCalendar
:events="events"
:view="currentView"
:editable="true"
@view-change="currentView = $event"
@event-created="addEvent"
@event-updated="updateEvent"
@event-deleted="deleteEvent"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const currentView = ref('month')
const views = [
{ value: 'month', label: 'π
Month' },
{ value: 'week', label: 'π Week' },
{ value: 'day', label: 'π Day' },
{ value: 'agenda', label: 'π Agenda' },
]
// Event management
const addEvent = (event) => {
events.value.push({ ...event, id: Date.now() })
}
const updateEvent = (updatedEvent) => {
const index = events.value.findIndex((e) => e.id === updatedEvent.id)
if (index > -1) events.value[index] = updatedEvent
}
const deleteEvent = (eventId) => {
events.value = events.value.filter((e) => e.id !== eventId)
}
</script><script setup lang="ts">
import { ref } from 'vue'
import { EventCalendar } from 'vue3-event-calendar'
const events = ref([])
const handleDateClick = (dateInfo) => {
// Create event when clicking on empty date
const newEvent = {
id: `event-${Date.now()}`,
title: 'New Event',
start: dateInfo.date,
end: new Date(dateInfo.date.getTime() + 60 * 60 * 1000), // 1 hour duration
color: 'blue',
description: 'Click to edit this event',
}
events.value.push(newEvent)
}
const handleEventClick = (eventInfo) => {
// Handle event editing
const event = eventInfo.event
const newTitle = prompt('Edit event title:', event.title)
if (newTitle !== null) {
const index = events.value.findIndex((e) => e.id === event.id)
if (index > -1) {
events.value[index] = { ...event, title: newTitle }
}
}
}
</script>
<template>
<EventCalendar
:events="events"
:editable="true"
@date-click="handleDateClick"
@event-click="handleEventClick"
/>
</template>Make sure to import the component styles:
/* In your main CSS file or component */
@import 'vue3-event-calendar/dist/index.css';The component uses Tailwind CSS classes that can be customized:
/* Custom calendar container */
.vue-calendar {
border-radius: 12px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
/* Custom event styling */
.calendar-event {
transition: all 0.2s ease;
}
.calendar-event:hover {
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}- Node.js 18+
- Vue 3.3+
- TypeScript 5+
# Clone the repository
git clone https://github.com/your-username/vue3-event-calendar.git
cd vue3-event-calendar
# Install dependencies
pnpm install
# Start development server
pnpm dev
# Start Storybook for component development
pnpm storybook
# Build for production
pnpm buildThe package includes comprehensive Storybook stories demonstrating all features:
pnpm storybookVisit the Storybook to see:
- All calendar views in action
- Event interaction examples
- Responsive behavior demonstrations
- Customization options
EventCalendar- Main calendar componentCalendar- Basic calendar without event managementBaseCalendar- Core calendar functionality
useCalendar- Calendar state managementuseCalendarGrid- Grid layout calculationsuseCalendarEvents- Event management utilities
All TypeScript definitions are included and exported for your convenience.
We welcome contributions! Please see our Contributing Guidelines for details.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
MIT License - see the LICENSE file for details.
- Built with Vue 3
- Styled with Tailwind CSS v4
- UI components from Reka UI
- Date utilities from date-fns
- Icons from Lucide Vue
Made with β€οΈ for the Vue.js community
VS Code + Vue (Official) (and disable Vetur).
- Chromium-based browsers (Chrome, Edge, Brave, etc.):
- Firefox:
TypeScript cannot handle type information for .vue imports by default, so we replace the tsc CLI with vue-tsc for type checking. In editors, we need Volar to make the TypeScript language service aware of .vue types.
See Vite Configuration Reference.
pnpm installpnpm devpnpm buildLint with ESLint
pnpm lint