npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-agenda-kit

v0.1.0

Published

A fast expandable calendar, agenda list and day timeline for React Native — declared row heights, worklet-driven scroll sync, zero measurement.

Readme

react-native-agenda-kit

An expandable month/week calendar, an agenda list and a day timeline for React Native — built to stay smooth under a hard fling on a low-end Android phone.

npm license platforms types

Install · Quick start · Features · Docs · API · Migration

The three surfaces of react-native-agenda-kit

Three surfaces share one date owner and one integer index space, so tapping a day, flinging the agenda, swiping the week strip and paging the timeline all stay in sync without latches, timers or changedItems bookkeeping.

Why another calendar

It is a rewrite of the parts of react-native-calendars that get slow, with the same public API so a migration is a copy, not a rewrite.

  • Nothing is measured. Row heights are declared per row type. A wrong number clips inside its slot instead of shifting the list — so a fling never jumps, and there is no onLayout round-trip on the critical path.
  • Scroll sync runs on the UI thread. Every scroll handler is a worklet; the JS thread is told only at settle. The originating scroller ignores moves it produced itself, which is the whole echo-proofing story.
  • A windowed agenda with a moving backdrop. Rows entering the window during a fast fling are deferred and skeleton tiles show through their slots, so a fling that outruns JS never shows bare ground.
  • No date library. Civil-date arithmetic is integer maths on epoch days. Worklets never touch strings.
  • One accent, eleven colours. The whole visual surface is eleven colour tokens and three font families.

How the pieces fit together


Install

npm install react-native-agenda-kit
npm install react-native-reanimated react-native-worklets react-native-gesture-handler

With Expo, use npx expo install for the three peers so the versions match your SDK.

Every scroll handler is a worklet, so the worklets Babel plugin must run over this package. With Expo (babel-preset-expo) it already does. Bare React Native, in babel.config.js:

module.exports = {
  presets: ['module:@react-native/babel-preset'],
  plugins: ['react-native-worklets/plugin'], // must be last
};

Then wrap your app once, as gesture-handler requires, and rebuild:

<GestureHandlerRootView style={{ flex: 1 }}>{/* … */}</GestureHandlerRootView>

If the plugin is missing, the kit says so by name in development rather than scrolling wrongly.

📖 Full walkthrough, including Expo vs bare and the rebuild step: docs/installation.md


Compatibility

| Peer | Minimum | Developed against | | --- | --- | --- | | react | ≥ 18.0.0 | 19.2.0 | | react-native | ≥ 0.74.0 | 0.83.6 | | react-native-reanimated | ≥ 4.0.0 | 4.2.1 | | react-native-worklets | ≥ 0.3.0 | 0.7.4 | | react-native-gesture-handler | ≥ 2.16.0 | 2.30.0 |

| | | | --- | --- | | New Architecture | ✅ Required — Reanimated 4 runs only on it | | Old Architecture | ❌ | | iOS / Android | ✅ Supported and tested | | Web | ❌ Not supported | | Expo | ✅ Managed and bare; use a development build rather than Expo Go | | TypeScript | ✅ Types ship for both ESM and CJS |

📖 Module formats, accessibility behaviour, font scaling, time zones: docs/compatibility.md


Quick start

import { useMemo, useState } from 'react';
import { Text, View } from 'react-native';
import {
  AgendaList,
  CalendarProvider,
  ExpandableCalendar,
  buildRange,
  type AgendaSection,
  type DateString,
} from 'react-native-agenda-kit';

type Item = { id: string; time: string; title: string };

const ROW_HEIGHTS = { Regular: 64 }; // declared, never measured

export default function Screen() {
  const [date, setDate] = useState<DateString>('2026-09-10');

  // The index space: a padded-to-whole-weeks window. Build it once.
  const range = useMemo(
    () => buildRange(new Date(2026, 6, 1), new Date(2026, 10, 30), 1),
    [],
  );

  const sections: AgendaSection<Item>[] = useMemo(
    () => [
      {
        title: '2026-09-10',
        data: [{ id: 'a', time: '09:00', title: 'Consultation' }],
      },
    ],
    [],
  );

  return (
    <CalendarProvider
      range={range}
      date={date}
      onDateChanged={(next) => setDate(next)}
      showTodayButton
      todayLabel="Today"
    >
      <ExpandableCalendar closeOnDayPress>
        <AgendaList<Item>
          sections={sections}
          itemHeightByType={ROW_HEIGHTS}
          renderItem={({ item }) => (
            <View style={{ height: ROW_HEIGHTS.Regular, justifyContent: 'center' }}>
              <Text>{item.time} · {item.title}</Text>
            </View>
          )}
          renderSectionHeader={(title) => <Text>{title}</Text>}
          stickySectionHeadersEnabled
        />
      </ExpandableCalendar>
    </CalendarProvider>
  );
}

ExpandableCalendar's children are rendered in the body it pushes down — put the agenda or the timeline there.

A day timeline instead

import { TimelineList } from 'react-native-agenda-kit';

<TimelineList
  events={{
    '2026-09-10': [
      { id: '1', start: '2026-09-10 09:00:00', end: '2026-09-10 09:30:00', title: 'Consultation' },
    ],
  }}
  showNowIndicator
  scrollToFirst
  timelineProps={{
    format24h: true,
    hourHeight: 60,
    availableHours: { '2026-09-10': [{ start: 8, end: 18 }] },
    onEventPress: (event) => console.log(event.id),
  }}
/>

Neighbouring dates must be present in events or a swipe shows a blank day. Note availableHours takes the available windows, not the unavailable ones — the one deliberate rename from react-native-calendars.

📖 Recipes for data loading, view switching, dark mode, refresh, localisation and more: docs/usage.md


Features

Every behaviour, its default, and the prop that switches it. The full switchboard — including what is deliberately always on, and why — is in docs/features.md.

ExpandableCalendar — week strip ⇄ month grid

| Feature | Default | Switch | | --- | --- | --- | | Drag the knob to expand | on | disablePan | | Open at mount | closed | initialPosition="open" | | Collapse on day press | on | closeOnDayPress={false} | | Collapse when the list scrolls | on | closeOnListScroll={false} | | Drag distance before it commits | 30 px | openThreshold, closeThreshold | | Dots, selection, disabled days | off | markedDates | | Month names over each 1st | off | monthShortLabels | | Screen-reader labels | off | knobAccessibilityLabel, dayAccessibilityLabel | | Toggle from your own button | — | ref.current.toggleCalendarPosition() |

AgendaList — windowed section list

| Feature | Default | Switch | | --- | --- | --- | | Declared row heights | — | itemHeightByType (required) | | Row type resolution | item.itemCustomHeightType → 'Regular' | getItemType | | Section header height | 32 px | sectionHeaderHeight | | Sticky headers (a real overlay) | off | stickySectionHeadersEnabled | | Pull to refresh | off | onRefresh + refreshing | | Skeleton backdrop during a fling | off | backdrop, rowGroundColor | | Drag / settle callbacks | off | onDragBegin, onSettle | | Scroll deceleration | 0.985 | decelerationRate | | Imperative scrolling | — | ref → scrollToDate, scrollToSection, scrollToOffset |

TimelineList — day view

| Feature | Default | Switch | | --- | --- | --- | | Now indicator | off | showNowIndicator | | Open at now / first event / a time | top of day | scrollToNow, scrollToFirst, initialTime | | 24-hour clock | on | timelineProps.format24h | | Hour height | 100 px | timelineProps.hourHeight | | Available-hours wash | off | timelineProps.availableHours (+ availableHoursColor) | | Overlap packing | always on | overlapEventsSpacing, rightEdgeSpacing | | Custom event blocks | default fill | renderEvent, onEventPress, or event.color | | Day paging | always on | include neighbouring dates in events |

CalendarProvider — the date owner

| Feature | Default | Switch | | --- | --- | --- | | Today pill | off | showTodayButton + todayLabel | | Lift the pill above a tab bar | 0 | todayBottomMargin | | Date / month callbacks | — | onDateChanged, onMonthChange | | Dark theme | light | themeBase={darkTheme} | | Brand colours and fonts | built-in | theme |

Always on, on purpose

Worklet scroll handlers · declared row heights · windowing · echo-proofing the controlled date · allowFontScaling={false} inside the kit · integer epoch-day date maths. Why each one is not a prop →


Theming

Eleven colours and three font families. If a colour is not on this list, the kit does not draw it.

The eleven colour tokens in light and dark

import { CalendarProvider, darkTheme } from 'react-native-agenda-kit';

// Hoist it: a fresh object every render rebuilds every stylesheet in the tree.
const THEME = {
  colors: { accent: '#0F766E', accentFill: '#0F766E' },
  fonts: { regular: 'Inter-Regular', medium: 'Inter-Medium', semiBold: 'Inter-SemiBold' },
};

<CalendarProvider theme={THEME} themeBase={darkTheme} {...rest}>

lightTheme and darkTheme are exported, as are the static metrics (SPACING, FONT_SIZE, RADIUS, ANIMATION, EASING) if you want to match them elsewhere. AgendaKitThemeProvider is available if you would rather theme above CalendarProvider.

📖 What each token paints, ink vs fill, and what is deliberately not themeable: docs/theming.md


Migrating from react-native-calendars

The component and prop names match deliberately. Six differences matter:

| | react-native-calendars | this kit | | --- | --- | --- | | Row heights | measured | declared via itemHeightByType (required) | | Sticky headers | emulated | a real fixed overlay | | unavailableHours | the unavailable windows | availableHours — the available ones | | theme object | a large nested theme | eleven colours + three fonts | | Strings | ships English defaults | ships none — pass todayLabel, monthShortLabels, a11y labels | | Date range | pastScrollRange / futureScrollRange | one range from buildRange |

ExpandableCalendar still accepts firstDay, renderHeader, headerStyle, minDate, maxDate, pastScrollRange, futureScrollRange, hideArrows, hideDayNames, theme and friends — typed and ignored, so copied JSX compiles while you delete them. No header band or day-names row is rendered; render your own above the calendar.

📖 Step-by-step diffs: docs/migration.md


Notes and limits

  • AgendaList heights are contracts. A row taller than its declared type clips.
  • TimelineList needs neighbouring dates present in events to swipe into.
  • Text ignores OS font scaling inside the kit (allowFontScaling={false}), because declared heights and scaled text cannot both be right. Scale your itemHeightByType yourself if you need to honour it.
  • Reduce Motion collapses every duration to 0; a running screen reader switches ExpandableCalendar to a static open grid.
  • Tested on iOS and Android. Web is not supported.

Something not behaving? docs/troubleshooting.md lists every error message the kit can emit and what causes it.


Documentation

| | | | --- | --- | | Installation | Peers, the Babel plugin, rebuilding | | Compatibility | Versions, architecture, platforms, a11y, time zones | | Usage | Quick starts and recipes | | Features | Every toggle, default and switch | | API | Every prop, ref, helper and type | | Theming | The eleven tokens | | Migration | From react-native-calendars | | Troubleshooting | Errors and failure modes |

Contributing

npm install
npm run typecheck
npm run build

Issues and pull requests: github.com/shivampandey-07/react-native-agenda-kit

License

MIT © Shivam Pandey