> ## Documentation Index
> Fetch the complete documentation index at: https://super-calendar.afonsojramos.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom events

> Type your events and render them your way.

## The event type

Events are plain objects. The only required fields are `start`, `end`, and
`title`; everything else is yours. `CalendarEvent<T>` is generic, so you can
attach your own fields and read them back in callbacks and renderers.

```tsx theme={null}
import type { CalendarEvent } from "@super-calendar/native";

type Meta = { id: string; calendarId: string; color: string };

const events: CalendarEvent<Meta>[] = [
  {
    title: "Standup",
    start: new Date(2026, 5, 23, 9, 0),
    end: new Date(2026, 5, 23, 9, 30),
    id: "1",
    calendarId: "work",
    color: "#1F6FEB",
  },
];
```

* `allDay` lays the event out in the all-day lane above the grid instead of in
  the columns. It's also inferred for midnight-to-midnight spans. Pass
  `showAllDayEventCell={false}` to hide the lane entirely (its events won't show).
  In the `schedule` view (which has no lane) an all-day event reads "All day"
  instead of a time range; override the wording with `allDayLabel` (e.g. for a
  different language).
* `disabled` opts an event out of drag interactions.
* `draggable`, `startEditable`, and `durationEditable` control per-event drag and
  resize (see the [drag guide](/guides/dragging)).
* Multi-day events draw as one continuous bar across the days they span in the
  month view, and as a per-day clipped segment on the week/day time grid.

## Render your own event

Pass `renderEvent` — a component (so it can use hooks) that receives
`RenderEventArgs`. It's used in every mode and for every event shape (timed,
all-day, multi-day), so you only write it once.

```tsx theme={null}
import type { RenderEventArgs } from "@super-calendar/native";
import { Pressable, Text } from "react-native";

function MyEvent({ event, onPress }: RenderEventArgs<Meta>) {
  return (
    <Pressable onPress={onPress} style={{ flex: 1, backgroundColor: event.color, borderRadius: 6 }}>
      <Text numberOfLines={1}>{event.title}</Text>
    </Pressable>
  );
}

<Calendar /* ... */ renderEvent={MyEvent} />;
```

<Tip>
  Wrapping events in a portaling overlay (a context menu, popover) on the web? Portal it into your
  app's React root, not `document.body` — see the [Web guide](/guides/web#portaling-overlays).
</Tip>

On the day/week grid a timed event's box is sized to its duration and given to your
renderer as `boxHeight`, so it tracks the grid's hour scale as the grid zooms or
resizes. Fit your content to `boxHeight`: the built-in renderer clamps its own
content, and a custom renderer should adapt (show less, or scroll) rather than
assume a fixed size, since content taller than the slot is clipped.

## Lighter touches

If you only want to tweak the built-in event box, you don't need a full
`renderEvent`:

* `eventCellStyle` — a style (or a function of the event) merged onto the
  built-in box.
* `keyExtractor` — a stable key per event; defaults to start-time + index.
* `showTime` shows the time range under the title (default true). The title comes
  first: on the day/week grid it fills the box with as many whole lines as fit
  (never a half-cut line), and the time only appears once a full line is free
  beneath it.
* `ellipsizeTitle` ends a single-line title (the all-day lane, month cells) with a
  trailing ellipsis when it overflows, instead of a hard clip (default false).

## Screen-reader labels

Each event announces a built-in label: its title plus the time range (or "all
day"), which the grid otherwise only shows visually. Override it per event with
`eventAccessibilityLabel`. It receives the event and a `{ mode, isAllDay, ampm }`
context, and its return value replaces the default label across every view.

```tsx theme={null}
<Calendar
  mode="week"
  date={date}
  events={events}
  eventAccessibilityLabel={(event, { isAllDay }) =>
    isAllDay
      ? `${event.title}, all day`
      : `${event.title}, from ${format(event.start, "h:mm a")} to ${format(event.end, "h:mm a")}`
  }
/>
```

The same prop is on the standalone `MonthView`, `MonthList`, and `TimeGrid`, so a
custom renderer and its label stay in sync.

## Background events

Set `display: "background"` to paint an event's time range as a shaded,
non-interactive band behind the grid instead of an event box — blocked time,
maintenance windows, public holidays:

```tsx theme={null}
const events: CalendarEvent[] = [
  { title: "Maintenance", start: at(9), end: at(12), display: "background" },
  { title: "Standup", start: at(9), end: at(10) }, // renders normally, on top
];
```

Background events shade the time grid and the resource timeline's lanes (an
all-day or multi-day one covers each day's full window). They take no overlap
column, render no chip in the month grid or agenda, and produce no year-view
dot. Restyle the band with the `backgroundEvent` slot or the theme's
`backgroundEvent` colour token.

## Loading events from a feed

`useEventSource` (from `@super-calendar/core`) owns the fetching for you: point
it at a JSON feed, an iCalendar feed (`.ics` URLs are parsed automatically), or
your own async function, and hand the result to any view. Set
`refetchIntervalMs` for a live feed; `refetch` reloads on demand, and a failed
refetch keeps the previous events while reporting `error`.

```tsx theme={null}
import { useEventSource } from "@super-calendar/core";

const { events, loading, error, refetch } = useEventSource("https://example.com/rooms.ics", {
  refetchIntervalMs: 5 * 60_000,
});

<Calendar mode="week" date={date} events={events} onChangeDate={setDate} />;
```

JSON feeds default to items with ISO `start`/`end` strings; pass `map` to
reshape anything else (rename fields, attach a `resourceId`, filter).

One nuance for function sources: swapping the function itself doesn't trigger a
refetch (inline functions change identity every render, which would loop).
The next interval tick or a manual `refetch()` always calls the latest
function, so change what the function reads, or call `refetch()` after
swapping it.
