Croner: Cron Scheduling for JavaScript and TypeScript Without Dependencies
Trigger functions or evaluate cron expressions in JavaScript or TypeScript. No dependencies. Most features. Node. Deno. Bun. Browser.
At a glance
- What is it?
- Croner evaluates cron expressions and triggers functions in Node, Deno, Bun and the browser, with seconds and year fields, time zones, overrun protection and TypeScript typings. It runs in memory, so anything that must survive a restart needs another layer.
- Who is it for?
- Croner fits Node, Deno, Bun or browser code that needs cron expressions evaluated in process: a single script, a small service, a build step, a page that shows upcoming run times. It does not fit anything that must survive a process restart, run on several machines without duplicate triggers, or persist job state, because the README states it operates in-memory with no database or configuration files.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 8, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Croner Actually Solves
A cron expression is a compact way to say when something should happen, and JavaScript has no built-in way to read one. Croner fills that gap: it parses cron syntax, computes the next matching instants, and optionally calls a function at each of them. The README lists the intended audience by runtime rather than by role: Node.js >=18.0 (both require and import), Deno >=2.0, Bun >=1.0.0, and browsers as standalone, UMD or ES-module. That is a wide net, and it is the point. The same object works in a server process, an edge runtime and a script tag.
The second use case is easy to overlook because it has nothing to do with timers. You can construct a Cron instance and ask it questions without ever scheduling anything: nextRuns(100) returns the next hundred matching dates, nextRun() returns one, and match(date) answers whether a given date fits the pattern. The README's own example uses this to count the days until Christmas Eve and to check whether a date falls on a Monday. That makes Croner usable as a pure expression evaluator in code that never runs a background loop, which is a different product from a job runner and worth judging separately.
Pattern Syntax and How Scheduling Works
Croner accepts a cron pattern, an optional options object, and an optional function, in that order. If the function is omitted, the job can be scheduled later with job.schedule(). The constructor schedules the job for the next matching time unless the options include { paused: true }, and it returns a Cron instance with methods for inspection and control.
The pattern language goes beyond the five-field classic. The README states support for seconds and year fields, L (last), W (weekday), # (nth occurrence), and + (AND logic). A pattern can also be a JavaScript Date object or an ISO 8601 time string, in which case the job fires once. The first README example uses six fields, */5 * * * * *, which means every fifth second because the seconds field is present.
Scheduling is in-process. There is no daemon, no database and no configuration file; the README states the library operates in-memory. Each job tracks its own state, and the status methods reflect that: isRunning() means scheduled and not paused or killed, isStopped() means permanently stopped with stop(), and isBusy() means the job is currently doing work. The last one matters because Croner has built-in overrun protection, so an execution that runs long does not silently stack up behind the next tick. The README links to a dedicated page for that behaviour rather than spelling out the policy, so read it before relying on a specific overlap outcome.
Control is explicit and small. trigger() forces an immediate run, pause() and resume() suspend and restart the schedule, and stop() ends the job for good, with the README noting that stop() is irreversible and also removes named jobs from the exported scheduledJobs array. Named jobs are the only global registry: passing a name in options makes Croner hold a reference in that array, which is convenient for inspection and a leak risk if you never call stop().
Installing Croner and Evaluating Your First Expression
Installation is a normal package-manager step, and the README points to croner.56k.guru for the full instructions. The import form depends on the runtime. In Node or Bun, the README shows an ESM import and a CommonJS require of the same package name:
import { Cron } from "croner";
const { Cron } = require("croner");In Deno, the README gives two sources, a deno.land/x URL and a jsr.io specifier, both pinned to a version:
import { Cron } from "https://deno.land/x/[email protected]/dist/croner.js";
import { Cron } from "jsr:@hexagon/[email protected]";In a browser, the README shows the UMD build loaded from a CDN, which exposes Croner globally:
<script src="https://cdn.jsdelivr.net/npm/croner@10/dist/croner.umd.min.js"></script>A first real use does not have to schedule anything. Construct a Cron from a pattern and ask for upcoming runs, which prints an array of Date objects:
const nextSundays = new Cron('0 0 0 * * 7').nextRuns(100);
console.log(nextSundays);If you do want a function to run, pass it as the third argument. This schedules the callback for every fifth second and returns a job you can pause, resume or stop:
const job = new Cron('*/5 * * * * *', () => {
console.log('This will run every fifth second');
});Two things to expect on the first run. The constructor schedules immediately unless you pass { paused: true }, so a job created at module load will start ticking before any explicit start call. And errors thrown inside the callback are not swallowed by default; the catch option controls that, with true ignoring errors silently and a function receiving them instead.
Where Croner Stops Being the Right Tool
The in-memory design is the central limitation, and it is a deliberate one. Nothing about a job is written to disk. If the process restarts, the schedule is gone unless your own code recreates it, and any run that was due during the downtime does not happen. The README does not document rollback, missed-run catch-up, or persistence of any kind, so treating Croner as a durable scheduler means building that layer yourself.
The same design makes horizontal scaling awkward. Two instances of the same service, each with the same Croner job, will both fire. Croner has no concept of a distributed lock, a leader election or a shared registry; scheduledJobs is an in-process array. If you run more than one replica, you need an external mechanism to decide which one executes, and Croner will not help you choose.
Long-running work is a second boundary. Overrun protection is present, but the README links to a documentation page for the details rather than stating a policy inline, so the exact behaviour when a callback exceeds its interval should be checked against that page and against your own tolerance for skipped or delayed runs. A job that must run to completion regardless of the next tick is better served by a queue with retries and visibility timeouts.
Finally, browser use is real but narrow. A tab that is closed stops executing, and background throttling applies to timers. Croner's browser support is useful for computing and displaying run times, less so for dependable background work.
Croner Versus node-cron and cron
The closest alternatives in the JavaScript ecosystem are node-cron and cron, and the README anticipates the comparison directly: it links to a migration section of the manual for readers coming from cron or node-cron, or upgrading from an older Croner version. That page, not this article, is the place to check API differences.
The design difference that matters most is the dependency count. Croner states zero dependencies, which keeps the installed tree small and removes transitive upgrade risk. The second difference is the pattern language: seconds and year fields, L, W, # and + are listed as supported, which is a wider grammar than the five-field expressions many schedulers accept. The third is runtime coverage. Croner targets Node, Deno, Bun and the browser from one package, while the older libraries are generally Node-centric.
What Croner does not add is durability. If your current library already persists job state or coordinates across processes, swapping it for Croner would remove that capability rather than add one. The migration guide is the right place to check whether a specific API you depend on exists here, and the README's own wording is that Croner has "most features," not all of them.
Maintenance, Versioning and Licence
The repository is not archived, and the last push was on 2026-08-31, which is recent enough that the project is clearly being worked on. The recent releases tell a more careful story than the push date alone. The newest is 11.0.0-dev.0, published on the same day as the last push, and the two before it are 10.0.2-dev.3 and 10.0.2-dev.2. All three visible releases carry a dev or prerelease marker, so the most recent published artifacts are not stable releases. Anyone installing from npm or JSR should check which dist-tag they are pulling and pin deliberately rather than taking whatever the latest tag resolves to.
Upgrade cost depends on how much of the API you use. The README keeps the surface small: a constructor, a handful of status and control methods, and an options table with keys such as name, maxRuns, catch and paused. Code that uses only the constructor and nextRun() has little to break. Code that depends on the exact semantics of overrun protection, or on the scheduledJobs registry, has more to re-verify on a major bump, which is why the migration section exists.
The licence is MIT, stated in the README and in the LICENSE file at the repository root. That permits commercial use and modification with the usual attribution and warranty terms. This is a description of the licence text, not legal advice; if your organisation has specific obligations around attribution or redistribution, have someone review the LICENSE file itself.
Editorial conclusion
Croner fits Node, Deno, Bun or browser code that needs cron expressions evaluated in process: a single script, a small service, a build step, a page that shows upcoming run times. It does not fit anything that must survive a process restart, run on several machines without duplicate triggers, or persist job state, because the README states it operates in-memory with no database or configuration files. Before adopting it, check that your runtime meets the stated floors (Node >=18.0, Deno >=2.0, Bun >=1.0.0), that you can accept the MIT licence, and that the version you install is a stable release rather than the 11.0.0-dev.0 line. If you need durable scheduling, the choice is a queue or a database-backed scheduler, and Croner becomes the expression evaluator inside it rather than the scheduler itself.
Frequently asked questions
Is Croner free to use?
Yes. Croner is published under the MIT licence, and the README states it has no dependencies, so there is no paid tier or licence key involved.
What runtimes does Croner support?
The README lists Node.js >=18.0 (both require and import), Deno >=2.0 and Bun >=1.0.0, plus browsers as standalone, UMD or ES-module builds.
How do I install Croner with npm?
Install the croner package with your package manager, then import the Cron class from "croner" or require it and destructure Cron. The README points to croner.56k.guru for full installation instructions.
Does Croner support seconds in cron expressions?
Yes. The README states that Croner supports seconds and year fields in addition to the standard fields, and its first example uses a six-field pattern, */5 * * * * *, to run every fifth second.
What happens to scheduled jobs when the process restarts?
They are lost. The README states that Croner operates in-memory with no need for a database or configuration files, so jobs must be recreated by your own code after a restart and missed runs are not documented as being caught up.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/hexagon-croner)