# How to Use the Anime.js Scope Feature for Scoped Animations

> Master Anime.js scoped animations with createScope and revert. Isolate animations to DOM sections and manage responsive defaults using media queries for cleaner, efficient control.

- Repository: [Julian Garnier/anime](https://github.com/juliangarnier/anime)
- Tags: how-to-guide
- Published: 2026-03-04

---

**Use `createScope()` to isolate animations to specific DOM sections, manage responsive defaults with media queries, and automatically clean up with `revert()`.**

Anime.js version 3 introduces a powerful Scope API that enables developers to create encapsulated animation contexts within specific DOM boundaries. The scope feature for scoped animations allows you to restrict animation queries to designated root elements, share default parameters across multiple animations, and handle responsive behavior through integrated media query support.

## What Are Scoped Animations in Anime.js?

Scoped animations provide DOM-level encapsulation by restricting animation effects to a specific root element and its children. When you create a scope using `createScope()`, the library temporarily switches the global execution context so that all subsequent animation calls automatically target elements within your specified root.

This architecture prevents animation conflicts between different page sections and enables modular animation design. According to the source code in [`src/scope/scope.js`](https://github.com/juliangarnier/anime/blob/main/src/scope/scope.js) (lines 107-121), the `execute()` method handles this context switching by temporarily modifying `globals.scope`, `globals.root`, and `globals.defaults` to the current scope before running your callbacks.

## Creating a Scope with `createScope()`

The `createScope()` factory function accepts a configuration object and returns a Scope instance. The constructor, implemented in [`src/scope/scope.js`](https://github.com/juliangarnier/anime/blob/main/src/scope/scope.js) (lines 41-62), supports three primary parameters:

- **root**: A CSS selector string, React ref, Angular element reference, or DOM node that defines the scope boundary. Defaults to `document`.
- **defaults**: An object containing default animation parameters (easing, duration, etc.) that merge with global defaults.
- **mediaQueries**: An object mapping names to media query strings, enabling responsive scope refreshes.

```javascript
import { createScope, animate } from 'animejs';

const scope = createScope({
  root: '.animation-container',
  defaults: { ease: 'out(3)', duration: 800 },
  mediaQueries: { isMobile: '(max-width: 600px)' }
});

```

## Registering Animation Constructors

Scope instances provide three distinct methods for registering animation logic, each serving different lifecycle requirements.

### Using `add()` for Responsive Animations

The `add()` method registers constructors that execute every time the scope refreshes, such as when media query conditions change. According to [`src/scope/scope.js`](https://github.com/juliangarnier/anime/blob/main/src/scope/scope.js) (lines 158-169), these constructors are stored in `this.constructors` and re-executed during `refresh()`.

```javascript
scope.add(self => {
  // Rebuilds automatically when isMobile changes
  const distance = self.matches.isMobile ? 100 : 300;
  animate('.box', { translateX: distance, duration: 1000 });
});

```

### Using `addOnce()` for Persistent Animations

For animations that should persist across refreshes without reconstruction, use `addOnce()`. As implemented in [`src/scope/scope.js`](https://github.com/juliangarnier/anime/blob/main/src/scope/scope.js) (lines 176-194), these callbacks run once and are never reverted when the scope refreshes.

```javascript
scope.addOnce(() => {
  // Continuous background rotation survives media query changes
  animate('.background', {
    rotate: 360,
    duration: 4000,
    loop: true,
    ease: 'linear'
  });
});

```

### Using `keepTime()` for Seamless Transitions

The `keepTime()` method, defined in [`src/scope/scope.js`](https://github.com/juliangarnier/anime/blob/main/src/scope/scope.js) (lines 198-212), preserves the animation playhead position when scopes refresh. This prevents jarring resets when responsive parameters change.

```javascript
scope.keepTime(() => {
  return animate('.progress-bar', {
    width: ['0%', '100%'],
    duration: 5000,
    ease: 'linear',
    loop: true
  });
});

```

## Handling Responsive Design with Media Queries

When you pass a `mediaQueries` object to `createScope()`, the library creates `MediaQueryList` objects and attaches change listeners that automatically trigger `refresh()` when viewport conditions change. Inside your `add()` callbacks, you receive a `self` parameter containing a `matches` object that reflects the current state of your defined media queries.

The `handleEvent` method in [`src/scope/scope.js`](https://github.com/juliangarnier/anime/blob/main/src/scope/scope.js) (lines 217-222) catches these change events, while `refresh()` (lines 124-141) manages the reconstruction cycle by clearing previous revertibles and running all permanent constructors again.

```javascript
const scope = createScope({
  root: '.hero-section',
  mediaQueries: {
    isTablet: '(max-width: 1024px)',
    isMobile: '(max-width: 640px)'
  }
})
.add(self => {
  // Access responsive states via self.matches
  const scale = self.matches.isMobile ? 0.8 : self.matches.isTablet ? 1.0 : 1.2;
  animate('.hero-title', { scale, duration: 600 });
});

```

## Cleaning Up Scoped Animations

Proper cleanup prevents memory leaks and event listener accumulation. The scope provides two mechanisms for destruction: automatic media query listener removal and manual `revert()` invocation.

### Manual Revert

Calling `revert()` immediately stops all animations, removes media query listeners, and clears internal collections. According to [`src/scope/scope.js`](https://github.com/juliangarnier/anime/blob/main/src/scope/scope.js) (lines 250-252), this method iterates through all revertible objects and executes their cleanup routines.

```javascript
// Cleanup when navigating away or component unmounting
scope.revert();

```

### Return Cleanup Functions

When using `add()`, return a cleanup function that removes event listeners or DOM modifications. The scope stores these functions and executes them during `refresh()` before rebuilding animations.

```javascript
scope.add(() => {
  const element = document.querySelector('.interactive');
  const handler = () => animate(element, { scale: 1.1 });
  element.addEventListener('click', handler);
  
  // Cleanup function for scope management
  return () => element.removeEventListener('click', handler);
});

```

## Complete Working Example

The following example, adapted from the official playground at [`tests/playground/scope/index.js`](https://github.com/juliangarnier/anime/blob/main/tests/playground/scope/index.js), demonstrates combining media queries, persistent animations, and time-keeping across responsive breakpoints:

```javascript
import { createScope, animate, utils } from 'animejs';

const scope = createScope({
  root: '.animation-stage',
  mediaQueries: { isSmall: '(max-width: 800px)' },
  defaults: { ease: 'linear' }
})
.add(self => {
  const squares = utils.$('.square');

  // Persistent animations that survive refreshes
  self.addOnce(() => animate('.square', {
    y: [0, -50, 0, 50, 0],
    loop: true,
    ease: 'inOut(2)',
    duration: 2500
  }));

  // Time-keeping animation for seamless transitions
  self.keepTime(() => animate('.square', {
    rotate: 360,
    duration: 2000,
    loop: true,
    alternate: true
  }));

  // Interactive effects with cleanup
  function handlePointerEnter() {
    animate(this, { scale: 1.5, ease: 'out(3)', duration: 500 });
  }
  function handlePointerLeave() {
    animate(this, { scale: 1, ease: 'out(3)', duration: 500 });
  }

  squares.forEach($sq => {
    $sq.addEventListener('pointerenter', handlePointerEnter);
    $sq.addEventListener('pointerleave', handlePointerLeave);
  });

  return () => {
    squares.forEach($sq => {
      $sq.removeEventListener('pointerenter', handlePointerEnter);
      $sq.removeEventListener('pointerleave', handlePointerLeave);
    });
  };
});

// Manual cleanup trigger
document.body.addEventListener('click', () => scope.revert());

```

## Summary

- **Use `createScope()`** to establish DOM boundaries and shared defaults for your animations, preventing conflicts between page sections.
- **Choose the right constructor method**: `add()` for responsive animations that rebuild on media query changes, `addOnce()` for persistent loops, and `keepTime()` for animations that must maintain their playhead across refreshes.
- **Leverage media queries** by passing a `mediaQueries` object to `createScope()` and accessing match states via `self.matches` in your constructors.
- **Clean up properly** by calling `revert()` when components unmount or scopes are destroyed, and return cleanup functions from `add()` to remove event listeners.

## Frequently Asked Questions

### What is the difference between `add()` and `addOnce()` in Anime.js scopes?

The `add()` method registers constructors that execute every time the scope refreshes, such as when media query conditions change, making it ideal for responsive animations. In contrast, `addOnce()` registers constructors that run only once and persist across scope refreshes without being torn down, which is perfect for infinite looping background animations that should not reset when the viewport changes.

### How do media queries work with Anime.js scoped animations?

When you pass a `mediaQueries` object to `createScope()`, the library creates `MediaQueryList` objects and attaches change listeners that automatically trigger `refresh()` when viewport conditions change. Inside your `add()` callbacks, you receive a `self` parameter containing a `matches` object that reflects the current state of your defined media queries, allowing you to adjust animation parameters dynamically without manual event listeners.

### Does using `keepTime()` affect animation performance?

Using `keepTime()` has minimal performance impact because it simply preserves the internal playhead time of the returned Tickable animation when the scope refreshes, rather than destroying and recreating the animation instance. This method stores the Tickable in `this.constructorsOnce` and ensures continuity across media query changes, which is particularly useful for progress indicators or ambient animations where resetting the animation state would create a jarring user experience.

### When should I manually call `revert()` on a scope?

You should manually call `revert()` when your component unmounts, the user navigates away from the page section, or you need to immediately stop all animations and remove event listeners within that scope. The `revert()` method iterates through all revertible objects, stops animations, removes media query listeners, and executes any cleanup functions returned by your constructors, preventing memory leaks in single-page applications or dynamic web components.