Skip to main content

no-floating-infinite-animations

Require infinite Web Animations to be retained so they can be canceled.

Rule catalog ID: R020

Targeted pattern scopeโ€‹

This type-aware rule targets Element#animate(...) calls whose timing options explicitly use infinite iterations:

  • { iterations: Infinity }
  • { iterations: +Infinity }
  • { iterations: Number.POSITIVE_INFINITY }
  • { iterations: globalThis.Number.POSITIVE_INFINITY }

The rule uses TypeScript parser services to confirm that the receiver is a DOM Element. That keeps project-local animate(...) methods out of scope.

What this rule reportsโ€‹

The rule reports infinite animations when the returned Animation is discarded or immediately used through a non-cleanup member:

  • standalone infinite element.animate(...) calls
  • immediate chains such as element.animate(...).play()

Finite one-shot animations are intentionally allowed. Fire-and-forget finite animations are common and do not have the same long-running cleanup risk. Immediate cancel() and finish() calls are allowed.

Why this rule existsโ€‹

Element#animate() returns an Animation object. Infinite animations continue until canceled, finished, the effect is removed, or the document lifecycle ends. If the returned Animation is discarded, component or route cleanup code cannot reliably stop it.

Incorrectโ€‹

element.animate(keyframes, {
duration: 1000,
iterations: Infinity,
});
element.animate(keyframes, {
iterations: Number.POSITIVE_INFINITY,
}).play();

Correctโ€‹

const animation = element.animate(keyframes, {
duration: 1000,
iterations: Infinity,
});

cleanupCallbacks.add(() => animation.cancel());
function startPulse(element: Element) {
return element.animate(keyframes, {
duration: 1000,
iterations: Infinity,
});
}
element.animate(keyframes, {
iterations: 1,
});

Behavior and migration notesโ€‹

This rule is deliberately narrower than a general Element#animate() rule. It only targets explicitly infinite animations because one-shot animations are often safe to let complete naturally.

This rule does not autofix. A safe fix needs to choose the correct lifecycle owner and cancellation point.

ESLint flat config exampleโ€‹

import runtimeCleanup from "eslint-plugin-runtime-cleanup";

export default [
runtimeCleanup.configs["recommended-type-checked"],
];

When not to use itโ€‹

Do not enable this rule without TypeScript parser services. For intentionally page-lifetime infinite decorative animations, use a narrow disable comment near the animation start.

Further readingโ€‹