Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelog
  1. Home
  2. Blog
  3. Component guides
  4. React Cron Generator: Schedules Without Writing Cron
Auf Deutsch lesen
  1. Blog
  2. Component guides
  3. React Cron Generator: Schedules Without Writing Cron
Component guides
Component guides

React Cron Generator: Schedules Without Writing Cron

SR
Serban Rusu · Founder of Wingo UI
Oct 19, 2026 · 9 min read

On this page

0%
  1. What should a React cron generator do?
  2. Which React cron libraries are there?
  3. What props does the Cron Picker need?
  4. How do I show the next runs in the right time zone?
  5. How do I save the schedule from a form?
  6. How does the custom tab validate cron?
  7. Is the Cron Picker accessible and usable on a phone?
  8. What cron rules trip up a schedule builder?
  9. How do I add it to my project?
  10. FAQ
    1. What is a React cron generator?
    2. Should a cron builder show the next run times?
    3. What happens when both day of month and day of week are set in cron?
    4. Does the Wingo UI Cron Picker support Quartz cron expressions?
    5. Is the Wingo UI Cron Picker free?

TL;DR

A good React cron generator hides the syntax: frequency tabs, weekday and day chips and a time field write the expression, a sentence reads it back, and a list of the next runs proves it before anyone saves. The Wingo UI Cron Picker does this from one value string, shows the runs in a chosen time zone, and keeps a validated Custom tab for people who already know cron.

Published Oct 19, 2026

You are adding scheduled reports, backups or reminders to an app, and you need a React cron generator so your users can say when the job runs. The backend wants 0 9 * * 1-5. The person filling in the form wants "every weekday at 9 in the morning" and has never heard of crontab. A plain text input fails them, and a pile of dropdowns that mirror the five fields only moves the syntax into a different shape. This guide covers what a cron expression UI should do, the props that matter in the Wingo UI Cron Picker, how to save the value safely, and the cron rules that trip up most schedulers.

What should a React cron generator do?

Let people pick a schedule in their own words, then prove it back to them. A react cron editor, a react cron builder and a schedule picker React apps drop into a form are one control under three names, and the good ones share five traits:

  1. Frequency first. Tabs for every few minutes, hourly, daily, weekly and monthly cover most real jobs. Each tab shows only the fields it needs.
  2. Chips for days. Weekdays as seven chips and days of the month as a grid of 1 to 31, so nobody types 1,3,5.
  3. A sentence that reads it back. "Mondays, Wednesdays, and Fridays at 8:30 AM" is how a person checks their own choice.
  4. The next runs. Concrete dates catch what a sentence hides: a wrong time zone, a 30 February that never comes, a job that runs sixty times an hour.
  5. An escape hatch. A Custom tab for people who already know cron, validated as they type.

Which React cron libraries are there?

Two open source packages cover the same job: react-js-cron, built on Ant Design, and react-cron-generator, which defaults to Quartz syntax. Facts are from each project's GitHub README, as of October 2026.

OptionWhat it isCron formatRead back and previewUI dependency
react-js-cron (opens in a new tab)MIT package, controlled with value and setValueStandard cron, with shortcuts for @daily style macrosNo sentence in the README; humanizeValue writes SUN-SAT and JAN-DEC names into the value; next runs not mentionedantd 6 or later, React 18 or later
react-cron-generator (opens in a new tab)Package with onChange(value, text)Quartz (6 or 7 fields) by default, 5-field Unix with isUnixA description from cronstrue with showResultText; next runs not mentionedNo UI library listed; import its CSS file for the default styles
Wingo UI Cron PickerPro source component, installs with npx wingo-ui@latest add cron-picker5-field Unix cron, names, macrosA sentence, the expression with copy, and the next runsTailwind CSS v4, motion, lucide-react

If your app already uses Ant Design, react-js-cron fits right in. If your scheduler is Quartz or Spring, react-cron-generator speaks that format and the Cron Picker does not. The rest of this guide covers the Cron Picker.

What props does the Cron Picker need?

One string. value (or defaultValue, which is 0 9 * * 1-5 when you pass nothing) holds the expression, and onValueChange gets the new one after every tab, chip or field change.

bash
npx wingo-ui@latest add cron-picker
tsx
"use client";
import { useState } from "react";
import { CronPicker } from "@/components/ui/cron-picker";
export function BackupSchedule() {
const [cron, setCron] = useState("0 2 * * 0");
return (
<CronPicker
label="Run the backup"
value={cron}
onValueChange={setCron}
frequencies={["daily", "weekly", "monthly", "custom"]}
locale="en-US"
/>
);
}

The picker opens on the tab that fits the expression. 0 2 * * 0 opens Weekly with Sunday selected and reads "Sundays at 2:00 AM". Anything the tabs cannot express, such as */15 9-17 * * 1-5, opens Custom with the text as it is. The props you will change most often:

  • frequencies: the tabs, in order. The default is minutes, hourly, daily, weekly, monthly and custom; add "yearly" for dates like January 1.
  • minuteIntervals and hourIntervals: the choices for every N minutes ([5, 10, 15, 30]) and every N hours ([1, 2, 3, 4, 6, 12]).
  • showNextRuns: how many upcoming runs to list, 5 by default, 0 to hide them.
  • showSummary and showExpression: the sentence and the mono expression chip with its copy button.
  • allowCustom: the Custom tab, on by default.
  • weekStartsOn: the chip order. -1 follows the locale, so en-US starts on Sunday.

The demo starts on 0 9 * * 1-5, so it opens on Weekly with Monday to Friday picked. Tap a few days off and change the time. The sentence, the expression and the next runs follow on every change.

Tap Tue and Thu to leave Mon, Wed and Fri, then set the time: the sentence, the expression and the next five runs update at once. The demo counts from a fixed moment, Thursday, September 24, 2026 at 4:40 PM
$ npx wingo-ui@latest add cron-picker
ProCron Picker docs

A weekly or monthly tab with nothing picked sets the value to "" and says "Pick at least one day", so a form can block saving instead of storing an empty schedule.

How do I show the next runs in the right time zone?

Pass the zone your scheduler uses. A cron expression has no time zone of its own: the machine that runs it decides. Vercel's Cron Jobs docs (opens in a new tab) say the time zone is always UTC. GitHub Actions scheduled workflows (opens in a new tab) run in UTC by default and take an optional IANA timezone. Cronie reads a CRON_TZ variable per crontab, per the crontab man page (opens in a new tab).

Set timeZone to that zone and the picker computes and shows the next runs in it, with a badge that names it. Leave it empty and the runs use the device's zone, which is only correct when the server and the user share one.

tsx
<CronPicker
value={cron}
onValueChange={setCron}
timeZone="America/New_York"
description="New York time, whatever the server's time zone."
layout="split"
locale="en-US"
/>

layout="split" puts the fields on the left and the sentence and runs on the right once the picker is 672px wide. It is a container query, so the same picker stacks on a phone or in a narrow side panel. hourCycle switches between 12 and 24 hours (auto follows the locale), and now sets the moment the runs count from, which is how the demos stay stable.

The split layout with a New York time zone: change the time and watch the runs on the right, each with its date, time and how far away it is
$ npx wingo-ui@latest add cron-picker
ProCron Picker docs

Daylight saving time is the other half. The cronie man page says local times skipped by the change never match and times that occur twice run twice. Depending on the zone, a 2:30 AM job can be skipped on the spring change or run twice on the autumn one, so pick a time outside the switch hour for anything that must run exactly once.

How do I save the schedule from a form?

Give the picker a name. It renders a hidden input with the expression, or an empty value while the expression is invalid, so a plain form post or a Server Action gets a string to check again on the server.

The picker's helpers (isValidCron, describeCron, getNextRuns) live in a "use client" file, so a Server Action cannot call them. Use a server-side parser such as cron-parser (opens in a new tab), and keep the field count at five because cron-parser also accepts a seconds field. The picker accepts @daily style macros in Custom, and its README does not list @midnight or @annually, so the action below expands the picker's macros to five fields before it checks anything.

bash
npm install cron-parser
ts
// app/reports/actions.ts
"use server";
import { CronExpressionParser } from "cron-parser";
export type ScheduleState = { error: string; saved?: string };
// the macros the picker accepts, as five fields
const MACROS: Record<string, string> = {
"@yearly": "0 0 1 1 *",
"@annually": "0 0 1 1 *",
"@monthly": "0 0 1 * *",
"@weekly": "0 0 * * 0",
"@daily": "0 0 * * *",
"@midnight": "0 0 * * *",
"@hourly": "0 * * * *",
};
export async function saveSchedule(_prev: ScheduleState, formData: FormData): Promise<ScheduleState> {
const raw = String(formData.get("schedule") ?? "").trim();
const schedule = MACROS[raw.toLowerCase()] ?? raw;
// the hidden input is empty while the picker shows an error
if (schedule.split(/\s+/).length !== 5) return { error: "Pick a schedule first" };
try {
CronExpressionParser.parse(schedule, { tz: "America/New_York" }).next();
} catch {
return { error: "That schedule is not a valid cron expression" };
}
// save it with your own data layer here
return { error: "", saved: schedule };
}
tsx
// app/reports/schedule-form.tsx
"use client";
import { useActionState, useState } from "react";
import { Button } from "@/components/ui/button";
import { CronPicker } from "@/components/ui/cron-picker";
import { saveSchedule } from "./actions";
export function ReportScheduleForm() {
const [cron, setCron] = useState("0 9 * * 1-5");
const [state, formAction, pending] = useActionState(saveSchedule, { error: "" });
return (
<form action={formAction} className="flex flex-col gap-4">
<CronPicker
name="schedule"
label="Send the report"
value={cron}
onValueChange={setCron}
timeZone="America/New_York"
locale="en-US"
error={state.error || undefined}
/>
<Button type="submit" loading={pending}>
Save schedule
</Button>
</form>
);
}

Enter inside the picker's minute field and the Custom inputs never submits the form around it, so a user editing the minute or typing an expression does not save half a schedule. The time field is a plain Time Picker: on a desktop, Enter there commits the time and then submits like any text input. For a list of saved jobs, readOnly drops the fields and keeps only the sentence and the expression with copy: <CronPicker value={job.cron} readOnly size="sm" /> fits in a table row.

How does the custom tab validate cron?

As you type, with one message per problem. The Custom tab takes a raw expression or, under By field, five small labeled boxes. An hour of 25 says "Hour must be between 0 and 23". Four fields say "Use 5 fields: minute, hour, day, month, weekday". An L in the day field says which forms the field accepts. @daily, @weekly and the other time-based macros are accepted, while @reboot is rejected.

The expression has an hour of 25: read the error under the input, then switch to By field and fix the hour box
$ npx wingo-ui@latest add cron-picker
ProCron Picker docs

The same rules ship as functions for your own code: validateCron returns the field and bounds of an error, cronErrorMessage turns it into a sentence, and detectFrequency tells you which tab an expression would open on.

Is the Cron Picker accessible and usable on a phone?

Yes. The frequency tabs are a Segmented Control: arrow keys move between them, and when the full names do not fit, as on a phone, they show short ones (Min, Hour, Day). The weekday and day chips are a Toggle Group: Space toggles a chip and arrows move between chips. Chips are 32, 36 or 40px for sm, md and lg, and 44px on touch screens.

The every N minutes choice is a Select that opens as a bottom sheet on phones. The time opens columns on a desktop and a wheel sheet on a phone, from the Time Picker, which the React time picker guide covers in depth. Screen readers hear the new sentence half a second after the last change, through a polite live region, so a run of chip taps is announced once. Clicking the label focuses the tabs, and reduced motion turns the slides into fades.

What cron rules trip up a schedule builder?

Four come up again and again, and the picker answers each one in a specific way.

  • Day of month OR day of week. When both day fields are restricted, cron runs on either one. 0 9 13 * 5 reads "At 9:00 AM on the 13th of the month or on Fridays", which is rarely what someone meant. The tabs never write both fields, which also keeps the output valid on Vercel, where an expression with both is refused.
  • Names on platforms that reject them. Vercel does not accept MON or JAN. The tabs always write numbers (1-5). An expression typed into Custom is kept as typed, so names there pass through.
  • Dates that never come. 0 0 30 2 * is valid syntax, and the run list says "No runs in the next 4 years" instead of staying silent.
  • A busy hour field. * 2 * * * is every minute from 2:00 to 2:59, read back as "Every minute during hour 2". GitHub Actions also caps schedules at once every 5 minutes, and the default minuteIntervals start at 5.

How do I add it to my project?

Run npx wingo-ui@latest add cron-picker. The CLI copies the source into components/ui, adds the Field, Select, Segmented Control, Toggle Group, Time Picker and other parts it imports, and installs motion and lucide-react. The Cron Picker is a Pro component (pricing). If you are building a whole automation screen, the Automation Builder Page already uses it for scheduled triggers, as shown in the React workflow builder guide. More single-component deep dives live in the component guides category, and the React form components guide covers the labels, errors and mobile rules every field in that form shares.

Components in this post

  • Cron Picker

    A React cron expression builder: frequency tabs, weekday and day chips, a time, a sentence that reads the schedule back and the next runs.

    Pro

FAQ

What is a React cron generator?

It is a form control that builds a cron expression from friendly inputs such as a frequency, a list of weekdays and a time, so people can schedule a job without learning the five fields. Most also show the expression and a plain-English description of it.

Should a cron builder show the next run times?

Yes. A sentence like Every weekday at 9:00 AM can still hide a wrong time zone or a date that never comes, while five concrete dates make the mistake obvious. The Wingo UI Cron Picker lists five by default through showNextRuns.

What happens when both day of month and day of week are set in cron?

Standard cron runs the job when either field matches, so 0 9 13 * 5 runs on the 13th and on every Friday. The cronie crontab man page documents this rule, and Vercel Cron Jobs refuse expressions that set both.

Does the Wingo UI Cron Picker support Quartz cron expressions?

No. It reads and writes 5-field Unix cron with lists, ranges, steps, MON and JAN names and macros such as @daily. Six or seven field Quartz expressions show the error Use 5 fields in the Custom tab.

Is the Wingo UI Cron Picker free?

No, it is a Pro component. The Select, Segmented Control, Field and Copy Button it builds on are free.

  • Cron
  • Scheduling
  • Forms
  • Accessibility
  • React

Share

SR

About the author

Serban Rusu

Founder of Wingo UI

Serban Rusu founded Wingo UI. He builds its component library, CLI and MCP server, and writes about React interfaces for phones and AI coding agents.

More from Serban
Keep reading

Related posts

All posts
Component guides

Embla Carousel React: Accessible Slides, No Layout Shift

Build an embla carousel react component for photos, listings, testimonials and onboarding, with keyboard controls, pausable autoplay and no layout shift.

SRSerban Rusu·Oct 19, 2026·9 min read
Component guides

React Animated Number Counter: Rolling Odometer Digits

Build a React animated number counter that rolls each digit like an odometer, takes its separators from Intl, holds its width and respects reduced motion.

SRSerban Rusu·Oct 19, 2026·9 min read
Component guides

React Audio Player with Waveform: Scrub, Seek, Peaks

Build a React audio player with waveform scrubbing for podcasts, chat voice notes and call logs, with keyboard seek, precomputed peaks and no layout shift.

SRSerban Rusu·Oct 19, 2026·9 min read
Newsletter

Get new posts by email

New guides, tutorials and comparisons from the Wingo UI blog, sent when they are published.

No spam. Unsubscribe at any time.

WingoUI

Animated, configurable, mobile-first React components and blocks. Copy the source, make it yours, and let your coding agent build with it.

Product

  • Components
  • Templates
  • Pricing
  • AI agents
  • Theme generator

Resources

  • Docs
  • Blog
  • Changelog
  • Changelog RSS
  • llms.txt

Legal

  • Terms
  • Privacy
  • Refunds
  • License
  • Imprint

Component categories

  • Buttons & Actions
  • Inputs
  • Forms
  • Navigation
  • Overlays
  • Feedback
  • Data Display
  • Tables & Lists
  • Charts & Stats
  • Layout
  • Media
  • AI Kit
  • Text & Effects
  • Mobile
  • Commerce
  • Marketing Sections
  • Blocks
  • Hooks & Utilities

© 2026 Wingo UI

Wingo UI