useCalendarFeed
Reads a published calendar from its iCalendar link and gives you its events, ready for any of the
calendar views. iCloud, Outlook and Google Calendar can all share a calendar as a link like this,
usually starting webcal:// or ending .ics.
Import
import { useCalendarFeed } from "~lib/calendar";
API
const { data: events } = useCalendarFeed(url, { days, timeZone });
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
url | string | Yes | N/A | The calendar's link. A webcal:// link is fetched over HTTPS |
days | number | No | 7 | How many days of events to read, from the start of today |
timeZone | string | No | The device's zone | The time zone to read the days in, such as "Europe/London" |
Return Value
data is a list of CalendarEvents, in the order they start: each event's name, start and
end, and its description, location and allDay when it has them, with calendar set to
the calendar's name. Pass it straight to a calendar view.
Your widget waits for the feed before it is drawn, as it does for useFetch, so data is always
there. A link that does not lead to a calendar shows an error.
What It Reads
- Repeating events are expanded into the days you asked for: daily, weekly, monthly and yearly repeats, including ones such as "the last Friday of every month", with any dates left out or added, and any single occurrence that was moved or changed. A rule that repeats by the hour shows on its first date only.
- Time zones come from the feed, whether it names them the usual way (
"Europe/London"), the Windows way Outlook uses ("GMT Standard Time"), or defines its own. - All-day events keep the dates they are written with, so a birthday is on the same day wherever the frame is.
- Cancelled events are left out.
Examples
An Agenda From a Shared Link
Let the person installing your widget paste their calendar's link into a Sensitive Input. A
private calendar's link lets anyone who has it read the calendar, so keep it hidden like a
password:
import { AgendaCalendar } from "~lib/calendar/agenda-calendar";
import { useCalendarFeed } from "~lib/calendar";
import { useInputs } from "~lib/use-inputs";
import { useScheduledDate } from "~lib/use-scheduled-date";
export const Widget = () => {
const { CALENDAR_LINK } = useInputs();
const { data: events } = useCalendarFeed(CALENDAR_LINK, { days: 3 });
const today = useScheduledDate();
return (
<AgendaCalendar
events={events}
startDate={today}
days={3}
width="100%"
height="100%"
/>
);
};
Several Calendars in One View
Read each link and put the events together. Each event's calendar says which one it came from:
const { data: work } = useCalendarFeed(WORK_LINK);
const { data: family } = useCalendarFeed(FAMILY_LINK);
return (
<ScheduleCalendar events={[...work, ...family]} width="100%" height="100%" />
);
parseCalendarFeed
If you already have the feed's text, parseCalendarFeed reads it the same way, for the window
you give it:
import { parseCalendarFeed } from "~lib/calendar";
const events = parseCalendarFeed(text, {
timeZone: "Europe/London",
from: new Date("2026-10-05T00:00:00Z"),
to: new Date("2026-10-12T00:00:00Z"),
});
It lists the events that overlap the window from from to to, and reads a time the feed gives
no zone for in timeZone. It throws when the text holds no calendar.
See Also
- AgendaCalendar - A few days side by side
- ScheduleCalendar - Upcoming events grouped by day
- useFetch - For any other API