shadcn.io is not affiliated with official shadcn/ui
Recurring Events Calendar Block for Solid
A custom repeat dialog over an event editor, shown open: pick day, week, month or year, how many apart, the weekdays, an end and dates to skip, and read the next five dates worked out from the rule.
Installation
Overview
Let people say exactly how an event repeats, with this custom repeat dialog for Solid with Ark UI. It belongs to the event editor of Cadencewick, a scheduling tool: the page is drawn under a soft scrim inside a hairline frame with a plus mark on each corner, and its Repeats line is the one real piece of text on it. The dialog holds a frequency Native Select, a Number Input for how many apart, a weekday Toggle Group for a weekly rule (the monthly rule swaps it for the 20th or the third Tuesday), an end of never, after a number of times or on a date, and a skip-a-date field whose exceptions become chips you can remove. Beside the fields the next five dates are worked out from the rule as you change it, with the start time, and a note says when the rule ends before five. Done, the only Shine Button, writes the rule back as a sentence such as "Every 2 weeks on Tue, until 20 Dec"; Cancel keeps the old one. For preview the dialog is drawn open in place when the page loads; the Custom repeat button opens the real Dialog, with the same fields and the same state. For calendars, schedulers, reminders and any app with repeating events.
FAQ
Why is the dialog already open, and what do I keep?
So you can see it without a click. On load the dialog is a copy drawn in place over the editor: a native dialog element that isn't modal, so nothing is trapped, hidden or focused, and the page around the preview keeps working. The Custom repeat button opens the real Dialog, modal, with its focus trap, Escape and focus return, and the copy stays closed until a reload. Both show the same RepeatFields component and share the block's state, so a rule set in the copy is there in the real one. In your app keep the Dialog and drop the copy: the "in-place" state, the scrim and the dialog element inside the frame.
How are the next five dates worked out?
By testing each day from the event's start, Tuesday 20 October 2026, against the rule: a daily rule every N days, a weekly one the ticked weekdays in every Nth week (weeks start on Monday), a monthly one the 20th or the third Tuesday of every Nth month, and a yearly one the same date every Nth year. The end is checked as it goes. "After N times" counts every date the rule produces, skipped ones included, as a calendar file does; "On a date" stops after that day. The search stops after about six and a half years, so a rule that never matches shows "No dates remain under this rule".
How do I skip a date?
Pick a date in the skip field and press Add. It has to be one of the dates the rule produces, so a Wednesday on a Tuesday rule is refused with a line that says so, and a date already skipped, or a fourth one, is refused too (this demo allows three). The chip for each skipped date has a remove button, and a polite status message says what changed. Skipped dates drop out of the next five and the list fills in from the ones after them.
What is checked before Done?
The interval must be a whole number from 1 to 99, a count must be 1 to 99, and an end date must be on or after the day the event starts. The message shows under the fields in a slot that is always there, so nothing moves, and Done with a problem keeps the dialog open and puts focus back on the frequency field. Weekly rules always keep at least one weekday ticked.
What stays local, and how do I wire it up?
Everything. Done only rewrites the line on the drawn editor in this tab, and nothing is saved or sent. In your app turn the rule into an RRULE string (frequency, interval, BYDAY, COUNT or UNTIL, EXDATE for the skipped dates) in done, send it to your server, and let the server expand the dates; keep the on-screen list for a preview only.
Is it accessible from the keyboard?
Yes. The real dialog is named by its title and described by its sentence, focus starts in the frequency field and stays inside until it closes, and Escape, Cancel and the close button return focus to Custom repeat. The weekday buttons are a toggle group, the monthly and end choices are native radios that take the arrow keys, and the next five are an ordered list named "Next five". The trigger is the button wrapped in DialogTrigger with asChild, and the dialog is controlled with open and onOpenChange. The copy shown on load moves no focus.