Overview

  • The Vue Scheduler component supports on-demand event loading. When enabled, the Scheduler loads events for the current viewport and requests more data while the user scrolls.

  • On-demand loading dramatically improves performance for large data sets. It reduces the initial loading time of the Vue Scheduler and limits how much event data is transferred from the backend at once.

  • To improve the user experience, you can preload events for areas adjacent to the current viewport and keep those events rendered in advance.

  • The downloadable sample uses Vue 3 + Vite frontend and a PHP backend with SQLite storage.

  • For a basic introduction to using the Vue Scheduler component, see Vue Scheduler: Build a Reservation Application in 5 Minutes.

License

Licensed for testing and evaluation purposes. Please see licensing information in the download package. You can use the source code of the tutorial if you are a licensed user of DayPilot Pro for JavaScript.

How to Enable On-Demand Event Loading in the Vue Scheduler

vue scheduler on demand event loading viewport

By default, the sample still defines a full-year Scheduler grid. In the current Vue 3 version, the Scheduler options are bound directly as component props and the timeline starts on the first day of the current year:

const startDate = DayPilot.Date.today().firstDayOfYear()
const timeHeaders = [{ groupBy: 'Month' }, { groupBy: 'Day', format: 'd' }]
const days = DayPilot.Date.today().daysInYear()

<DayPilotScheduler
  :timeHeaders="timeHeaders"
  scale="Day"
  :days="days"
  :startDate="startDate"
/>

If you want to load events for the whole visible date range in one request, you can use visibleStart() and visibleEnd() to get the grid boundaries. In this refreshed sample, the Scheduler shows the current year, so the initial viewport starts in January and the full visible range is much wider than the cells visible on the first screen.

For large data sets, it is better to switch to dynamic event loading. Whenever the viewport changes during scrolling, the Scheduler fires onScroll and lets you provide only the events needed for that area.

const onScroll = async args => {
  args.events = await loadViewportEvents(args.viewport)
  args.loaded()
}

<DayPilotScheduler
  :dynamicLoading="true"
  dynamicEventRendering="Disabled"
  :onScroll="onScroll"
/>

The current Vue wrapper loads resources asynchronously, so the sample also performs one explicit initial viewport load after the resources are available. This keeps the first rendered viewport populated before the user starts scrolling.

onMounted(async () => {
  await loadResources()
  await nextTick()

  const scheduler = schedulerRef.value?.control
  if (!scheduler) {
    return
  }

  const events = await loadViewportEvents(scheduler.getViewport())
  scheduler.update({ events })
})

How to Get the Viewport Rows

vue scheduler on demand event loading viewport rows

If you display only a few resources, limiting the horizontal date range may be enough. However, when the vertical axis contains many rows, it is useful to limit the query to the resources that are currently visible.

The refreshed sample keeps that logic inside a helper that reads the first and last visible resources from args.viewport.resources and sends them to the backend:

const resourceId = item => {
  if (item && typeof item === 'object') {
    return item.id
  }

  return item
}

const loadViewportEvents = async viewport => {
  if (!viewport?.resources?.length) {
    return []
  }

  const rstart = resourceId(viewport.resources[0])
  const rend = resourceId(viewport.resources[viewport.resources.length - 1])

  const result = await DayPilot.Http.get(`/api/backend_events_viewport.php?from=${viewport.start}&to=${viewport.end}&rstart=${rstart}&rend=${rend}`)
  return result.data
}

The small resourceId() helper keeps the code tolerant of the current wrapper data shape. It accepts either raw resource IDs or visible-resource objects and normalizes both forms to the ID that the PHP backend expects.

On the server side, the query filters by both the date range and the resource range:

$stmt = $db->prepare("SELECT * FROM event WHERE NOT ((end <= :start) OR (start >= :end)) AND resource_id >= :rstart AND resource_id <= :rend ORDER BY resource_id, start");

This keeps the payload limited to the rows that are visible in the viewport, which becomes increasingly important as the number of resources grows.

How to Preload Events for Adjacent Areas

To make scrolling feel smoother, the sample uses a small buffer and loads one month more to the left and one month more to the right of the current viewport.

const loadViewportEvents = async viewport => {
  if (!viewport?.resources?.length) {
    return []
  }

  const from = viewport.start.addMonths(-1)
  const to = viewport.end.addMonths(1)
  const rstart = resourceId(viewport.resources[0])
  const rend = resourceId(viewport.resources[viewport.resources.length - 1])

  const result = await DayPilot.Http.get(`/api/backend_events_viewport.php?from=${from}&to=${to}&rstart=${rstart}&rend=${rend}`)
  return result.data
}

The sample also sets dynamicEventRendering: 'Disabled'. Normally, progressive rendering keeps the DOM smaller by drawing only the events that are currently needed. In this case, the backend already limits the loaded data to a narrow range around the viewport, so rendering the buffered set immediately feels smoother when the user scrolls a short distance.

How to Run the Project

The updated download contains two folders:

  • frontend is the Vue 3 + Vite application.

  • backend is the PHP backend project.

Start the PHP backend first from the project root:

cd backend
php -S 127.0.0.1:8090 -t .

Then start the Vue frontend in a separate terminal:

cd frontend
npm install
npm run dev

The Vue frontend uses a Vite proxy to forward /api/* requests to the backend on port 8090 (see frontend/vite.config.js):

server: {
  proxy: {
    '/api': {
      target: 'http://127.0.0.1:8090',
      changeOrigin: true,
    },
  },
},

For a production build of the frontend, run:

cd frontend
npm run build

The PHP project uses SQLite. The database file (daypilot.sqlite) is created automatically in backend/api/ and seeded with 200 resources and sample events for the current year on the first run. The schema remains simple:

CREATE TABLE event (
    id          INTEGER  PRIMARY KEY AUTOINCREMENT,
    name        TEXT,
    start       DATETIME,
    [end]       DATETIME,
    resource_id INTEGER
);

CREATE TABLE resource (
    id   INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT
);

History

  • April 20, 2026: Rebuilt the sample on the current Vue 3 + Vite builder template, updated it to daypilot-pro-vue 2026.2.6907, refreshed the PHP seed data and initial viewport loading logic, and updated the article text and screenshots.