javascript scheduler infinite scrolling

Infinite scrolling shifts the Scheduler timeline as the user approaches either edge. In DayPilot Pro for JavaScript since version 2026.4.7072, infiniteScrollingEnabled: "Auto" sizes the timeline buffer, shift distance and edge margin for the current viewport and time scale.

Automatic Sizing

Use Auto mode to adapt to different screen sizes and zoom levels without choosing the manual buffer settings:

const dp = new DayPilot.Scheduler("dp", {
  startDate: "2027-10-01",
  scale: "Day",
  cellWidth: 40,
  infiniteScrollingEnabled: "Auto",
  resources: [{name: "Room A", id: "A"}],
  dynamicLoading: true,
  onScroll: async args => {
    const query = new URLSearchParams({
      start: args.viewport.start.toString(), end: args.viewport.end.toString(),
      resources: args.viewport.resources.join(",")
    });
    try { const {data} = await DayPilot.Http.get("/api/events?" + query); args.events = data; }
    catch { args.clearEvents = false; args.events = []; args.control.message("Could not load events."); }
    finally { args.loaded(); }
  }
});
dp.scrollTo("2027-10-08");
dp.init();

The application endpoint /api/events must return a JSON array of events for the requested dates and resource IDs. Each event needs an id, start, end, resource and display text. Auto mode sizes the timeline; dynamic event loading supplies its data.

Auto mode adapts when the grid width, row header width or time scale changes. It supports generated timelines in left-to-right Resources view with fixed cell widths. It rejects rtl: true, scale: "Manual", cellWidthSpec: "Auto" and viewType: "Days".

Timeline Range and Viewport

Use visibleStart() and visibleEnd() to read the timeline range, and getViewPort() to read the dates and resources currently visible. In Auto mode, days, infiniteScrollingStepDays, and infiniteScrollingMargin do not set or report the automatically chosen range.

Manual Sizing

Set infiniteScrollingEnabled: true to keep manual control:

  • days sets the buffered date range (a finite number greater than zero).

  • infiniteScrollingStepDays sets the shift distance in days (a finite number greater than zero; default 30).

  • infiniteScrollingMargin sets the edge trigger width in pixels (a finite number greater than or equal to zero; default 50).

The timeline must be wider than the viewport plus both edge margins. Choose a shift distance that keeps the requested date in the new timeline. Increase days or reduce the margin/shift distance if the buffer is too short; use Auto mode to handle changing viewport sizes automatically. Invalid manual settings raise an error.

Call scrollTo() before init() to choose an initial date away from either edge.

Infinite scrolling is disabled by default. Your application remains responsible for loading event data and enforcing any limits on the dates users can navigate to.

Demo

Tutorial

API: infiniteScrollingEnabled, cellWidth, dynamicLoading.

The framework examples combine Auto timeline sizing with viewport-based event loading. Failed requests preserve existing events, and args.loaded() completes each loading cycle.

The framework examples use DayPilot Pro. For installation, see the Angular, React or Vue Scheduler guide.

Angular

Keep the asynchronous onScroll loader in config alongside infiniteScrollingEnabled and dynamicLoading.

import {Component} from "@angular/core";
import {DayPilot, DayPilotModule} from "daypilot-pro-angular";

@Component({
  selector: "app-scheduler-example",
  standalone: true,
  imports: [DayPilotModule],
  template: `
    
    <daypilot-scheduler [config]="config"></daypilot-scheduler>
  `
})
export class SchedulerExampleComponent {
  config: DayPilot.SchedulerConfig = {
    startDate: "2026-11-01",
    days: 30,
    scale: "Day",
    timeHeaders: [{groupBy: "Month"}, {groupBy: "Day", format: "d"}],
    resources: [{id: "A", name: "Room A"}, {id: "B", name: "Room B"}],
    events: [],
    infiniteScrollingEnabled: "Auto",
    cellWidth: 40,
    dynamicLoading: true,
    onScroll: async args => {
      const query = new URLSearchParams({start: args.viewport.start.toString(), end: args.viewport.end.toString(), resources: args.viewport.resources.join(",")});
      try { const {data} = await DayPilot.Http.get("/api/events?" + query); args.events = data; }
      catch { args.clearEvents = false; args.events = []; args.control.message("Could not load reservations."); }
      finally { args.loaded(); }
    }
  };

}

Tutorial

React

Pass the loader as onScroll and the two scrolling/loading options as individual props. The callback supplies results through args.events.

import React from "react";
import {DayPilot, DayPilotScheduler} from "daypilot-pro-react";

export default function SchedulerExample() {

  return <div>

    <DayPilotScheduler
      startDate={"2026-11-01"}
      days={30}
      scale={"Day"}
      timeHeaders={[{groupBy: "Month"}, {groupBy: "Day", format: "d"}]}
      resources={[{id: "A", name: "Room A"}, {id: "B", name: "Room B"}]}
      events={[]}
      infiniteScrollingEnabled={"Auto"}
      cellWidth={40}
      dynamicLoading={true}
      onScroll={async args => {
        const query = new URLSearchParams({start: args.viewport.start.toString(), end: args.viewport.end.toString(), resources: args.viewport.resources.join(",")});
        try { const {data} = await DayPilot.Http.get("/api/events?" + query); args.events = data; }
        catch { args.clearEvents = false; args.events = []; args.control.message("Could not load reservations."); }
        finally { args.loaded(); }
      }}
    />
  </div>;
}

Vue

Bind :onScroll to the loader declared in the script. Its DayPilot arguments contain the viewport dates and resource IDs to send to the endpoint.

<script setup>
import {DayPilot, DayPilotScheduler} from "daypilot-pro-vue";

const startDate = "2026-11-01";
const days = 30;
const scale = "Day";
const timeHeaders = [{groupBy: "Month"}, {groupBy: "Day", format: "d"}];
const resources = [{id: "A", name: "Room A"}, {id: "B", name: "Room B"}];
const events = [];
const infiniteScrollingEnabled = "Auto";
const cellWidth = 40;
const dynamicLoading = true;
const onScroll = async args => {
  const query = new URLSearchParams({start: args.viewport.start.toString(), end: args.viewport.end.toString(), resources: args.viewport.resources.join(",")});
  try { const {data} = await DayPilot.Http.get("/api/events?" + query); args.events = data; }
  catch { args.clearEvents = false; args.events = []; args.control.message("Could not load reservations."); }
  finally { args.loaded(); }
};

</script>

<template>
  <div>

    <DayPilotScheduler
      :startDate="startDate"
      :days="days"
      :scale="scale"
      :timeHeaders="timeHeaders"
      :resources="resources"
      :events="events"
      :infiniteScrollingEnabled="infiniteScrollingEnabled"
      :cellWidth="cellWidth"
      :dynamicLoading="dynamicLoading"
      :onScroll="onScroll"
    />
  </div>
</template>

Availability

Availability of this feature in DayPilot editions:

ProductLitePro
DayPilot for JavaScript
DayPilot for ASP.NET WebForms
DayPilot for ASP.NET MVC
DayPilot for Java