react-rrule-builder-ts
v0.1.3
Published
rrule component for react with mui
Maintainers
Readme
react-rrule-builder-ts
Overview
react-rrule-builder-ts is a React component that generates RRULE strings for use in iCalendar events, built with TypeScript. This package is inspired by the original react-rrule-generator, which is now deprecated.
You can view the demo and explore component functionality in the storybook.
Status
Beta: This package is functional and actively used, but has not yet reached a 1.0 release. The API may change between minor (0.X.X) versions and will be noted in changelog. Bug reports and feedback are welcome.
Features
- Generates RRULE strings compliant with iCalendar (RFC 5545) standards.
- Supports hourly, daily, weekly, monthly, and yearly frequencies with full repeat options.
- Rehydrate component state from an existing RRULE string.
- Per-instance Zustand store via React Context — multiple
<RRuleBuilder>instances work independently on the same page. - Includes form validation using Yup with range-checked schemas per frequency.
- Responsive layout — weekly, monthly, and yearly controls automatically adapt when space is tight. Try resizing the SmallEmbedded story to see it in action.
- Integrates with MUI (Material-UI) components with customizable size and variant.
- Exports all public types, enums, and the
BuilderStoreProviderfor external store access. - Accessible: ARIA labels on interactive elements, unique IDs via
useId().
Installation
yarn add react-rrule-builder-tsor
npm install react-rrule-builder-tsUsage
import { RRuleBuilder } from 'react-rrule-builder-ts';
import { AdapterLuxon } from "@mui/x-date-pickers/AdapterLuxon";
import { DateTime } from "luxon";
const MyComponent = () => {
const handleRRuleChange = (rruleString: string) => {
console.log(rruleString);
};
return (
<RRuleBuilder
dateAdapter={AdapterLuxon}
datePickerInitialDate={DateTime.now()}
onChange={handleRRuleChange}
enableYearlyInterval={true}
enableResponsiveLayout={true}
/>
);
};Date Format
Both date pickers default to the adapter's localized format. To follow an app-level setting (for example an org-wide DD/MM/YYYY), pass dateFormat:
import { RRuleBuilder, DateFormat } from 'react-rrule-builder-ts';
import { AdapterLuxon } from "@mui/x-date-pickers/AdapterLuxon";
<RRuleBuilder dateAdapter={AdapterLuxon} dateFormat={DateFormat.DD_MM_YYYY} />DateFormat is a plain as const object, not a TypeScript enum, so its members are ordinary string literals ("MM/dd/yyyy", "dd/MM/yyyy", "yyyy/MM/dd"). Values from your own string enum or const object that hold the same strings are accepted anywhere dateFormat or the DateFormat type is expected, and dateFormat itself is typed as string, so any adapter-compatible format works. (As with any TypeScript enum, the reverse does not hold: DateFormat.DD_MM_YYYY is not assignable to a variable typed as your enum, because enums are nominal.) If the name collides with one of yours, alias it on import: import { DateFormat as RRuleDateFormat } from 'react-rrule-builder-ts'.
The tokens are case-sensitive Luxon/date-fns syntax (dd, MM, yyyy). A setting-style string such as "DD/MM/YYYY" is not a valid format and renders garbled text, so map settings to the constants instead. If your setting names match the constant keys, indexing works directly: dateFormat={DateFormat[orgDateFormat]} for an orgDateFormat of "DD_MM_YYYY". An empty string is treated as unset.
External Store Access
If you need to access the builder's state from sibling components (e.g. to read validationErrors or call validateForm), wrap both components in a BuilderStoreProvider:
import { RRuleBuilder, BuilderStoreProvider, useBuilderStore } from 'react-rrule-builder-ts';
import { AdapterLuxon } from "@mui/x-date-pickers/AdapterLuxon";
const ValidationStatus = () => {
const { validationErrors, validateForm } = useBuilderStore();
return (
<div>
<button onClick={() => validateForm()}>Validate</button>
{Object.entries(validationErrors).map(([field, msg]) => (
<p key={field} style={{ color: 'red' }}>{msg}</p>
))}
</div>
);
};
const MyPage = () => (
<BuilderStoreProvider>
<RRuleBuilder dateAdapter={AdapterLuxon} />
<ValidationStatus />
</BuilderStoreProvider>
);API
RRuleBuilder Props
dateAdapter(new (...args: any[]) => MuiPickersAdapter<TDate>) Date adapter instance used by MUI date pickers (e.g.AdapterLuxon,AdapterDateFns,AdapterDayjs).datePickerInitialDate(TDate) Initial date for the start date picker. Type must match the date adapter.onChange((rruleString: string) => void) Callback fired when the RRULE string changes.rruleString(string) Existing RRULE string to rehydrate component state from.enableOpenOnClickDatePicker(boolean, default:true) When enabled, date pickers open when the input field is clicked.enableYearlyInterval(boolean, default:false) Shows the interval input for yearly frequency.enableResponsiveLayout(boolean, default:true) Enables automatic responsive layout for weekly, monthly, and yearly controls based on container width.showStartDate(boolean, default:true) Shows or hides the start date picker.defaultFrequency(Frequency, default:Frequency.WEEKLY) Sets the default frequency of the recurrence rule.inputSize(TextFieldProps["size"], default:"small") Size of all input fields. Respects MUI theme defaults.inputVariant(TextFieldProps["variant"], default:"outlined") Variant of all input fields. Respects MUI theme defaults.lang({ startDatePickerLabel: string; endDatePickerLabel: string }) Localization labels for the date pickers.timeZone(PickersTimezone, default:"UTC") Timezone for date pickers and RRULE generation. See MUI's date picker timezone documentation for details.dateFormat(string, optional) Display format for the start and end date pickers, forwarded as theformatprop of both MUIDatePickers (e.g."dd/MM/yyyy"). Use the exportedDateFormatconstants or any format string your date adapter understands. Tokens are case-sensitive Luxon/date-fns syntax (dd,MM,yyyy); a setting-style string such as"DD/MM/YYYY"is not a valid format and renders garbled text. When omitted or empty, the pickers use the adapter's default localized format.
Store and Actions
The component uses a per-instance Zustand store via React Context. Access the store with useBuilderStore() inside a BuilderStoreProvider or RRuleBuilder.
BuilderState
repeatDetails(AllRepeatDetails) — Current repeat rule details (interval, byDay, byMonth, byMonthDay, bySetPos).frequency(Frequency) — Current RRULE frequency.startDate(TDate | null) — Selected start date.validationErrors(Record<string, string>) — Validation errors fromvalidateForm().endDetails(EndDetails<TDate>) — End condition (never, after N occurrences, or on a specific date).RRuleString(string | undefined) — The generated RRULE string.radioValue(MonthBy | YearlyBy | null) — Selected radio option for monthly/yearly views.minEndDate(TDate | undefined) — Minimum end date (start date + 1 day).
BuilderActions
setFrequency(frequency: Frequency)— Sets frequency and resets repeat details.setRepeatDetails(details: AllRepeatDetails)— Updates repeat details and rebuilds the RRULE string.validateForm(): Promise<boolean>— Validates the form using Yup schemas. Returnstrueif valid, populatesvalidationErrorsif not.setEndDetails(details: EndDetails<TDate>)— Updates end condition. Uses a discriminated union:{ endingType: EndType.NEVER }{ endingType: EndType.AFTER, occurrences: number | null }{ endingType: EndType.ON, endDate: TDate | null }
setStartDate(startDate: TDate | null)— Sets start date and auto-adjusts end date if needed.buildRRuleString()— Rebuilds the RRULE string from current state.setOnChange(onChange: (rruleString: string) => void)— Sets the onChange callback.setStoreFromRRuleString(rruleString: string)— Rehydrates store state from an RRULE string.setRadioValue(radioValue: MonthBy | YearlyBy | null)— Sets the radio option for monthly/yearly.
Exported Types
import {
RRuleBuilder,
useBuilderStore,
BuilderStoreProvider,
// Enums
Weekday, // MO, TU, WE, TH, FR, SA, SU
Months, // JAN=1 through DEC=12
MonthBy, // BYMONTHDAY, BYSETPOS
YearlyBy, // BYMONTH, BYSETPOS
WeekdayExtras, // DAY, WEEKDAY, WEEKEND
OnThe, // 1, 2, 3, 4, -1
AllWeekDayOptions,
EndType, // NEVER, AFTER, ON
DateFormat, // MM_DD_YYYY, DD_MM_YYYY, YYYY_MM_DD (const object, also usable as a type)
// Types
type RRuleBuilderProps, // generic over the Luxon DateTime type, defaults to DateTime
type AllRepeatDetails,
type EndDetails,
} from 'react-rrule-builder-ts';Peer Dependencies
This library requires the following peer dependencies:
react>= 18react-dom>= 18@mui/material@mui/x-date-pickers@emotion/react@emotion/styledrrulezustandyupluxonlodash
Other Notes
- MUI is a peer dependency and is used for all UI components.
- The package uses MUI's DatePicker — you can use any MUI-compatible date adapter.
- Zustand manages state per-instance via React Context, making it observable and enabling external validation.

