Skip to content

Interactive Calendar

An interactive calendar component built with useTemporal, demonstrating the powerful stableMonth pattern for creating consistent calendar grids.

Live Demo

November 2025

Mon
Tue
Wed
Thu
Fri
Sat
Sun
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7

How It Works

This calendar component uses several key features of useTemporal:

1. Stable Month Pattern

The calendar uses createStableMonth() which creates a consistent 42-day (6-week) grid. This ensures the calendar layout never jumps in height when navigating between months.

typescript
import { createStableMonth } from '@allystudio/usetemporal/calendar'

// Creates a 6-week grid containing the month
const stableMonth = createStableMonth(adapter, 1, new Date())

2. Navigation

Navigate between months using simple date manipulation:

typescript
const nextMonth = () => {
  currentMonth.value = new Date(
    currentMonth.value.getFullYear(),
    currentMonth.value.getMonth() + 1,
    1
  )
}

3. Day Division

The stable month is divided into individual days using the divide() operation:

typescript
import { divide } from '@allystudio/usetemporal/operations'

// Get all 42 days from the stable month
const days = divide(adapter, stableMonth, 'day')

Key Features

  • Stable Grid: Always shows 6 weeks (42 days) for consistent layout
  • Native Adapter: Uses only native JavaScript Date operations
  • Reactive: Built with Vue's reactivity system
  • Accessible: Keyboard navigation support
  • Weekend Highlighting: Automatically highlights weekends
  • Today Indicator: Current day is clearly marked

Component Structure

The calendar component follows best practices:

  1. Level 1 API (Pure Functions): Uses @allystudio/usetemporal/operations for tree-shakable imports
  2. Native Adapter: Zero dependencies beyond the library itself
  3. Composable Pattern: Easy to integrate into any Vue application

Implementation Code

Here's the complete implementation:

vue
<template>
  <div class="calendar-view">
    <div class="calendar-header">
      <button @click="previousMonth">‹</button>
      <h3>{{ monthName }}</h3>
      <button @click="nextMonth">›</button>
    </div>

    <div class="calendar-grid">
      <!-- Weekday headers -->
      <div v-for="day in weekDays" :key="day" class="weekday-header">
        {{ day }}
      </div>

      <!-- Calendar days -->
      <div
        v-for="(day, index) in days"
        :key="index"
        :class="getDayClasses(day)"
      >
        {{ day.date.getDate() }}
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue'
import { createTemporal } from '@allystudio/usetemporal'
import { createNativeAdapter } from '@allystudio/usetemporal/native'
import { createStableMonth } from '@allystudio/usetemporal/calendar'
import { divide } from '@allystudio/usetemporal/operations'

// Create temporal instance with native adapter
const adapter = createNativeAdapter()
const temporal = createTemporal({ adapter, weekStartsOn: 1 })

// Current browsing month
const currentMonth = ref<Date>(new Date())

// Weekday names
const weekDays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']

// Month name for header
const monthName = computed(() =>
  currentMonth.value.toLocaleDateString('en', {
    month: 'long',
    year: 'numeric'
  })
)

// Create stable month period (42 days / 6 weeks)
const stableMonth = computed(() =>
  createStableMonth(adapter, 1, currentMonth.value)
)

// Divide stable month into days
const days = computed(() =>
  divide(adapter, stableMonth.value, 'day')
)
</script>

Why Use Stable Month?

Traditional calendar implementations often have issues:

  1. Variable Height: Different months have different numbers of weeks (4-6), causing layout shifts
  2. Complex Logic: Calculating which days from adjacent months to show
  3. Edge Cases: Handling months that start on different days of the week

The stableMonth pattern solves all of these by providing a consistent 42-day grid that:

  • Always shows exactly 6 weeks
  • Includes necessary days from previous/next months
  • Maintains consistent layout across all months

Next Steps

Bundle Size

This implementation is extremely lightweight:

  • Core library: ~5-7KB
  • Native adapter: ~1KB
  • Calendar utilities: ~2KB
  • Total: ~8-10KB minified + gzipped

Perfect for documentation sites and applications where bundle size matters!

Released under the MIT License.