Skip to main content

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​

NameTypeRequiredDefaultDescription
urlstringYesN/AThe calendar's link. A webcal:// link is fetched over HTTPS
daysnumberNo7How many days of events to read, from the start of today
timeZonestringNoThe device's zoneThe 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​

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​