
| Demo | |
|---|---|
| Downloads | |
| Languages | JavaScript |
| Technologies | React |
Overview
Learn how to use the React Scheduler to display milestones.
By default, the milestones are displayed as green diamonds.
You can change the milestone color, shape, and description.
Milestones can show custom SVG icons.
You can use visual links to connect related milestones.
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.
React Scheduler Component
This milestone view is based on the React Scheduler component from the DayPilot Pro for JavaScript library.
The Scheduler is designed to display events but it can also display milestones - icons placed at specified date/time locations.
The component in src/scheduler/Scheduler.jsx initializes the milestone, resource, and link arrays using React state and passes them as props. The timeline starts in January 2027. The handlers below use the Scheduler control for local edits; changes remain in memory and are reset when you reload the page.
const Scheduler = () => {
const schedulerRef = useRef(null);
// ...
return (
<DayPilotScheduler
ref={schedulerRef}
startDate={"2027-01-01"}
days={365}
scale={"Day"}
timeHeaders={[{groupBy: "Month"}, {groupBy: "Day", format: "d"}]}
eventHtmlRightMargin={0}
eventDeleteHandling={"Disabled"}
timeRangeSelectedHandling={"Enabled"}
onTimeRangeSelecting={onTimeRangeSelecting}
onTimeRangeSelected={onTimeRangeSelected}
onBeforeEventRender={onBeforeEventRender}
events={events}
resources={resources}
links={links}
/>
);
};How to display a milestone in the scheduler?

To display a milestone, include a new item in the events array and set type: "Milestone".
The required data fields:
idstartresourcetype
You don’t have to include an end date: a milestone represents a single point in time. The text property is optional and is not displayed inside the icon. The description handler shown below displays it next to the milestone.
const [events] = useState(() => [
{
id: 1,
start: "2027-01-02T00:00:00",
resource: "A",
type: "Milestone",
text: "M1"
},
// ...
]);How to create a milestone by selecting a cell?
Click an empty cell to open a dialog for the milestone description. The onTimeRangeSelecting handler limits a drag selection to one day, because the new milestone needs only a start time. After the dialog closes, clear the selection and return without adding an item if the user canceled.
Use scheduler.events.add() to add the item to the selected resource. Include type: "Milestone" so it renders as a milestone instead of a regular event. When adding it dynamically, set end equal to start to make the zero duration explicit and ensure that future milestones render correctly.
const onTimeRangeSelecting = (args) => {
args.end = args.start.addDays(1);
};
const onTimeRangeSelected = async (args) => {
const scheduler = args.control;
const modal = await DayPilot.Modal.prompt("Create a new milestone:", "Milestone 1");
scheduler.clearSelection();
if (modal.canceled) {
return;
}
scheduler.events.add({
id: DayPilot.guid(),
start: args.start,
end: args.start,
resource: args.resource,
type: "Milestone",
text: modal.result
});
};How to use custom milestone colors?
You can set a custom color of the milestone icon using the backColor property of the data object.
Blue milestone
This example creates a blue “Milestone 2”. The blue milestone color is set using the backColor property ("#3c78d8").
![]()
{
id: 2,
start: "2027-01-03T00:00:00",
resource: "C",
type: "Milestone",
backColor: "#3c78d8",
text: "Milestone 2"
},Yellow milestone
The yellow “M3” milestone sets the backColor value to "#f1c232".
![]()
{
id: 3,
start: "2027-01-04T00:00:00",
resource: "D",
type: "Milestone",
backColor: "#f1c232",
text: "M3"
},Red milestone
This is an “M4” milestone with a red background (backColor: "#cc0000").
![]()
{
id: 4,
start: "2027-01-02T00:00:00",
resource: "E",
type: "Milestone",
backColor: "#cc0000",
text: "M4"
},Color defined using CSS

Another option is to use a custom CSS class that will define the color:
Add a custom CSS class using the
cssClassproperty.Define the CSS class and use a custom
background-colorstyle.
{
id: 8,
start: "2027-01-09T12:00:00",
resource: "B",
type: "Milestone",
cssClass: "milestone-black",
text: "Milestone description"
},This example uses a custom .milestone-black CSS class which is defined in Scheduler.css:
.scheduler_default_task_milestone.milestone-black .scheduler_default_event_inner {
background-color: black;
}How to display a milestone description next to the icon?

Standard Scheduler events use the text property of the data object to specify the text that will be displayed in the event box. Milestones are too small to display any text inside, so by default the text property is ignored.
To display the text property value on the right side of a milestone, you can use the
htmlRightproperty.Remember that all properties that start with
html*treat the content as raw HTML which will not be encoded. If the text is provided by the user, it’s necessary to encode the text to prevent XSS attacks.
The following onBeforeEventRender handler uses the description specified using the text property and displays it next to the icon on the right side.
const onBeforeEventRender = (args) => {
if (args.data.type === "Milestone") {
args.data.htmlRight = DayPilot.Util.escapeHtml(args.data.text || "");
// ...
}
};To control the distance between the milestone icon and the description, use the eventHtmlRightMargin property. The default value is 10 (pixels).
Set the eventHtmlRightMargin prop to zero to place the description immediately next to the icon:
<DayPilotScheduler
// ...
eventHtmlRightMargin={0}
// ...
/>By default, the text is vertically centered. You can move the text to the top of the milestone area using CSS:
.scheduler_default_main .scheduler_default_event_right {
align-items: start;
}After reducing the margin and moving the text to the top it looks like this:

How to change the milestone icon shape?
The default shape is a diamond, created by rotating a square through 45 degrees using CSS. The sample includes this base rule in Scheduler.css to show the positioning and rotation; the custom classes below override it for other shapes.
.scheduler_default_task_milestone .scheduler_default_event_inner {
position: absolute;
top: 16%;
left: 16%;
right: 16%;
bottom: 16%;
background: #38761d;
border: 0 none;
transform: rotate(45deg);
}You can apply custom CSS styles to change the shape. Some common shapes can be created using rotation or border styles. The selectors below combine the milestone type class with your custom class so they take precedence over the default theme.
Circle icon
![]()
A circle milestone shape can be created using a high border-radius value.
The “M5” milestone displays a red circle using the following combination of properties and CSS styles:
Milestone:
{
id: 5,
start: "2027-01-05T00:00:00",
resource: "F",
type: "Milestone",
backColor: "#cc0000",
cssClass: "milestone-circle",
text: "M5"
},CSS:
.scheduler_default_task_milestone.milestone-circle .scheduler_default_event_inner {
border-radius: 25px;
}Square icon
![]()
The square icon can be created by resetting the rotation and corner radius used in the default milestone CSS.
Milestone:
{
id: 6,
start: "2027-01-08T00:00:00",
resource: "E",
type: "Milestone",
backColor: "#cc0000",
cssClass: "milestone-square",
text: "M6"
},CSS:
.scheduler_default_task_milestone.milestone-square .scheduler_default_event_inner {
transform: rotate(0);
border-radius: 0;
}SVG icon
![]()
You can also hide the default object and use a custom SVG icon for the milestone using an active area. The symbol value references the bold checkmark-4 symbol from DayPilot Icons, included in the sample’s public/icons/daypilot.svg sprite. A checkmark provides a clear visual marker for a completed milestone:
{
id: 7,
start: "2027-01-11T00:00:00",
resource: "E",
type: "Milestone",
cssClass: "milestone-blank",
text: "M7",
areas: [
{left: 0, right:0, top: 0, bottom: 0, symbol: "icons/daypilot.svg#checkmark-4", fontColor: "#00639e"}
]
},CSS:
.scheduler_default_task_milestone.milestone-blank .scheduler_default_event_inner {
transform: rotate(0);
border-radius: 0;
background: none;
}How to restrict drag and drop moving to horizontal (time) direction?
In some cases, the milestone only makes sense for the specified row (resource). To prevent drag and drop moving to a different row, use moveVDisabled: true in the event data object.
const onBeforeEventRender = (args) => {
if (args.data.type === "Milestone") {
// ...
args.data.moveVDisabled = true;
// ...
}
};How to delete a milestone?

The standard React Scheduler events use a built-in delete icon displayed in the upper-right corner. As the milestones use a limited event size and a specific shape, this icon will be misplaced and it’s better to turn it off.
To turn it off for all events, set the eventDeleteHandling prop to "Disabled":
<DayPilotScheduler
// ...
eventDeleteHandling={"Disabled"}
// ...
/>The sample disables the built-in delete icon globally. If your Scheduler displays both standard events and milestones, you can instead set eventDeleteHandling="Update" and hide the icon only for milestones. This keeps it available for standard events. Use the onBeforeEventRender event handler that lets you customize each event.
Set deleteDisabled on each milestone in the render handler. The sample also includes this per-item setting, so milestones remain protected if you enable the built-in icon for other events:
const onBeforeEventRender = (args) => {
if (args.data.type === "Milestone") {
// ...
args.data.deleteDisabled = true;
// ...
}
};You can also add an option to delete a milestone using a custom context menu. Right-click a milestone and choose Delete. The menu handler receives that milestone as args.source; use the React component ref to access the Scheduler control and remove it:
const onBeforeEventRender = (args) => {
if (args.data.type === "Milestone") {
// ...
args.data.contextMenu = new DayPilot.Menu({
items: [
{
text: "Delete",
onClick: (args) => schedulerRef.current.control.events.remove(args.source)
}
]
});
}
};How to add links between milestones?

The Scheduler supports links between events, allowing you to connect two milestones or a milestone with an event.
const [links] = useState(() => [
{
from: 4,
to: 5
},
// ...
]);By default, the links use the FinishToStart type, which connects the end of the source milestone (from) with the start of the target milestone (to).
You can specify the link type using the type property. Other supported types include "StartToStart", which connects the start sides. The sample’s second link explicitly uses "FinishToStart" to connect M5 to M6, so its connector leaves the finish side of M5 and enters the start side of M6:
{
from: 5,
to: 6,
type: "FinishToStart"
}History
September 17, 2026: Upgraded to React 19 and Vite, with DayPilot Pro 2026.3. Fixed milestone creation and context-menu deletion.
June 13, 2024: Upgraded to React 18, functional component and Hooks API, DayPilot Pro 2024.2.5951
December 27, 2021: Initial release
DayPilot




