Utilization
Add showUtilization and every resource shows a bar under its name: how much of the time it can be booked is taken, for the range on screen.
Turn it on
Section titled “Turn it on”<ResourceScheduler resources={resources} initialView={ViewType.Week} businessHours={{}} // Monday to Friday, 9 to 17 showUtilization/>Without showUtilization nothing changes. With it, the bar fills up to 100% and turns red above that. The text next to it is the rounded percentage, and “–” when the resource has no available time at all in the range (a Sunday, a week of leave). Hovering shows the hours, and screen readers read “6.5 of 8 hours booked” from a role="meter".
Set businessHours. Without any, every hour of the day counts as available, so a person who works 8 hours a day shows about 33%. Rooms and equipment that really are open all day can leave it out, or use businessHours: false on that resource.
How it is counted
Section titled “How it is counted”- Available time is the working time in the range: the resource’s own
businessHours, else thebusinessHoursprop, else every hour. From that, itsunavailableranges are cut out. Then it is multiplied bycapacity(below). - Booked time is the part of the resource’s events that falls inside the available time. Recurring events are expanded first. Nights, weekends and time off count on neither side, so an event from Wednesday 9:00 to Friday 17:00 counts as two working days, not as 56 hours.
- Events that overlap add up, so booking someone twice at once shows above 100%. That is the double-booking signal.
- The range is what you see: the day view’s visible hours (
dayStartHourtodayEndHour), the week, the month, the quarter or the year. Weekends hidden withhideWeekendsare still part of a month range, which only matters when you have nobusinessHours.
Capacity
Section titled “Capacity”capacity is how many bookings a resource can take at the same time. It defaults to 1 and multiplies the available time, so two bookings at once on a two-station lab are 100%, not 200%:
const resources: Resource[] = [ { id: "lab", name: "Test lab", capacity: 2, events: [] }, // two stations { id: "van", name: "Van 3", events: [] }, // one van: capacity 1];Capacity changes only the utilization numbers. It does not stop anyone from double booking: for that, use eventOverlap or an isValidDrop that counts the bookings.
Totals per column
Section titled “Totals per column”showColumnUtilization adds a row under the grid with one cell per column: how much of everyone’s available time is booked in it. In the day view a column is an hour (or a slot), in the other views a day. It answers “which day is overloaded?” at a glance, and it works with or without showUtilization.
<ResourceScheduler resources={resources} businessHours={{}} showColumnUtilization />The row is counted like the bars: each resource’s available time is its business hours minus unavailable, times capacity, and booked time is the part of its events inside that. The cell adds booked and available time over all resources, also those folded away inside a collapsed group, so 44 of 46 hours is 96% however many rows are on screen. A cell where nothing is available (a Sunday) shows “–”. The row sticks to the bottom of the scheduler while the rows scroll, and each cell is a role="meter" read out with its column and hours.
Without the component
Section titled “Without the component”getUtilization is the function the bars use, and columnUtilization the one behind the totals row. They are pure, so you can run it on a server, in a report or in your own header:
import { getUtilization } from "resource-scheduler";
const { bookedMinutes, availableMinutes, ratio } = getUtilization( ann, // a Resource with its events { start: new Date(2026, 9, 5), end: new Date(2026, 9, 12) }, // end is exclusive { businessHours: {} });// ratio is 0.58 for 58%, above 1 when overbooked, and null when nothing is availablecolumnUtilization(resources, columns, { businessHours }) adds up several resources over a list of columns (ranges in order, end exclusive) and returns one { bookedMinutes, availableMinutes, ratio } per column, for a chart or a report of your own. It reads each resource’s events once however many columns there are.
If your events are a flat list, merge them first with withEvents(resources, events). For a custom display, keep the visible range from onRangeChange and call getUtilization in renderResourceHeader:
const [range, setRange] = useState<{ start: Date; end: Date }>();
<ResourceScheduler resources={resources} businessHours={{}} onRangeChange={setRange} renderResourceHeader={(resource) => { const u = range && getUtilization(resource, range, { businessHours: {} }); return ( <div> {resource.name} {u?.ratio != null && <small> {Math.round(u.ratio * 100)}% booked</small>} </div> ); }}/>The hover text and the screen reader label come from labels.utilization(bookedHours, availableHours), with hours rounded to a tenth, and the name of the totals row is labels.columnUtilization (“Utilization”). Translate them like any other label.
Not included
Section titled “Not included”- A chart. The totals row is a bar and a percentage per column;
columnUtilizationgives you the numbers for a real chart. - Totals of a group or a selection of rows. The row always adds up every resource;
groupUtilizationgives a group’s total. - A weekly hours budget as a separate number. Express it as
businessHours(a part-timer working Monday, Wednesday and Friday) andunavailable(leave).