Schedule Readback

Reads a cron expression back in plain English with the next runs in your zone and the server's, catching classic cron traps.

StopsCron job fires at the wrong time

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/schedule-readback.json

The files land in your project. They're yours to edit.

Usage

schedule-readback-usage.tsx
import { ScheduleReadback } from "@/components/ui/schedule-readback"

<ScheduleReadback label="Report schedule (server runs in UTC)" timeZone="UTC" value={cron} onValueChange={setCron} />

What it catches

  • A * in the minute field that runs every minute
  • Day-of-month and day-of-week both set, which runs on either (OR)
  • Day 31 (or 29–30) skipping shorter months, and impossible dates like 30 Feb
  • Scheduler zone differing from yours, with runs shown in both
  • Invalid expressions, with the reason

Props

ScheduleReadback props
PropTypeDefault
label

Visible label.

string"Schedule"
value

Controlled cron expression.

string—
defaultValue

Initial expression when uncontrolled.

string""
onValueChange

Called with the expression on every change, including one-tap fixes.

(value: string) => void—
timeZone

Zone the scheduler runs in (servers and CI are often "UTC"). Defaults to the viewer's zone.

string—
displayTimeZone

Zone the run times are shown in. Defaults to the viewer's browser zone.

string—
count

How many upcoming runs to list.

number5
from

Compute runs after this moment instead of now (useful for tests and docs).

Date | number—
runsLabel

Heading of the run list; `{count}` and `{zone}` (a short name like "GMT+5:30") are filled in.

string"Next {count} runs · your time ({zone})"
noRunsText

Shown when the schedule has no upcoming runs.

string"No upcoming runs."
locale

Locale for run times and "in 25 min".

string"en-GB"
describe

Replaces the plain-English readback, e.g. with `cronstrue.toString(expression, { locale })`.

(schedule: CronSchedule, expression: string) => string—
formatWarning

Rewrites a warning's text; `warning.id` says which check fired.

(warning: CronWarning) => string—
formatError

Rewrites a parse error; `field` is the 0-based field index when one is to blame.

(error: string, field?: number) => string—

* required. Also accepts the native props of <input> (className, aria-*, data-*…), passed through to it.

Accessibility

  • Visible <label>; aria-invalid with read-back via aria-describedby
  • Parse errors use role=alert; read-back and warnings render in polite live regions
  • Each fix button has an aria-label with the corrected expression; focus returns to the input after a fix

Only what the source does. Test with your own screen reader and content before shipping.

Limitations

  • 5-field Vixie/POSIX cron only; Quartz seconds/years, Jenkins H and cloud-specific syntaxes are not supported
  • Next runs are computed in the browser; DST handling depends on the zones you pass

Rules

Rules last verified:

Confirmed: when both day-of-month and day-of-week are restricted, the job runs when either matches. Other schedulers (systemd, Quartz, cloud cron) differ. Copied components don't update themselves, so re-check after rule changes (see updating a component).

What it stops

Cron job fires at the wrong time

Also known as

cron · crontab · cron expression · scheduler · job schedule · github actions schedule · vercel cron · utc

Field type

Dependencies

Pairs well with