# Slots, Hours, and Markers
URL: https://pro.rv-grid.com/guides/event-scheduler/customization/slots-hours/
Source: src/content/docs/guides/event-scheduler/customization/slots-hours.mdx
Description: Customize create previews, empty slots, closed hours, availability states, and current-time markers.
## Create previews

Create preview customization affects empty-slot hover labels and the drag-create range preview.

```ts
grid.eventScheduler = {
  view: 'week',
  weekStartDate: '2026-06-08',
  editable: true,
  allowCreate: true,
  customization: {
    createPreview: {
      hoverTimeProperties: ({ startDateTime }) => ({
        class: 'create-hover-slot',
        'data-hover-start': startDateTime,
      }),
      createRangeTemplate: ({ title, startLabel, endLabel, durationLabel, resource }) =>
        `${title} for ${resource?.name ?? 'Unassigned'}: ${startLabel}-${endLabel} (${durationLabel})`,
      createRangeProperties: ({ durationMinutes }) => ({
        class: durationMinutes >= 240 ? 'create-range--long' : 'create-range--short',
      }),
    },
  },
};
```

Use `hoverTimeProperties` for attributes and styling on the slot while the pointer is over a creatable time. Use `createRangeTemplate` and `createRangeProperties` for the range ghost shown during drag-create interactions.

## Empty slot customization

Empty slots are a primary extension point for booking, staffing, room planning, maintenance windows, pricing, capacity, and availability workflows. Use direct slot hooks when a product needs custom empty-cell content or behavior without replacing event bars.

| Hook | Purpose |
| --- | --- |
| `slotTemplate` | Renders custom content inside empty week or resource-timeline slots. Occupied slots keep their event bars. |
| `slotProperties` | Adds classes, styles, attributes, data fields, or event handlers to scheduler slot cells. |
| `slotTooltip` | Overrides the native tooltip for disabled, closed, priced, or capacity-based slots. |
| `onSlotClick` | Runs custom click behavior before built-in editor-open behavior. |
| `onSlotDoubleClick` | Runs custom double-click behavior for immediate create or custom workflows. |
| `onSlotContextMenu` | Receives the resolved slot when the scheduler context menu opens. |
| `isSlotDisabled` | Marks slots as disabled and blocks built-in create/editor behavior. |
| `isSlotSelectable` | Marks slots as non-selectable for create/selection UX. |

```ts
grid.eventScheduler = {
  view: 'resourceTimeline',
  weekStartDate: '2026-06-08',
  editable: true,
  allowCreate: true,
  slotTemplate: (h, context) =>
    h('span', { class: 'booking-slot-capacity' }, [
      context.disabled ? 'Closed' : `${getCapacity(context)} seats`,
    ]),
  slotProperties: (context) => ({
    class: [
      context.disabled ? 'booking-slot--closed' : '',
      !context.selectable ? 'booking-slot--invalid' : '',
      context.segmentCount > 0 ? 'booking-slot--busy' : 'booking-slot--open',
    ].filter(Boolean).join(' '),
    style: {
      '--booking-slot-capacity': getCapacity(context),
    },
    'data-booking-slot': context.startDateTime,
  }),
  slotTooltip: (context) =>
    context.disabled ? 'Outside working hours' : `Available from ${context.startDateTime}`,
  isSlotDisabled: (context) => isClosedForBusiness(context),
  isSlotSelectable: (context) => !hasBlockedAvailability(context),
  onSlotClick: (context, event) => {
    if (context.disabled) {
      event.preventDefault();
      showClosedSlotPopover(context);
    }
  },
  onSlotDoubleClick: (context) => {
    if (!context.disabled) createBookingImmediately(context);
  },
  onSlotContextMenu: (context) => {
    trackSlotMenuOpened(context);
  },
  contextMenu: {
    enabled: true,
    hiddenItems: {
      create: true,
      'assign-open': true,
    },
    items: ({ target, slot }) => target === 'slot' && slot
      ? [
          { name: 'Create booking', action: () => createBookingImmediately(slot) },
          { name: 'Block this time', action: () => blockSlot(slot) },
        ]
      : [],
  },
};
```

`slotTemplate` is intentionally limited to empty cells so products can customize available space without removing visible event bars. Use `customization.cells.dayTemplate` or `customization.cells.timelineTemplate` only when the whole scheduler body cell, including event rendering, should be replaced. Use `isSlotDisabled` and `isSlotSelectable` with `allowCreate` or conflict rules when closed hours should block create, drag, paste, and editor flows.

## Working and closed hours

Use working and closed-hour APIs when availability is part of the scheduler contract: business hours, weekends, holidays, lunch breaks, maintenance windows, or capacity holds. These hooks build on the existing `nonWorkingTime`, calendars, availability data, and conflict rules.

| Config | Purpose |
| --- | --- |
| `workingHours` | Defines global or per-weekday working ranges. Use `false` in `byDay` to close a weekday. |
| `closedHours` | Defines closed ranges such as lunch, maintenance, holidays, or blocked planning windows. |
| `availabilityRules` | Applies product-specific availability rules from slot context. |
| `closedSlotTemplate` | Renders custom content inside closed slots. |
| `closedSlotProperties` | Adds classes, styles, attributes, data fields, or event handlers to closed slots. |
| `closedSlotTooltip` | Overrides the native tooltip/title for closed slots. |
| `isTimeSlotAvailable` | Blocks built-in create/editor behavior when it returns `false`. |

Use `workingHours` for global business hours, `workingHours.byDay` for weekday-specific hours or closed weekends, `closedHours` for explicit closed ranges such as lunch or maintenance, and date-specific `closedHours` entries for holidays. Closed slots can be styled with `closedSlotProperties`, replaced with `closedSlotTemplate`, and explained through `closedSlotTooltip`.

```ts
grid.eventScheduler = {
  view: 'week',
  weekStartDate: '2026-06-08',
  editable: true,
  allowCreate: true,
  workingHours: {
    byDay: {
      1: { start: '08:00', end: '18:00' },
      2: { start: '08:00', end: '18:00' },
      3: { start: '08:00', end: '18:00' },
      4: { start: '08:00', end: '18:00' },
      5: { start: '08:00', end: '16:00' },
      0: false,
      6: false,
    },
  },
  closedHours: [
    {
      id: 'lunch',
      days: [1, 2, 3, 4, 5],
      ranges: { start: '12:00', end: '13:00' },
      title: 'Closed',
      reason: 'Lunch break',
      className: 'planner-slot--lunch',
    },
    {
      id: 'maintenance',
      dates: ['2026-06-10'],
      ranges: { start: '15:00', end: '17:00' },
      reason: 'Maintenance window',
      className: 'planner-slot--maintenance',
    },
  ],
  availabilityRules: ({ date, startMinutes }) =>
    date === '2026-06-12' && startMinutes >= 14 * 60
      ? { available: false, reason: 'Capacity hold', className: 'planner-slot--held' }
      : undefined,
  isTimeSlotAvailable: ({ date, slotIndex }) =>
    !isCompanyHoliday(date) && slotIndex !== 0,
  closedSlotProperties: ({ availabilityKind, reason }) => ({
    class: `planner-slot--closed planner-slot--${availabilityKind}`,
    style: {
      '--planner-closed-opacity': reason === 'Lunch break' ? 0.6 : 0.85,
    },
  }),
  closedSlotTooltip: ({ title, reason }) =>
    [title, reason].filter(Boolean).join(' - '),
  closedSlotTemplate: (h, { reason }) =>
    h('span', { class: 'planner-slot__closed-label' }, reason ?? 'Closed'),
};
```

Closed-slot context extends normal slot context with `available`, `availabilityKind`, `availabilityId`, `title`, and `reason`. Built-in create/editor behavior is blocked when `isTimeSlotAvailable` returns `false`, when `closedHours`/`availabilityRules` mark the slot unavailable, or when `isSlotDisabled` / `isSlotSelectable` deny the slot. For resource-specific calendars, remote availability, and holidays, continue using `calendars` and `eventSchedulerAvailability`; the closed-slot hooks style those states too.

## Current time marker

The current-time marker can use the built-in red line and badge, or it can be replaced by product-specific marker content. Week-style views render a horizontal marker on the active date. Resource timeline views render a vertical marker in the active timeline slot.

| Config | Purpose |
| --- | --- |
| `showCurrentTimeMarker` | Simple show/hide alias. |
| `currentTimeMarker` | Detailed marker config, including fixed `dateTime` for deterministic demos/tests. |
| `currentTimeUpdateInterval` | Live refresh interval in milliseconds. Defaults to `60000`; use `0` to disable automatic live refresh. |
| `currentTimeLabelFormatter` | Formats the marker label, such as `12:26`, `Now`, or a localized time. |
| `currentTimeMarkerTemplate` | Replaces marker content in week and resource timeline cells. |
| `currentTimeMarkerProperties` | Adds classes, styles, attributes, data fields, or event handlers to the marker cell. |

```ts
grid.eventScheduler = {
  view: 'week',
  weekStartDate: '2026-06-08',
  showCurrentTimeMarker: true,
  currentTimeUpdateInterval: 60_000,
  currentTimeLabelFormatter: ({ dateTime }) =>
    new Intl.DateTimeFormat('en-US', {
      hour: 'numeric',
      minute: '2-digit',
    }).format(new Date(dateTime)),
  currentTimeMarkerProperties: ({ orientation }) => ({
    class: orientation === 'horizontal'
      ? 'planner-now-marker planner-now-marker--horizontal'
      : 'planner-now-marker planner-now-marker--vertical',
    style: {
      '--event-scheduler-current-time-color': '#dc2626',
      '--planner-now-marker-thickness': '2px',
    },
  }),
  currentTimeMarkerTemplate: (h, context) =>
    h('span', { class: 'planner-now-marker__label' }, context.label),
};
```

Marker context includes `view`, `date`, `dayIndex`, `slotIndex`, `startMinutes`, `endMinutes`, `dateTime`, `label`, `position`, `orientation`, and, when available, `row`, `resourceId`, and `resource`. The marker is hidden when disabled, outside the visible date range, or outside the visible time range. Use `currentTimeMarker: { dateTime }` when the marker should point at a fixed instant rather than live `now`.
