Overview

  • How to export the current React Scheduler view to a multi-page PDF document.

  • The export toolbar lets you specify the page orientation (landscape/portrait) and the number of rows per page.

  • You can add custom text to the PDF document (header, page numbers).

  • The exported PDF file with the specified file name will be downloaded immediately.

  • Exporting the Scheduler to PDF is useful when you need to print the scheduler. It lets you fully control the content of each printed page.

  • The React project includes a trial version of DayPilot Pro for JavaScript (see License below)

License

Licensed for testing and evaluation purposes. Please see the license agreement included in the sample project. You can use the source code of the tutorial if you are a licensed user of DayPilot Pro for JavaScript. Buy a license.

Dependencies

Install DayPilot Pro (see npm.daypilot.org for an up-to-date NPM link). DayPilot will be used to display the Scheduler.

npm install https://npm.daypilot.org/daypilot-pro-react/trial/2026.3.7014.tar.gz

Install jsPDF. jsPDF is a library that allows PDF generation in JavaScript.

npm install jspdf

The downloadable sample uses React and Vite. Run npm install followed by npm run dev to start it locally.

React Scheduler Component Configuration

configuration

For an introduction to configuring the React Scheduler component, see the following tutorial:

Our PDF export project configures the Scheduler using JSX props. It displays May 2027 with one cell per day. The events and resources are initialized using React state; newly created events are added using the Scheduler control API:

<DayPilotScheduler
  timeHeaders={[{groupBy: "Month"}, {groupBy: "Day", format: "d"}]}
  scale="Day"
  days={31}
  startDate="2027-05-01"
  treeEnabled={true}
  events={events}
  resources={resources}
  onTimeRangeSelected={async (args) => {
    const dp = args.control;
    const modal = await DayPilot.Modal.prompt("Create a new event:", "Event 1");
    dp.clearSelection();
    if (modal.canceled) { return; }
    dp.events.add({
      start: args.start,
      end: args.end,
      id: DayPilot.guid(),
      resource: args.resource,
      text: modal.result
    });
  }}
  ref={schedulerRef}
  onBeforeEventRender={onBeforeEventRender}
/>

PDF Export Toolbar

toolbar

Above the Scheduler, there is a toolbar that lets you customize the output PDF appearance.

Page orientation. This option allows users to select the orientation of the pages in the exported PDF. It is implemented as a dropdown menu with two options:

  • "Portrait" for a vertical layout.

  • "Landscape" for a horizontal layout.

Rows per page. This dropdown list lets users select the number of rows that will be included in every PDF page. The available options in this dropdown are 10, 20, and 30 rows per page.

<div className={"toolbar"}>
  <div className={"toolbar-item"}><b>Export</b></div>
  <label htmlFor="orientation">Orientation:</label>
  <select id="orientation" value={orientation} onChange={(ev) => setOrientation(ev.target.value)} >
    <option value={"portrait"}>Portrait</option>
    <option value={"landscape"}>Landscape</option>
  </select>
  <label htmlFor="rows">Rows per page:</label>
  <select id="rows" value={rowsPerPage} onChange={(ev) => setRowsPerPage(parseInt(ev.target.value))} >
    <option>10</option>
    <option>20</option>
    <option>30</option>
  </select>
  <button onClick={() => exportToPdf()}>Download PDF</button>
</div>

Scheduler PDF Export Function

The exportToPdf() function uses the exportAs() method of the React Scheduler to export a section of the Scheduler grid to a JPEG image. This image is then inserted into the PDF file.

  • Each page will contain the specified number of rows (stored in the rowsPerPage state variable).

  • The React Scheduler component can export the current viewport, full grid or a selected range. The range export will be used to specify the rows that are to be included in the exported image.

  • The output image is scaled proportionally to fit the available PDF area. The page width minus one inch reserves half-inch side margins; the page height minus two inches reserves the 1.5-inch top offset and a half-inch bottom margin. This also keeps all 26 rows on the page when exporting 30 rows in landscape orientation.

  • The first page includes a header with the text "Scheduler". You can use the doc.text() method to insert additional information to each page, such as a page number.

const exportToPdf = () => {

  const doc = new jsPDF(orientation, "in", "letter");
  doc.setFontSize(40);
  doc.text("Scheduler", 0.5, 1);

  const rows = getScheduler().rows.all();
  const pages = Math.ceil(rows.length / rowsPerPage);

  for (let i = 0; i < pages; i++) {
    const startId = i*rowsPerPage;
    let endId = i*rowsPerPage + rowsPerPage - 1;
    endId = Math.min(endId, rows.length - 1);

    const image = getScheduler().exportAs("jpeg", {
      area: "range",
      scale: 2,
      resourceFrom: rows[startId].id,
      resourceTo: rows[endId].id,
      quality: 0.95
    });

    const dimensions = image.dimensions();  // pixels
    const maxDimensions = { width: doc.internal.pageSize.getWidth() - 1, height: doc.internal.pageSize.getHeight() - 2 }; // inches
    const adjustedDimensions = shrink(dimensions, maxDimensions);  // inches

    doc.addImage(image.toDataUri(), 'JPEG', 0.5, 1.5, adjustedDimensions.width, adjustedDimensions.height);

    const last = (i === pages - 1);
    if (!last) {
      doc.addPage();
    }
  }

  const blob = doc.output("blob");
  DayPilot.Util.downloadBlob(blob, "scheduler.pdf");

};

The shrink() helper, included in the full source below, uses the larger of the width and height ratios to preserve the image proportions. The last page contains the remaining rows. Open the downloaded scheduler.pdf in a PDF viewer and use its Print command to print it.

Full Source Code

This is the full source code of our React component that displays an interactive Scheduler and an export toolbar that allows users to export the Scheduler content to a multi-page PDF file.

import { useRef, useState } from 'react';
import { DayPilot, DayPilotScheduler } from "daypilot-pro-react";
import { jsPDF } from "jspdf";

const Scheduler = () => {
  const schedulerRef = useRef();

  const [orientation, setOrientation] = useState("portrait");
  const [rowsPerPage, setRowsPerPage] = useState(10);

  const onBeforeEventRender = (args) => {
    args.data.barHidden = true;
    args.data.backColor = "#6aa84f";
    args.data.borderColor = "#38761d";
    args.data.fontColor = "#ffffff";
  };


  const [events] = useState([
      {
        id: 1,
        text: "Event 1",
        start: "2027-05-02T00:00:00",
        end: "2027-05-05T00:00:00",
        resource: "B"
      },
      {
        id: 2,
        text: "Event 2",
        start: "2027-05-06T00:00:00",
        end: "2027-05-10T00:00:00",
        resource: "B",
      },
      {
        id: 3,
        text: "Event 3",
        start: "2027-05-02T00:00:00",
        end: "2027-05-08T00:00:00",
        resource: "E",
      },
      {
        id: 4,
        text: "Event 4",
        start: "2027-05-02T00:00:00",
        end: "2027-05-08T00:00:00",
        resource: "G",
      }
  ]);

  const [resources] = useState([
      {name: "Resource A", id: "A"},
      {name: "Resource B", id: "B"},
      {name: "Resource C", id: "C"},
      {name: "Resource D", id: "D"},
      {name: "Resource E", id: "E"},
      {name: "Resource F", id: "F"},
      {name: "Resource G", id: "G"},
      {name: "Resource H", id: "H"},
      {name: "Resource I", id: "I"},
      {name: "Resource J", id: "J"},
      {name: "Resource K", id: "K"},
      {name: "Resource L", id: "L"},
      {name: "Resource M", id: "M"},
      {name: "Resource N", id: "N"},
      {name: "Resource O", id: "O"},
      {name: "Resource P", id: "P"},
      {name: "Resource Q", id: "Q"},
      {name: "Resource R", id: "R"},
      {name: "Resource S", id: "S"},
      {name: "Resource T", id: "T"},
      {name: "Resource U", id: "U"},
      {name: "Resource V", id: "V"},
      {name: "Resource W", id: "W"},
      {name: "Resource X", id: "X"},
      {name: "Resource Y", id: "Y"},
      {name: "Resource Z", id: "Z"},
  ]);

  const getScheduler = () => schedulerRef.current?.control;

  const exportToPdf = () => {

    const doc = new jsPDF(orientation, "in", "letter");
    doc.setFontSize(40);
    doc.text("Scheduler", 0.5, 1);

    const rows = getScheduler().rows.all();
    const pages = Math.ceil(rows.length / rowsPerPage);

    for (let i = 0; i < pages; i++) {
      const startId = i*rowsPerPage;
      let endId = i*rowsPerPage + rowsPerPage - 1;
      endId = Math.min(endId, rows.length - 1);

      const image = getScheduler().exportAs("jpeg", {
        area: "range",
        scale: 2,
        resourceFrom: rows[startId].id,
        resourceTo: rows[endId].id,
        quality: 0.95
      });

      const dimensions = image.dimensions();  // pixels
      const maxDimensions = { width: doc.internal.pageSize.getWidth() - 1, height: doc.internal.pageSize.getHeight() - 2 }; // inches
      const adjustedDimensions = shrink(dimensions, maxDimensions);  // inches

      doc.addImage(image.toDataUri(), 'JPEG', 0.5, 1.5, adjustedDimensions.width, adjustedDimensions.height);

      const last = (i === pages - 1);
      if (!last) {
        doc.addPage();
      }
    }

    const blob = doc.output("blob");
    DayPilot.Util.downloadBlob(blob, "scheduler.pdf");

  };

  function shrink(dimensions, max) {
    const widthRatio = dimensions.width / max.width;
    const heightRatio = dimensions.height / max.height;

    let ratio = Math.max(widthRatio, heightRatio);
    ratio = Math.max(ratio, 1);

    const width = dimensions.width / ratio;
    const height = dimensions.height / ratio;
    return { width, height};
  }

  return (
    <div>
      <div className={"toolbar"}>
        <div className={"toolbar-item"}><b>Export</b></div>
        <label htmlFor="orientation">Orientation:</label>
        <select id="orientation" value={orientation} onChange={(ev) => setOrientation(ev.target.value)} >
          <option value={"portrait"}>Portrait</option>
          <option value={"landscape"}>Landscape</option>
        </select>
        <label htmlFor="rows">Rows per page:</label>
        <select id="rows" value={rowsPerPage} onChange={(ev) => setRowsPerPage(parseInt(ev.target.value))} >
          <option>10</option>
          <option>20</option>
          <option>30</option>
        </select>
        <button onClick={() => exportToPdf()}>Download PDF</button>
      </div>
      <DayPilotScheduler
        timeHeaders={[{groupBy: "Month"}, {groupBy: "Day", format: "d"}]}
        scale="Day"
        days={31}
        startDate="2027-05-01"
        treeEnabled={true}
        events={events}
        resources={resources}
        onTimeRangeSelected={async (args) => {
          const dp = args.control;
          const modal = await DayPilot.Modal.prompt("Create a new event:", "Event 1");
          dp.clearSelection();
          if (modal.canceled) { return; }
          dp.events.add({
            start: args.start,
            end: args.end,
            id: DayPilot.guid(),
            resource: args.resource,
            text: modal.result
          });
        }}
        ref={schedulerRef}
        onBeforeEventRender={onBeforeEventRender}
      />
    </div>
  );
};
export default Scheduler;