---
title: "Javascript Timeline Header and Footer Template Example | Mobiscroll"
component: "Timeline"
framework: "JavaScript"
library_version: "6.1.5"
url: "https://demo.mobiscroll.com/javascript/timeline/hour-day-week-month-quarter-year-header-footer-template"
---

# Header and Footer Template - JavaScript Timeline demo

Customize time-scale headers and add occupancy footers to a Javascript Timeline — format cells from hour to year and display per-slot utilization. Plain JavaScript API.

- [Live demo](https://demo.mobiscroll.com/javascript/timeline/hour-day-week-month-quarter-year-header-footer-template)
- [View code on GitHub](https://github.com/acidb/mobiscroll-demos-javascript/tree/main/src/demos/eventcalendar/timeline/hour-day-week-month-quarter-year-header-footer-template)
- This demo in other frameworks: [jQuery](https://demo.mobiscroll.com/jquery/timeline/hour-day-week-month-quarter-year-header-footer-template.md), [Angular](https://demo.mobiscroll.com/angular/timeline/hour-day-week-month-quarter-year-header-footer-template.md), [React](https://demo.mobiscroll.com/react/timeline/hour-day-week-month-quarter-year-header-footer-template.md), [Vue](https://demo.mobiscroll.com/vue/timeline/hour-day-week-month-quarter-year-header-footer-template.md)

## Description

The headers hold key information like the date, day of the week and in some cases it also holds the full date. Whenever you need to show extra information, or if you would like to change the styling or date format, time format you can use the various header templates, depending on the view configuration. You can also show a footer element, for displaying more information.

Add summaries, statistics or simply color the background depending on the number events that occur on that specific day, like in this example.

**Check out how you can customize the date header for the scheduler** [Let's go →](https://demo.mobiscroll.com/javascript/scheduler/date-header-template)

## What this demo shows

- Shows six timeline configurations with hour, day, week, month, quarter, and year resolutions.
- **Timeline layout** Time periods are arranged horizontally, while resources are listed as rows on the left.
- **Header templates** Each timeline resolution uses a custom header format for its time scale, such as hours, days, weeks, months, quarters, or years.
- **Footer templates** Footer cells display the resource occupancy percentage for each visible time slot.
- **Occupancy calculation** Percentages are calculated from the number of distinct resources occupied by events in each time slot.
- **Density overview** Values such as 25%, 63%, 88%, and 100% highlight how fully booked each time slot is.
- **Header and footer rendering** The demo demonstrates custom rendering for timeline header and footer cells with aggregated scheduling metrics.
- **Dynamic updates** Occupancy values update as events are created, moved, resized, or removed.
- **Event cards** Events are shown as colored cards with a left color stripe, a bold title, and the exact start and end time.
- **Date positioning** Events are positioned by their assigned resource and exact date and time range.
- **Event interaction** Events highlight on hover and show drag and resize handles for repositioning or changing duration.
- **Event selection** Clicking an event selects and highlights it.
- **Event creation** New events can be created by double-clicking the timeline or by clicking and dragging across a time range.
- **Scrolling** The timeline supports horizontal and vertical scrolling for navigating through time and resources.

## Best for

- **Workforce planning** Compare staffing coverage and open capacity across different timeline resolutions.
- **Equipment scheduling** Track when shared equipment is booked and identify time slots with remaining availability.
- **Room booking** Review resource occupancy across rooms while keeping the individual event schedule visible.
- **Asset management** Monitor scheduled usage for multiple resources in a row-based timeline layout.
- **Capacity analysis** Show high-level utilization metrics without hiding the detailed events for each resource.
- **Custom scheduling dashboards** Add summary values, occupancy indicators, or other aggregated metrics to timeline headers and footers.

## Tutorial

### Hourly header/footer template

With an hourly (or sub-hourly) resolution the [`renderHour`](https://mobiscroll.com/docs/javascript/eventcalendar/timeline#renderer-renderHour) and [`renderHourFooter`](https://mobiscroll.com/docs/javascript/eventcalendar/timeline#renderer-renderHourFooter) options can be used for customizing the header and footer.

```html
<div id="my-timeline" class="md-date-header-template"></div>
```

```js
mobiscroll.eventcalendar('#my-timeline', {
    view: {
        timeline: {
            type: 'day'
        }
    },
    renderHour: function (args) {
        var date = args.date;
        var formatDate = mobiscroll.formatDate;
        var eventOccurrence = 'none';

        if (args.events) {
            var eventNr = args.events.length;
            if (eventNr === 0) {
                eventOccurrence = 'none';
            } else if (eventNr === 1) {
                eventOccurrence = 'one';
            } else if (eventNr < 4) {
                eventOccurrence = 'few';
            } else {
                eventOccurrence = 'more';
            }
        }

        return '<div class="md-date-header-hour md-date-header-events-' + eventOccurrence + '">'
            + formatDate('h:mm A', date) + '</div>';
    },
    renderHourFooter: function (args) {
        var events = args.events;
        var occuppancy = 0;

        if (events) {
            var resourceIds = [];
            var nr = 0;
            for (var i = 0; i < events.length; ++i) {
                var event = events[i];
                if (resourceIds.indexOf(event.resource) < 0) {
                    nr++;
                    resourceIds.push(event.resource);
                }
            }
            occuppancy = (nr * 100 / myResources.length).toFixed(0);
        }

        return '<div class="md-date-footer">' + occuppancy + '%'</div>';
    }
});
```

```css
.md-date-header-hour {
    font-size: 12px;
    font-weight: 600;
    text-align: center;
    line-height: 20px;
    border-radius: 8px;
    margin: 3px;
    color: #000;
}

.md-date-header-events-one {
    background: #e9f5e4;
}

.md-date-header-events-few {
    background: #d0e3c6;
}

.md-date-header-events-more {
    background: #8ca77e;
}

.md-date-footer {
    font-size: 14px;
    font-weight: 600;
    text-align: center;
    line-height: 26px;
}
```

### Daily header/footer template

With a daily resolution, the [`renderTimelineDay`](https://mobiscroll.com/docs/javascript/eventcalendar/timeline#renderer-renderTimelineDay) and [`renderDayFooter`](https://mobiscroll.com/docs/javascript/eventcalendar/timeline#renderer-renderDayFooter) options can come in handy for customizing the header and footer.

```html
<div id="my-timeline" class="md-date-header-template"></div>
```

```js
mobiscroll.eventcalendar('#my-timeline', {
    view: {
        timeline: {
            type: 'month'
        }
    },
    renderTimelineDay: function (args) {
        var date = args.date;
        var formatDate = mobiscroll.formatDate;
        var eventOccurrence = 'none';

        if (args.events) {
            var eventNr = args.events.length;
            if (eventNr === 0) {
                eventOccurrence = 'none';
            } else if (eventNr === 1) {
                eventOccurrence = 'one';
            } else if (eventNr < 4) {
                eventOccurrence = 'few';
            } else {
                eventOccurrence = 'more';
            }
        }
                    
        return '<div class="md-date-header md-date-header-day md-date-header-events-' + eventOccurrence + '">' +
            '<div class="md-date-header-day-name">' + formatDate('DDD', date) + '</div>' +
            '<div class="md-date-header-day-nr">' + formatDate('DD', date) + '</div></div>';
    },
    renderDayFooter: function (args) {
        var occuppancy = 0;

        if (events) {
            var resourceIds = [];
            var nr = 0;
            for (var i = 0; i < events.length; ++i) {
                var event = events[i];
                if (resourceIds.indexOf(event.resource) < 0) {
                    nr++;
                    resourceIds.push(event.resource);
                }
            }
            occuppancy = (nr * 100 / myResources.length).toFixed(0);
        }

        return '<div class="md-date-footer">' + occuppancy + '%</div>';
    }
});
```

```css
.md-date-header-day {
    text-align: center;
    line-height: 20px;
    border-radius: 8px;
    margin: 4px;
}

.md-date-header-day-name {
    font-size: 13px;
    font-weight: 600;
}

.md-date-header-day-nr {
    font-size: 14px;
}

.md-date-header-events-one {
    background: #e9f5e4;
}

.md-date-header-events-few {
    background: #d0e3c6;
}

.md-date-header-events-more {
    background: #8ca77e;
}

.md-date-footer {
    font-size: 14px;
    font-weight: 600;
    text-align: center;
    line-height: 26px;
}
```

### Weekly header/footer template

When the resolution is weekly, the [`renderWeek`](https://mobiscroll.com/docs/javascript/eventcalendar/timeline#renderer-renderWeek) and [`renderWeekFooter`](https://mobiscroll.com/docs/javascript/eventcalendar/timeline#renderer-renderWeekFooter) options can be used.

```html
<div id="my-timeline" class="md-date-header-template"></div>
```

```js
mobiscroll.eventcalendar('#my-timeline', {
    view: {
        timeline: {
            type: 'week',
            resolutionHorizontal: 'week',
            size: 6
        }
    },
    renderWeek: function (args) {
        var date = args.date;
        var formatDate = mobiscroll.formatDate;
        var eventOccurrence = 'none';

        if (args.events) {
            var eventNr = args.events.length;
            if (eventNr === 0) {
                eventOccurrence = 'none';
            } else if (eventNr === 1) {
                eventOccurrence = 'one';
            } else if (eventNr < 4) {
                eventOccurrence = 'few';
            } else {
                eventOccurrence = 'more';
            }
        }

        return '<div class="md-date-header-week md-date-header-events-' + eventOccurrence + '">' +
            formatDate('MMM DD', args.startDate) + ' - ' + formatDate('MMM DD', args.endDate) + '</div></div>';
    },
    renderWeekFooter: function (args) {
        var occuppancy = 0;

        if (events) {
            var resourceIds = [];
            var nr = 0;
            for (var i = 0; i < events.length; ++i) {
                var event = events[i];
                if (resourceIds.indexOf(event.resource) < 0) {
                    nr++;
                    resourceIds.push(event.resource);
                }
            }
            occuppancy = (nr * 100 / myResources.length).toFixed(0);
        }

        return '<div class="md-date-footer">' + occuppancy + '%</div>';
    }
});
```

```css
.md-date-header-template .mbsc-timeline-header-week-text {
    display: block;
}

.md-date-header-template .mbsc-timeline-footer-week {
    text-align: center;
}

.md-date-header-week {
	text-align: center;
	font-weight: 600;
	color: initial;
	line-height: 20px;
	border-radius: 8px;
	margin: 3px;
}

.md-date-header-events-one {
	background: #e9f5e4;
}

.md-date-header-events-few {
	background: #d0e3c6;
}

.md-date-header-events-more {
	background: #8ca77e;
}

.md-date-footer {
	font-size: 14px;
	font-weight: 600;
	text-align: center;
	line-height: 26px;
}
```

### Monthly header/footer template

When displaying multiple months, the [`renderMonth`](https://mobiscroll.com/docs/javascript/eventcalendar/timeline#renderer-renderMonth) and [`renderMonthFooter`](https://mobiscroll.com/docs/javascript/eventcalendar/timeline#renderer-renderMonthFooter) options can be used for customizing the header and footer per month.

```html
<div id="my-timeline" class="md-date-header-template"></div>
```

```js
mobiscroll.eventcalendar('#my-timeline', {

    view: {
        timeline: {
            type: 'month',
            resolutionHorizontal: 'month',
            size: 6
        }
    },
    renderMonth: function (args) {
        var date = args.date;
        var formatDate = mobiscroll.formatDate;
        var eventOccurrence = 'none';

        if (args.events) {
            var eventNr = args.events.length;
            if (eventNr === 0) {
                eventOccurrence = 'none';
            } else if (eventNr === 1) {
                eventOccurrence = 'one';
            } else if (eventNr < 4) {
                eventOccurrence = 'few';
            } else {
                eventOccurrence = 'more';
            }
        }

        return '<div class="md-date-header-month md-date-header-events-' + eventOccurrence + '">' +
            formatDate('MMM', date) + '</div>';
    },
    renderMonthFooter: function (args) {
        var occuppancy = 0;

        if (events) {
            var resourceIds = [];
            var nr = 0;
            for (var i = 0; i < events.length; ++i) {
                var event = events[i];
                if (resourceIds.indexOf(event.resource) < 0) {
                    nr++;
                    resourceIds.push(event.resource);
                }
            }
            occuppancy = (nr * 100 / myResources.length).toFixed(0);
        }

        return '<div class="md-date-footer">' + occuppancy + '%</div>';
    }
});
```

```css
.md-date-header-month {
	text-align: center;
	font-weight: 600;
	color: initial;
	line-height: 20px;
	border-radius: 8px;
	margin: 3px;
}

.md-date-header-events-one {
	background: #e9f5e4;
}

.md-date-header-events-few {
	background: #d0e3c6;
}

.md-date-header-events-more {
	background: #8ca77e;
}

.md-date-footer {
	font-size: 14px;
	font-weight: 600;
	text-align: center;
	line-height: 26px;
}
```

### Quarter header/footer template

When displaying multiple quarters, the [`renderQuarter`](https://mobiscroll.com/docs/javascript/eventcalendar/api#renderer-renderQuarter) and [`renderQuarterFooter`](https://mobiscroll.com/docs/javascript/eventcalendar/timeline#renderer-renderQuarterFooter) options can be used for customizing the header and footer per quarter.

```html
<div id="my-timeline" class="md-date-header-template"></div>
```

```js
mobiscroll.eventcalendar('#my-timeline', {

    view: {
        timeline: {
            type: 'year',
            resolutionHorizontal: 'quarter',
            size: 1
        }
    },
    renderQuarter: function (args) {
        var date = args.date;
        var formatDate = mobiscroll.formatDate;
        var eventOccurrence = 'none';

        if (args.events) {
            var eventNr = args.events.length;
            if (eventNr === 0) {
                eventOccurrence = 'none';
            } else if (eventNr === 1) {
                eventOccurrence = 'one';
            } else if (eventNr < 4) {
                eventOccurrence = 'few';
            } else {
                eventOccurrence = 'more';
            }
        }

        return '<div class="md-date-header-quarter md-date-header-events-' + eventOccurrence + '">Quarter ' +
            (date.getMonth() / 3 + 1) + '</div>';
    },
    renderQuarterFooter: function (args) {
        var occuppancy = 0;

        if (events) {
            var resourceIds = [];
            var nr = 0;
            for (var i = 0; i < events.length; ++i) {
                var event = events[i];
                if (resourceIds.indexOf(event.resource) < 0) {
                    nr++;
                    resourceIds.push(event.resource);
                }
            }
            occuppancy = (nr * 100 / myResources.length).toFixed(0);
        }

        return '<div class="md-date-footer">' + occuppancy + '%</div>';
    }
});
```

```css
.md-date-header-quarter {
	text-align: center;
	font-weight: 600;
	color: initial;
	line-height: 20px;
	border-radius: 8px;
	margin: 3px;
}

.md-date-header-events-one {
	background: #e9f5e4;
}

.md-date-header-events-few {
	background: #d0e3c6;
}

.md-date-header-events-more {
	background: #8ca77e;
}

.md-date-footer {
	font-size: 14px;
	font-weight: 600;
	text-align: center;
	line-height: 26px;
}
```

### Yearly header/footer template

With a yearly resolution, the [`renderYear`](https://mobiscroll.com/docs/javascript/eventcalendar/timeline#renderer-renderYear) and [`renderYearFooter`](https://mobiscroll.com/docs/javascript/eventcalendar/timeline#renderer-renderYearFooter) options can be used for customizing the header and footer.

```html
<div id="my-timeline" class="md-date-header-template"></div>
```

```js
mobiscroll.eventcalendar('#my-timeline', {
    view: {
        timeline: {
            type: 'year',
            resolutionHorizontal: 'year',
            size: 6
        }
    },
    renderYear: function (args) {
        var date = args.date;
        var formatDate = mobiscroll.formatDate;
        var eventOccurrence = 'none';

        if (args.events) {
            var eventNr = args.events.length;
            if (eventNr === 0) {
                eventOccurrence = 'none';
            } else if (eventNr === 1) {
                eventOccurrence = 'one';
            } else if (eventNr < 4) {
                eventOccurrence = 'few';
            } else {
                eventOccurrence = 'more';
            }
        }

        return '<div class="md-date-header-year md-date-header-events-' + eventOccurrence + '">' +
            formatDate('YYYY', date) + '</div>';
    },
    renderYearFooter: function (args) {
        var occuppancy = 0;

        if (events) {
            var resourceIds = [];
            var nr = 0;
            for (var i = 0; i < events.length; ++i) {
                var event = events[i];
                if (resourceIds.indexOf(event.resource) < 0) {
                    nr++;
                    resourceIds.push(event.resource);
                }
            }
            occuppancy = (nr * 100 / myResources.length).toFixed(0);
        }

        return '<div class="md-date-footer">' + occuppancy + '%</div>';
    }
});
```

## API used in this demo

- [`theme`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-theme) - option
- [`themeVariant`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-themeVariant) - option
- [`view`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-view) - option
- [`resources`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-resources) - option
- [`data`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-data) - option
- [`renderHour`](https://mobiscroll.com/docs/javascript/eventcalendar/api#renderer-renderHour) - renderer
- [`renderHourFooter`](https://mobiscroll.com/docs/javascript/eventcalendar/api#renderer-renderHourFooter) - renderer
- [`renderTimelineDay`](https://mobiscroll.com/docs/javascript/eventcalendar/api#renderer-renderTimelineDay) - renderer
- [`renderDayFooter`](https://mobiscroll.com/docs/javascript/eventcalendar/api#renderer-renderDayFooter) - renderer
- [`renderWeek`](https://mobiscroll.com/docs/javascript/eventcalendar/api#renderer-renderWeek) - renderer
- [`renderWeekFooter`](https://mobiscroll.com/docs/javascript/eventcalendar/api#renderer-renderWeekFooter) - renderer
- [`renderMonth`](https://mobiscroll.com/docs/javascript/eventcalendar/api#renderer-renderMonth) - renderer
- [`renderMonthFooter`](https://mobiscroll.com/docs/javascript/eventcalendar/api#renderer-renderMonthFooter) - renderer
- [`renderQuarter`](https://mobiscroll.com/docs/javascript/eventcalendar/api#renderer-renderQuarter) - renderer
- [`renderQuarterFooter`](https://mobiscroll.com/docs/javascript/eventcalendar/api#renderer-renderQuarterFooter) - renderer
- [`renderYear`](https://mobiscroll.com/docs/javascript/eventcalendar/api#renderer-renderYear) - renderer
- [`renderYearFooter`](https://mobiscroll.com/docs/javascript/eventcalendar/api#renderer-renderYearFooter) - renderer

## Code

### JS

```js
mobiscroll.setOptions({
  theme: 'ios',
  themeVariant: 'light'
});

var formatDate = mobiscroll.formatDate;

var myResources = [
  {
    id: 1,
    name: 'Resource A',
    color: '#e20000',
  },
  {
    id: 2,
    name: 'Resource B',
    color: '#d6d145',
  },
  {
    id: 3,
    name: 'Resource C',
    color: '#4981d6',
  },
  {
    id: 4,
    name: 'Resource D',
    color: '#e25dd2',
  },
  {
    id: 5,
    name: 'Resource E',
    color: '#1dab2f',
  },
  {
    id: 6,
    name: 'Resource F',
    color: '#430404',
  },
  {
    id: 7,
    name: 'Resource G',
    color: '#f7961e',
  },
  {
    id: 8,
    name: 'Resource H',
    color: '#34c8e0',
  },
];

function getEventOccurrence(events) {
  var eventOccurrence = 'none';

  if (events) {
    var eventNr = events.length;
    if (eventNr === 0) {
      eventOccurrence = 'none';
    } else if (eventNr === 1) {
      eventOccurrence = 'one';
    } else if (eventNr < 4) {
      eventOccurrence = 'few';
    } else {
      eventOccurrence = 'more';
    }
  }

  return eventOccurrence;
}

function getOccuppancy(events) {
  var occuppancy = 0;

  if (events) {
    var resourceIds = [];
    var nr = 0;
    for (var i = 0; i < events.length; ++i) {
      var event = events[i];
      if (resourceIds.indexOf(event.resource) < 0) {
        nr++;
        resourceIds.push(event.resource);
      }
    }
    occuppancy = ((nr * 100) / myResources.length).toFixed(0);
  }

  return '<div class="md-date-footer">' + occuppancy + '%</div>';
}

mobiscroll.eventcalendar('#demo-hour-header-template', {
  view: {
    timeline: {
      type: 'day',
    },
  },
  resources: myResources,
  data: [
    {
      id: 1,
      start: '2026-10-28T02:00',
      end: '2026-10-28T10:00',
      title: 'Event 1',
      resource: 1,
    },
    {
      id: 2,
      start: '2026-10-28T15:00',
      end: '2026-10-28T21:00',
      title: 'Event 2',
      resource: 1,
    },
    {
      id: 3,
      start: '2026-10-28T08:00',
      end: '2026-10-28T15:00',
      title: 'Event 3',
      resource: 2,
    },
    {
      id: 4,
      start: '2026-10-28T05:00',
      end: '2026-10-28T13:00',
      title: 'Event 4',
      resource: 3,
    },
    {
      id: 5,
      start: '2026-10-28T03:00',
      end: '2026-10-28T10:00',
      title: 'Event 5',
      resource: 4,
    },
    {
      id: 6,
      start: '2026-10-28T16:00',
      end: '2026-10-28T23:00',
      title: 'Event 6',
      resource: 4,
    },
    {
      id: 7,
      start: '2026-10-28T06:00',
      end: '2026-10-28T11:00',
      title: 'Event 7',
      resource: 5,
    },
    {
      id: 8,
      start: '2026-10-28T15:00',
      end: '2026-10-28T22:00',
      title: 'Event 8',
      resource: 5,
    },
    {
      id: 9,
      start: '2026-10-28T08:00',
      end: '2026-10-28T17:00',
      title: 'Event 9',
      resource: 6,
    },
    {
      id: 10,
      start: '2026-10-28T05:00',
      end: '2026-10-28T13:00',
      title: 'Event 10',
      resource: 7,
    },
    {
      id: 11,
      start: '2026-10-28T16:00',
      end: '2026-10-28T21:00',
      title: 'Event 11',
      resource: 7,
    },
    {
      id: 12,
      start: '2026-10-28T09:00',
      end: '2026-10-28T18:00',
      title: 'Event 12',
      resource: 8,
    },
  ],
  renderHour: function (args) {
    return (
      '<div class="md-date-header md-date-header-hour md-date-header-events-' +
      getEventOccurrence(args.events) +
      '">' +
      formatDate('h:mm A', args.date) +
      '</div>'
    );
  },
  renderHourFooter: function (args) {
    return getOccuppancy(args.events);
  },
});

mobiscroll.eventcalendar('#demo-day-header-template', {
  view: {
    timeline: {
      type: 'month',
    },
  },
  resources: myResources,
  data: [
    {
      id: 1,
      start: '2026-10-02T00:00',
      end: '2026-10-10T00:00',
      title: 'Event 1',
      resource: 1,
    },
    {
      id: 2,
      start: '2026-10-16T00:00',
      end: '2026-10-24T00:00',
      title: 'Event 2',
      resource: 1,
    },
    {
      id: 3,
      start: '2026-10-08T00:00',
      end: '2026-10-15T00:00',
      title: 'Event 3',
      resource: 2,
    },
    {
      id: 4,
      start: '2026-10-04T00:00',
      end: '2026-10-11T00:00',
      title: 'Event 4',
      resource: 3,
    },
    {
      id: 5,
      start: '2026-10-07T00:00',
      end: '2026-10-15T00:00',
      title: 'Event 5',
      resource: 4,
    },
    {
      id: 6,
      start: '2026-10-21T00:00',
      end: '2026-10-27T00:00',
      title: 'Event 6',
      resource: 4,
    },
    {
      id: 7,
      start: '2026-10-10T00:00',
      end: '2026-10-19T00:00',
      title: 'Event 7',
      resource: 5,
    },
    {
      id: 8,
      start: '2026-10-24T00:00',
      end: '2026-10-30T00:00',
      title: 'Event 8',
      resource: 5,
    },
    {
      id: 9,
      start: '2026-10-08T00:00',
      end: '2026-10-17T00:00',
      title: 'Event 9',
      resource: 6,
    },
    {
      id: 10,
      start: '2026-10-05T00:00',
      end: '2026-10-13T00:00',
      title: 'Event 10',
      resource: 7,
    },
    {
      id: 11,
      start: '2026-10-17T00:00',
      end: '2026-10-26T00:00',
      title: 'Event 11',
      resource: 7,
    },
    {
      id: 12,
      start: '2026-10-09T00:00',
      end: '2026-10-20T00:00',
      title: 'Event 12',
      resource: 8,
    },
  ],
  renderTimelineDay: function (args) {
    var date = args.date;

    return (
      '<div class="md-date-header md-date-header-day md-date-header-events-' +
      getEventOccurrence(args.events) +
      '">' +
      '<div class="md-date-header-day-name">' +
      formatDate('DDD', date) +
      '</div>' +
      '<div class="md-date-header-day-nr">' +
      formatDate('DD', date) +
      '</div></div>'
    );
  },
  renderDayFooter: function (args) {
    return getOccuppancy(args.events);
  },
});

mobiscroll.eventcalendar('#demo-week-header-template', {
  view: {
    timeline: {
      type: 'week',
      resolutionHorizontal: 'week',
      size: 6,
    },
  },
  resources: myResources,
  data: [
    {
      id: 1,
      start: '2026-10-10T00:00',
      end: '2026-10-24T00:00',
      title: 'Event 1',
      resource: 1,
    },
    {
      id: 2,
      start: '2026-11-02T00:00',
      end: '2026-11-18T00:00',
      title: 'Event 2',
      resource: 1,
    },
    {
      id: 3,
      start: '2026-10-27T00:00',
      end: '2026-11-13T00:00',
      title: 'Event 3',
      resource: 2,
    },
    {
      id: 4,
      start: '2026-11-04T00:00',
      end: '2026-11-27T00:00',
      title: 'Event 4',
      resource: 3,
    },
    {
      id: 5,
      start: '2026-10-24T00:00',
      end: '2026-11-02T00:00',
      title: 'Event 5',
      resource: 4,
    },
    {
      id: 6,
      start: '2026-11-10T00:00',
      end: '2026-11-24T00:00',
      title: 'Event 6',
      resource: 4,
    },
    {
      id: 7,
      start: '2026-10-20T00:00',
      end: '2026-11-03T00:00',
      title: 'Event 7',
      resource: 5,
    },
    {
      id: 8,
      start: '2026-11-08T00:00',
      end: '2026-11-19T00:00',
      title: 'Event 8',
      resource: 5,
    },
    {
      id: 9,
      start: '2026-10-28T00:00',
      end: '2026-11-15T00:00',
      title: 'Event 9',
      resource: 6,
    },
    {
      id: 10,
      start: '2026-10-09T00:00',
      end: '2026-10-23T00:00',
      title: 'Event 10',
      resource: 7,
    },
    {
      id: 11,
      start: '2026-11-05T00:00',
      end: '2026-11-22T00:00',
      title: 'Event 11',
      resource: 7,
    },
    {
      id: 12,
      start: '2026-10-24T00:00',
      end: '2026-11-13T00:00',
      title: 'Event 12',
      resource: 8,
    },
  ],
  renderWeek: function (args) {
    return (
      '<div class="md-date-header md-date-header-week md-date-header-events-' +
      getEventOccurrence(args.events) +
      '">' +
      formatDate('MMM DD', args.startDate) +
      ' - ' +
      formatDate('MMM DD', args.endDate) +
      '</div></div>'
    );
  },
  renderWeekFooter: function (args) {
    return getOccuppancy(args.events);
  },
});

mobiscroll.eventcalendar('#demo-month-header-template', {
  view: {
    timeline: {
      type: 'month',
      resolutionHorizontal: 'month',
      size: 6,
    },
  },
  resources: myResources,
  data: [
    {
      id: 1,
      start: '2026-09-10T00:00',
      end: '2026-11-07T00:00',
      title: 'Event 1',
      resource: 1,
    },
    {
      id: 2,
      start: '2027-01-01T00:00',
      end: '2027-02-08T00:00',
      title: 'Event 2',
      resource: 1,
    },
    {
      id: 3,
      start: '2026-11-27T00:00',
      end: '2026-12-23T00:00',
      title: 'Event 3',
      resource: 2,
    },
    {
      id: 4,
      start: '2026-10-25T00:00',
      end: '2026-11-19T00:00',
      title: 'Event 4',
      resource: 3,
    },
    {
      id: 5,
      start: '2026-11-10T00:00',
      end: '2026-12-18T00:00',
      title: 'Event 5',
      resource: 4,
    },
    {
      id: 6,
      start: '2027-02-24T00:00',
      end: '2027-03-27T00:00',
      title: 'Event 6',
      resource: 4,
    },
    {
      id: 7,
      start: '2026-12-02T00:00',
      end: '2027-01-13T00:00',
      title: 'Event 7',
      resource: 5,
    },
    {
      id: 8,
      start: '2027-02-08T00:00',
      end: '2027-03-06T00:00',
      title: 'Event 8',
      resource: 5,
    },
    {
      id: 9,
      start: '2026-12-20T00:00',
      end: '2027-01-17T00:00',
      title: 'Event 9',
      resource: 6,
    },
    {
      id: 10,
      start: '2026-10-01T00:00',
      end: '2026-11-14T00:00',
      title: 'Event 10',
      resource: 7,
    },
    {
      id: 11,
      start: '2027-02-14T00:00',
      end: '2027-03-20T00:00',
      title: 'Event 11',
      resource: 7,
    },
    {
      id: 12,
      start: '2026-11-24T00:00',
      end: '2026-12-20T00:00',
      title: 'Event 12',
      resource: 8,
    },
  ],
  renderMonth: function (args) {
    return (
      '<div class="md-date-header md-date-header-month md-date-header-events-' +
      getEventOccurrence(args.events) +
      '">' +
      formatDate('MMM', args.date) +
      '</div>'
    );
  },
  renderMonthFooter: function (args) {
    return getOccuppancy(args.events);
  },
});

mobiscroll.eventcalendar('#demo-quarter-header-template', {
  view: {
    timeline: {
      type: 'year',
      resolutionHorizontal: 'quarter',
      size: 1,
    },
  },
  resources: myResources,
  data: [
    {
      id: 1,
      start: '2026-09-10T00:00',
      end: '2026-11-07T00:00',
      title: 'Event 1',
      resource: 1,
    },
    {
      id: 2,
      start: '2027-01-01T00:00',
      end: '2027-02-08T00:00',
      title: 'Event 2',
      resource: 1,
    },
    {
      id: 3,
      start: '2026-11-27T00:00',
      end: '2026-12-23T00:00',
      title: 'Event 3',
      resource: 2,
    },
    {
      id: 4,
      start: '2026-10-25T00:00',
      end: '2026-11-19T00:00',
      title: 'Event 4',
      resource: 3,
    },
    {
      id: 5,
      start: '2026-11-10T00:00',
      end: '2026-12-18T00:00',
      title: 'Event 5',
      resource: 4,
    },
    {
      id: 6,
      start: '2027-02-24T00:00',
      end: '2027-03-27T00:00',
      title: 'Event 6',
      resource: 4,
    },
    {
      id: 7,
      start: '2026-12-02T00:00',
      end: '2027-01-13T00:00',
      title: 'Event 7',
      resource: 5,
    },
    {
      id: 8,
      start: '2027-02-08T00:00',
      end: '2027-03-06T00:00',
      title: 'Event 8',
      resource: 5,
    },
    {
      id: 9,
      start: '2026-12-20T00:00',
      end: '2027-01-17T00:00',
      title: 'Event 9',
      resource: 6,
    },
    {
      id: 10,
      start: '2026-10-01T00:00',
      end: '2026-11-14T00:00',
      title: 'Event 10',
      resource: 7,
    },
    {
      id: 11,
      start: '2027-02-14T00:00',
      end: '2027-03-20T00:00',
      title: 'Event 11',
      resource: 7,
    },
    {
      id: 12,
      start: '2026-11-24T00:00',
      end: '2026-12-20T00:00',
      title: 'Event 12',
      resource: 8,
    },
  ],
  renderQuarter: function (args) {
    return (
      '<div class="md-date-header md-date-header-quarter md-date-header-events-' +
      getEventOccurrence(args.events) +
      '">Quarter ' +
      (args.date.getMonth() / 3 + 1) +
      '</div>'
    );
  },
  renderQuarterFooter: function (args) {
    return getOccuppancy(args.events);
  },
});

mobiscroll.eventcalendar('#demo-year-header-template', {
  view: {
    timeline: {
      type: 'year',
      resolutionHorizontal: 'year',
      size: 6,
    },
  },
  resources: myResources,
  data: [
    {
      id: 1,
      start: '2027-04-10T00:00',
      end: '2028-08-07T00:00',
      title: 'Event 1',
      resource: 1,
    },
    {
      id: 2,
      start: '2029-06-01T00:00',
      end: '2030-09-08T00:00',
      title: 'Event 2',
      resource: 1,
    },
    {
      id: 3,
      start: '2028-03-27T00:00',
      end: '2029-10-23T00:00',
      title: 'Event 3',
      resource: 2,
    },
    {
      id: 4,
      start: '2028-08-25T00:00',
      end: '2029-11-19T00:00',
      title: 'Event 4',
      resource: 3,
    },
    {
      id: 5,
      start: '2027-09-10T00:00',
      end: '2029-04-18T00:00',
      title: 'Event 5',
      resource: 4,
    },
    {
      id: 6,
      start: '2030-01-24T00:00',
      end: '2031-05-27T00:00',
      title: 'Event 6',
      resource: 4,
    },
    {
      id: 7,
      start: '2025-04-02T00:00',
      end: '2026-08-13T00:00',
      title: 'Event 7',
      resource: 5,
    },
    {
      id: 8,
      start: '2028-04-08T00:00',
      end: '2029-11-06T00:00',
      title: 'Event 8',
      resource: 5,
    },
    {
      id: 9,
      start: '2028-01-20T00:00',
      end: '2029-08-17T00:00',
      title: 'Event 9',
      resource: 6,
    },
    {
      id: 10,
      start: '2026-10-01T00:00',
      end: '2027-11-14T00:00',
      title: 'Event 10',
      resource: 7,
    },
    {
      id: 11,
      start: '2029-10-14T00:00',
      end: '2030-11-20T00:00',
      title: 'Event 11',
      resource: 7,
    },
    {
      id: 12,
      start: '2029-02-24T00:00',
      end: '2031-04-20T00:00',
      title: 'Event 12',
      resource: 8,
    },
  ],
  renderYear: function (args) {
    return (
      '<div class="md-date-header md-date-header-year md-date-header-events-' +
      getEventOccurrence(args.events) +
      '">' +
      formatDate('YYYY', args.date) +
      '</div>'
    );
  },
  renderYearFooter: function (args) {
    return getOccuppancy(args.events);
  },
});
```

### HTML

```html
<div class="md-date-header-template">
    <div class="mbsc-form-group">
        <div class="mbsc-form-group-title">Hourly header template</div>
        <div id="demo-hour-header-template"></div>
    </div>
    <div class="mbsc-form-group">
        <div class="mbsc-form-group-title">Daily header template</div>
        <div id="demo-day-header-template"></div>
    </div>
    <div class="mbsc-form-group">
        <div class="mbsc-form-group-title">Weekly header template</div>
        <div id="demo-week-header-template"></div>
    </div>
    <div class="mbsc-form-group">
        <div class="mbsc-form-group-title">Monthly header template</div>
        <div id="demo-month-header-template"></div>
    </div>
    <div class="mbsc-form-group">
        <div class="mbsc-form-group-title">Quarterly header template</div>
        <div id="demo-quarter-header-template"></div>
    </div>
    <div class="mbsc-form-group">
        <div class="mbsc-form-group-title">Yearly header template</div>
        <div id="demo-year-header-template"></div>
    </div>
</div>
```

### CSS

```css
.md-date-header-template .mbsc-timeline-header-week-text {
    display: block;
}

.md-date-header {
    text-align: center;
    border-radius: 8px;
    line-height: 20px;
}

.md-date-header-day {
    padding: 3px;
    margin: 4px;
}

.md-date-header-hour {
    font-size: 12px;
    padding: 0;
    margin: 3px;
    color: #000;
    font-weight: 600;
}

.md-date-header-week,
.md-date-header-month,
.md-date-header-quarter,
.md-date-header-year {
    padding: 0 8px;
    margin: 3px;
    color: initial;
    font-weight: 600;
}

.md-date-header-template .mbsc-timeline-footer-week {
    text-align: center;
}

.md-date-header-day-name {
    font-size: 13px;
    font-weight: 600;
}

.md-date-header-day-nr {
    font-size: 14px;
}

.md-date-header-events-one {
    background: #e9f5e4;
}

.md-date-header-events-few {
    background: #d0e3c6;
}

.md-date-header-events-more {
    background: #8ca77e;
}

.md-date-footer {
    font-size: 14px;
    font-weight: 600;
    text-align: center;
    line-height: 26px;
}
```

## Implementation notes

- Render 6 separate `Eventcalendar` instances on the same page, one for each timeline resolution: hourly, daily, weekly, monthly, quarterly, and yearly. All instances share the same 8 resources (Resource A-H, each with a distinct color). Each instance has its own independent event dataset of 12 events positioned with `dyndatetime` offsets.
- **View configurations:**
  - Hourly: `timeline: { type: 'day' }` - one column per hour for today
  - Daily: `timeline: { type: 'month' }` - one column per day for the current month
  - Weekly: `timeline: { type: 'week', resolutionHorizontal: 'week', size: 6 }` - 6 week columns
  - Monthly: `timeline: { type: 'month', resolutionHorizontal: 'month', size: 6 }` - 6 month columns
  - Quarterly: `timeline: { type: 'year', resolutionHorizontal: 'quarter', size: 1 }` - 4 quarter columns within 1 year
  - Yearly: `timeline: { type: 'year', resolutionHorizontal: 'year', size: 6 }` - 6 year columns
- Implement two helper functions shared across all renderers:
  - `getEventOccurrence(events)` - categorizes the array length as `'none'` (0), `'one'` (1), `'few'` (2-3), or `'more'` (4+). Used to vary the visual density indicator in headers.
  - `getOccuppancy(events)` - counts distinct resource IDs present in the events array, then returns `(count / totalResources * 100).toFixed(0)`. Used to show the occupancy percentage in footers.
- Each instance uses a **header renderer** and a **footer renderer**. Both receive `args` with `args.events` (all events overlapping that column) and `args.date`. The week header additionally receives `args.startDate` and `args.endDate`. Use `formatDate` for date formatting:
  - Hour: `formatDate('h:mm A', args.date)`
  - Day: `formatDate('DDD', args.date)` (abbreviated day name) + `formatDate('DD', args.date)` (day number)
  - Week: `formatDate('MMM DD', args.startDate) + ' - ' + formatDate('MMM DD', args.endDate)`
  - Month: `formatDate('MMM', args.date)`
  - Quarter: compute as `args.date.getMonth() / 3 + 1` and render `'Quarter N'`
  - Year: `formatDate('YYYY', args.date)`
- All footer renderers are identical: render `getOccuppancy(args.events) + ' %'`.
- The renderer option names differ per resolution. React names → Angular template option → Vue slot:
  - Hour header: `renderHour` → `hourTemplate` → `#hour`
  - Hour footer: `renderHourFooter` → `hourFooterTemplate` → `#hourFooter`
  - Day header: `renderTimelineDay` → `timelineDayTemplate` → `#timelineDay`
  - Day footer: `renderDayFooter` → `dayFooterTemplate` → `#dayFooter`
  - Week header: `renderWeek` → `weekTemplate` → `#week`
  - Week footer: `renderWeekFooter` → `weekFooterTemplate` → `#weekFooter`
  - Month header: `renderMonth` → `monthTemplate` → `#month`
  - Month footer: `renderMonthFooter` → `monthFooterTemplate` → `#monthFooter`
  - Quarter header: `renderQuarter` → `quarterTemplate` → `#quarter`
  - Quarter footer: `renderQuarterFooter` → `quarterFooterTemplate` → `#quarterFooter`
  - Year header: `renderYear` → `yearTemplate` → `#year`
  - Year footer: `renderYearFooter` → `yearFooterTemplate` → `#yearFooter`

## Run it in your project

1. If not signed up yet, or don't own a license, you can [start a free trial](https://mobiscroll.com/starttrial).

2. Clone the repository:

   ```
   git clone https://github.com/acidb/mobiscroll-demos-javascript.git
   ```

3. If not installed yet, install the Mobiscroll CLI from NPM:

   ```
   npm install -g @mobiscroll/cli
   ```

4. Config Mobiscroll - open a terminal window in the root directory and run:

   ```
   mobiscroll config javascript
   ```

5. Install dependencies - open a terminal window in the root directory and run:

   ```
   npm install
   ```

6. Run the project:

   ```
   npm run dev
   ```

On a trial, add `--trial` to the config command: `mobiscroll config javascript --trial`.

Running a demo locally requires a Mobiscroll trial or a license; setup is per framework at https://mobiscroll.com/docs/javascript/getting-started/installation

## Related demos

- Previous: [Custom Cell Content](https://demo.mobiscroll.com/javascript/timeline/dynamic-cell-content-template.md) - Add workload badges, status icons, and a hover quick-add button to each Javascript Timeline cell — and cap events per cell.
- Next: [Employee Shift Slots](https://demo.mobiscroll.com/javascript/timeline/shift-template.md) - Build a Javascript Timeline with custom shift slot headers — replace hourly intervals with Morning and Afternoon, each with its own time range and color.
