Skip to content

<CoarDayView> — Day View Preview

Single-day time-grid surface — one hour-axis on the left, one day column on the right. Multi-day all-day events that touch the visible day appear in the all-day band that pins under the day header. Use it standalone via useDayView() when you need just the day view without the <CoarCalendar> shell.

html
<CoarDayView :builder="builder" />

Standalone usage

ts
import { ref } from 'vue';
import { Temporal } from '@js-temporal/polyfill';
import {
  CoarDayView,
  useDayView,
  type CalendarEvent,
} from '@cocoar/vue-calendar';

const events = ref<CalendarEvent[]>([
  {
    id: 'standup',
    start: Temporal.ZonedDateTime.from('2026-04-15T09:00:00[UTC]'),
    end:   Temporal.ZonedDateTime.from('2026-04-15T09:15:00[UTC]'),
  },
]);
const date = ref('2026-04-15');

const { builder, api } = useDayView();
builder
  .events(events)
  .date(date)
  .timezone('UTC')
  .timeRange([8, 18])
  .slotDuration(15)
  .onTimeClick(({ time }) => console.log(time.toString()));
html
<CoarDayView :builder="builder" />

The Day view shares its builder type, CalendarBuilder, with the Week view — they differ only in the days array the wrapper computes (Day uses [date], Week uses weekDates(date, fdow)).

Wed, Apr 15
DevConf — Vienna
12 AM
1 AM
2 AM
3 AM
4 AM
5 AM
6 AM
7 AM
8 AM
9 AM
10 AM
11 AM
12 PM
1 PM
2 PM
3 PM
4 PM
5 PM
6 PM
7 PM
8 PM
9 PM
10 PM
11 PM
12 AM
Daily standup
Design review
Pair: calendar
Lunch with Anna
1:1 with Bernhard
vue
<template>
  <div style="height: 600px; border: 1px solid var(--coar-border-neutral-tertiary); border-radius: var(--coar-radius-xs); overflow: hidden;">
    <CoarDayView :builder="builder" />
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import {
  CoarDayView,
  useDayView,
  Temporal,
  type CalendarEvent,
} from '@cocoar/vue-calendar';

const date = ref(Temporal.PlainDate.from('2026-04-15'));

const pd = (iso: string) => Temporal.PlainDate.from(iso);
const zdt = (iso: string, tz = 'Europe/Vienna') =>
  Temporal.ZonedDateTime.from(`${iso}[${tz}]`);

const events = ref<CalendarEvent[]>([
  // All-day band: a multi-day OOO that touches today.
  {
    id: 'devconf',
    start: pd('2026-04-13'),
    end: pd('2026-04-16'),
    meta: { title: 'DevConf — Vienna', color: '#7c3aed' },
  },
  // Three timed events with a 3-deep overlap cluster.
  {
    id: 'standup',
    start: zdt('2026-04-15T09:00:00'),
    end: zdt('2026-04-15T09:30:00'),
    meta: { title: 'Daily standup', color: '#10b981' },
  },
  {
    id: 'design',
    start: zdt('2026-04-15T11:00:00'),
    end: zdt('2026-04-15T12:30:00'),
    meta: { title: 'Design review', color: '#8b5cf6' },
  },
  {
    id: 'pair',
    start: zdt('2026-04-15T11:30:00'),
    end: zdt('2026-04-15T13:00:00'),
    meta: { title: 'Pair: calendar', color: '#f59e0b' },
  },
  {
    id: 'lunch',
    start: zdt('2026-04-15T12:00:00'),
    end: zdt('2026-04-15T13:00:00'),
    meta: { title: 'Lunch with Anna', color: '#ef4444' },
  },
  {
    id: 'one-on-one',
    start: zdt('2026-04-15T15:00:00'),
    end: zdt('2026-04-15T15:45:00'),
    meta: { title: '1:1 with Bernhard', color: '#3b82f6' },
  },
]);

const { builder } = useDayView();
builder
  .events(events)
  .date(date)
  .timezone('Europe/Vienna')
  // Apply drag/keyboard moves in place so the demo is interactive.
  .onEventDrop(({ event, next }) => {
    const idx = events.value.findIndex((e) => e.id === event.id);
    if (idx < 0) return;
    events.value = [
      ...events.value.slice(0, idx),
      { ...event, start: next.start, ...(next.end ? { end: next.end } : {}) },
      ...events.value.slice(idx + 1),
    ];
  });
</script>

Working hours

Constrain the visible hour range via timeRange([startHour, endHour]) and tighten drag-snap precision via slotDuration(15) for a 15-minute grid. Events outside the visible window are still in the data set but invisible.

Visible hour range constrained to [8, 18]; slot subdivision tightened to 15 min for finer drag-snap. The early flight (5–7 AM) is still in events but lives off the visible window.

Wed, Apr 15
8 AM
9 AM
10 AM
11 AM
12 PM
1 PM
2 PM
3 PM
4 PM
5 PM
6 PM
Daily standup
Design review
Lunch
Deep work
vue
<template>
  <div>
    <p class="hint">
      Visible hour range constrained to <code>[8, 18]</code>; slot
      subdivision tightened to 15 min for finer drag-snap. The early
      flight (5–7 AM) is still in <code>events</code> but lives off
      the visible window.
    </p>
    <div style="height: 520px; border: 1px solid var(--coar-border-neutral-tertiary); border-radius: var(--coar-radius-xs); overflow: hidden;">
      <CoarDayView :builder="builder" />
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import {
  CoarDayView,
  useDayView,
  Temporal,
  type CalendarEvent,
} from '@cocoar/vue-calendar';

const date = ref(Temporal.PlainDate.from('2026-04-15'));

const zdt = (iso: string, tz = 'Europe/Vienna') =>
  Temporal.ZonedDateTime.from(`${iso}[${tz}]`);

const events = ref<CalendarEvent[]>([
  // Off-window early — invisible at timeRange [8, 18].
  {
    id: 'red-eye',
    start: zdt('2026-04-15T05:00:00'),
    end: zdt('2026-04-15T07:30:00'),
    meta: { title: 'Early flight', color: '#84cc16' },
  },
  {
    id: 'standup',
    start: zdt('2026-04-15T09:00:00'),
    end: zdt('2026-04-15T09:15:00'),
    meta: { title: 'Daily standup', color: '#10b981' },
  },
  {
    id: 'review',
    start: zdt('2026-04-15T10:30:00'),
    end: zdt('2026-04-15T11:30:00'),
    meta: { title: 'Design review', color: '#8b5cf6' },
  },
  {
    id: 'lunch',
    start: zdt('2026-04-15T12:00:00'),
    end: zdt('2026-04-15T13:00:00'),
    meta: { title: 'Lunch', color: '#ef4444' },
  },
  {
    id: 'deep-work',
    start: zdt('2026-04-15T13:15:00'),
    end: zdt('2026-04-15T16:45:00'),
    meta: { title: 'Deep work', color: '#2563eb' },
  },
]);

const { builder } = useDayView();
builder
  .events(events)
  .date(date)
  .timezone('Europe/Vienna')
  .timeRange({ startMinutes: 8 * 60, endMinutes: 18 * 60 })
  .slotDuration(15)
  .onEventDrop(({ event, next }) => {
    const idx = events.value.findIndex((e) => e.id === event.id);
    if (idx < 0) return;
    events.value = [
      ...events.value.slice(0, idx),
      { ...event, start: next.start, ...(next.end ? { end: next.end } : {}) },
      ...events.value.slice(idx + 1),
    ];
  });
</script>

<style scoped>
.hint {
  margin: 0 0 12px;
  font-size: 13px;
  color: var(--coar-text-subtle, #6b7280);
}
.hint code {
  font-family: var(--coar-font-family-mono, monospace);
  font-size: 12px;
  background: var(--coar-background-neutral-tertiary, #f3f4f6);
  padding: 1px 5px;
  border-radius: 3px;
}
</style>

Inside <CoarCalendar>

<CoarCalendar> and <CoarDayView> consume the SAME CalendarBuilder instance — there's no sub-builder forking. Time-grid config goes directly on the composer's builder:

ts
const { builder } = useCalendar();
builder
  .timeRange({ startMinutes: 8 * 60, endMinutes: 18 * 60 })
  .slotDuration(15);

When the active view is day or week, the same builder feeds the embedded <CoarTimeGrid>. View-specific settings have no effect outside their view.

useDayView<TMeta>()

ts
function useDayView<TMeta>(): {
  builder: CalendarBuilder<TMeta>;
  api: CalendarApi<TMeta>;
};

Returns a fresh standalone builder + its imperative api. The builder type is the same CalendarBuilder used by <CoarCalendar>useDayView() is a thin shorthand that pre-sets view: 'day'.

Builder setters

Full reference: see the composer's API reference. Highlights for the day view:

SetterArgumentDefaultNotes
timeRange(r)MaybeRefOrGetter<{ startMinutes, endMinutes }>{0, 1440}Visible hour range, in minutes from midnight. Events outside are still rendered into the all-day band when applicable.
slotDuration(d)MaybeRefOrGetter<number>30Slot subdivision (minutes). Also the snap step when dragging.
pixelsPerHour(p)MaybeRefOrGetter<number>60Vertical density. 60 = 30 px per 30-min slot.
eventRenderer(r)EventRenderer<TMeta>Universal renderer. Branch on ctx.layout?.kind === 'positioned' (timed cards) vs 'allDayBar' (all-day band).
dayHeaderRenderer(r)DayHeaderRendererPer-day column header.

Imperative API

ts
interface CalendarApi<TMeta> {
  goTo(date: Temporal.PlainDate): void;
  goToToday(): void;
  next(): void;                            // ±1 day in day-view
  prev(): void;
  getVisibleRange(): ViewWindow | null;
  getVisibleEvents(): CalendarEvent<TMeta>[];
  scrollToTime(time: Temporal.PlainTime): void;   // Day / Week only
  scrollToDate(date: Temporal.PlainDate): void;   // Agenda only
  refresh(): void;
  refreshRange(window: ViewWindow): void;
  readonly loading: Readonly<Ref<boolean>>;
  readonly visibleRange: Readonly<Ref<ViewWindow | null>>;
  readonly gridReady: Readonly<Ref<boolean>>;
}

<CoarDayView> props + slots

PropTypeDescription
builderCalendarBuilderRequired. From useDayView() (or share the one from useCalendar()).
SlotScopePurpose
event{ event, layout }Per-event renderer. layout is the PositionedEvent (lane / startMinutes / endMinutes / clipping flags).
allDayEvent{ event, layout }All-day band renderer. layout is the AllDayBar (lane / startCol / endCol / clipping flags).
dayHeader{ date, isToday, isWeekend }Per-day column header.

Released under the Apache-2.0 License.