> For AI agents: the complete documentation index is available at https://open-spaced-repetition.github.io/ts-fsrs/llms.txt, the full documentation bundle is available at https://open-spaced-repetition.github.io/ts-fsrs/llms-full.txt.

# Scheduler overview

A scheduler is the object your application calls to create cards and schedule reviews. You rarely build one from scratch: `defineScheduler({ model, chrono })` creates a reusable definition from the two parts, and `DefaultScheduler` is a ready-made preset built from the same machinery. See [Architecture](https://open-spaced-repetition.github.io/ts-fsrs/guide/architecture.md) for how model, chrono, and middleware fit together, or the [Model](https://open-spaced-repetition.github.io/ts-fsrs/guide/model.md), [Chrono](https://open-spaced-repetition.github.io/ts-fsrs/guide/chrono.md), and [Middleware](https://open-spaced-repetition.github.io/ts-fsrs/guide/middleware.md) guides for each part.


For your Agent

Implement or explain a scheduler

Copy Prompt

Use the current ts-fsrs Scheduler contract for this task: [describe the task].

1. Read https://open-spaced-repetition.github.io/ts-fsrs/guide/scheduler/index.md and https://open-spaced-repetition.github.io/ts-fsrs/guide/scheduler/default-scheduler.md for the definition-and-instance model and the ready-made preset.
2. Create a custom scheduler with defineScheduler({ model, chrono }); add middleware with .use(...) and create instances with .create({ config }).
3. Keep the model limited to memory-state updates and interval math, the chrono to time values and day arithmetic, and middleware to policy around the core.
4. Keep the reusable definition at the program level and validate a user's configuration with .create({ config }).
5. Prefer DefaultScheduler when the default FSRS model, dateChrono, and standard policies fit.
6. Use the current public API and add focused tests for the scheduling behavior you implement.

## Definition and instance

A scheduler exists in two stages:

| Stage      | What it is                                                                                                         | How you get it                                       |
| ---------- | ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------- |
| Definition | A schema-only description: a model, a chrono, and optional middleware. It holds no configuration and no card data. | `defineScheduler({ model, chrono })` and `.use(...)` |
| Instance   | A working scheduler bound to one validated configuration.                                                          | `definition.create({ config })`                      |

`defineScheduler` keeps the concrete model and chrono types, so schemas and handlers stay type-checked through `.use(...)`. `.use(middlewareA, middlewareB)` returns a new definition with the middleware added in order; the original definition is unchanged and stays reusable. Calling `.create({ config })` validates that configuration against the scheduler schemas and returns an independent scheduler whose state is its own.

:::note Keep the definition, create the instances
Store the definition, including its registered middleware, at the program level. Call `.create({ config })` when a user or collection configuration becomes available. Each call validates that configuration and returns a separate instance, so two `.create()` calls never share scheduling state.
:::

## Reuse one definition for many schedulers

Here one definition builds two schedulers whose only difference is the target retention. Because `schedulerDesiredRetentionMiddleware` contributes `desiredRetention` to the config schema, `.create()` accepts and validates it per instance:

```ts twoslash file="../../../snippets/run-code/scheduler.ts" fold height=520
import { dateChrono, defineScheduler, Rating } from 'ts-fsrs'
import { schedulerDesiredRetentionMiddleware } from 'ts-fsrs/middlewares'
import { FSRS6_DEFAULT_WEIGHTS, FSRS6Model } from 'ts-fsrs/models/fsrs-6'

// Create a reusable definition once, at the program level.
const schedulerDefinition = defineScheduler({
  model: FSRS6Model,
  chrono: dateChrono,
}).use(schedulerDesiredRetentionMiddleware)

const now = new Date('2026-01-01T00:00:00.000Z')

// Each .create() validates its own config and returns an independent scheduler.
const library = schedulerDefinition.create({
  config: {
    weights: FSRS6_DEFAULT_WEIGHTS,
    enableShortTerm: true,
    numRelearningSteps: 1,
    desiredRetention: 0.9,
  },
})
const exam = schedulerDefinition.create({
  config: {
    weights: FSRS6_DEFAULT_WEIGHTS,
    enableShortTerm: true,
    numRelearningSteps: 1,
    desiredRetention: 0.8,
  },
})

function nextGoodDue(scheduler: typeof library) {
  const card = scheduler.newCard({ now })
  return scheduler
    .review({ card, grade: Rating.Good, now })
    .card.dueAt.toISOString()
}

console.log(
  JSON.stringify(
    {
      libraryRetention: 0.9,
      libraryDue: nextGoodDue(library),
      examRetention: 0.8,
      examDue: nextGoodDue(exam),
    },
    null,
    2
  )
)

```


▶ Run code

The lower retention target lets the interval grow, so the exam scheduler's first Good review is due on January 9 while the library scheduler's is due on January 3. The definition itself never changes; the two schedulers differ only through their validated configurations.

## Ready-made preset

`DefaultScheduler` is an asynchronous factory that assembles the same definition-and-instance machinery with the FSRS-7 model (by default), `dateChrono`, and the standard scheduling policies, so you only pass options. See [DefaultScheduler](https://open-spaced-repetition.github.io/ts-fsrs/guide/scheduler/default-scheduler.md) for the options and versions, or [Quick start](https://open-spaced-repetition.github.io/ts-fsrs/guide/quick-start.md) to run it first.
