Next.js и AOS

Библиотека AOS (Animate On Scroll) изначально разрабатывалась для классических HTML/JS проектов, где весь код выполняется в браузере. В среде Next.js возникает ключевое отличие — наличие серверного рендеринга (SSR), что влияет на работу библиотек, зависящих от window и document.

AOS напрямую использует DOM, поэтому при попытке инициализации на сервере возникает ошибка. Это требует аккуратной интеграции с учётом жизненного цикла React-компонентов.


Установка и подключение

Установка выполняется стандартным способом:

npm install aos

или

yarn add aos

Подключение стилей обязательно:

import 'aos/dist/aos.css'

Инициализация AOS в Next.js

Главная задача — запуск AOS только на клиенте. Для этого используется хук useEffect, который гарантированно выполняется после монтирования компонента в браузере.

import { useEffect } from 'react'
import AOS from 'aos'
import 'aos/dist/aos.css'

export default function MyApp({ Component, pageProps }) {
  useEffect(() => {
    AOS.init({
      duration: 800,
      once: true
    })
  }, [])

  return <Component {...pageProps} />
}

В Next.js это чаще всего размещается в файле _app.js или _app.tsx.


Проблема SSR и её обход

При серверном рендеринге отсутствует объект window, что может вызывать ошибки:

ReferenceError: window is not defined

Чтобы избежать этого:

Подход 1: useEffect (основной способ)

Инициализация только внутри useEffect — наиболее простой и безопасный метод.

Подход 2: динамический импорт

import dynamic from 'next/dynamic'

const AOS = dynamic(() => import('aos'), { ssr: false })

Однако этот способ применяется реже, поскольку AOS не используется как React-компонент.


Использование data-атрибутов

Анимации задаются через HTML-атрибуты:

<div data-aos="fade-up">
  Контент
</div>

Основные типы анимаций:

  • fade
  • fade-up
  • fade-down
  • fade-left
  • fade-right
  • zoom-in
  • zoom-out
  • slide-up
  • flip-left

Дополнительные параметры

AOS поддерживает гибкую настройку через data-атрибуты:

<div
  data-aos="fade-up"
  data-aos-duration="1000"
  data-aos-delay="200"
  data-aos-offset="100"
>
  Контент
</div>

Ключевые параметры:

  • duration — длительность анимации
  • delay — задержка перед запуском
  • offset — расстояние до начала анимации
  • easing — функция сглаживания
  • once — запуск только один раз

Глобальные настройки

Передаются в AOS.init():

AOS.init({
  offset: 120,
  duration: 600,
  easing: 'ease-in-out',
  delay: 0,
  once: false,
  mirror: false
})

Описание:

  • offset — сдвиг точки активации
  • duration — базовая длительность
  • easing — тип анимации
  • mirror — повтор при прокрутке вверх

Обновление анимаций при изменении DOM

Next.js активно использует динамический рендеринг, особенно при переходах между страницами и загрузке данных. В таких случаях AOS может не видеть новые элементы.

Для обновления используется:

AOS.refresh()

или

AOS.refreshHard()

Пример с React:

useEffect(() => {
  AOS.refresh()
}, [data])

Интеграция с маршрутизацией Next.js

При переходе между страницами AOS не всегда автоматически переинициализируется. Для корректной работы требуется отслеживать изменения маршрута.

import { useRouter } from 'next/router'

const router = useRouter()

useEffect(() => {
  const handleRouteChange = () => {
    AOS.refresh()
  }

  router.events.on('routeChangeComplete', handleRouteChange)

  return () => {
    router.events.off('routeChangeComplete', handleRouteChange)
  }
}, [])

Использование с компонентами

AOS работает независимо от структуры компонентов React. Главное — чтобы элементы присутствовали в DOM.

Пример:

function Card() {
  return (
    <div data-aos="zoom-in">
      Карточка
    </div>
  )
}

При использовании списков:

{items.map((item, index) => (
  <div key={index} data-aos="fade-up" data-aos-delay={index * 100}>
    {item.title}
  </div>
))}

Оптимизация производительности

AOS может создавать нагрузку при большом количестве элементов.

Основные рекомендации:

  • Ограничивать количество анимируемых элементов
  • Использовать once: true
  • Уменьшать длительность анимаций
  • Избегать сложных эффектов (например, flip)

Работа с серверными компонентами (Next.js 13+)

В новых версиях Next.js используется разделение на серверные и клиентские компоненты.

AOS можно использовать только в клиентских компонентах:

'use client'

Пример:

'use client'

import { useEffect } from 'react'
import AOS from 'aos'
import 'aos/dist/aos.css'

export default function AnimatedSection() {
  useEffect(() => {
    AOS.init()
  }, [])

  return <div data-aos="fade-up">Контент</div>
}

Кастомизация через CSS

AOS добавляет классы:

  • aos-init
  • aos-animate

Это позволяет создавать собственные анимации:

[data-aos="custom-fade"] {
  opacity: 0;
  transition: opacity 0.5s ease;
}

[data-aos="custom-fade"].aos-animate {
  opacity: 1;
}

Использование:

<div data-aos="custom-fade">
  Контент
</div>

Частые проблемы

Анимации не запускаются

  • AOS не инициализирован
  • отсутствуют стили
  • элементы вне viewport

Анимации не обновляются

  • требуется AOS.refresh()

Ошибка SSR

  • инициализация вне useEffect

Сравнение с альтернативами

AOS подходит для простых декларативных анимаций. В Next.js также используются:

  • Framer Motion — сложные анимации и контроль состояния
  • GSAP — продвинутые сценарии
  • React Spring — физические анимации

AOS выигрывает за счёт простоты и минимального количества кода, но уступает в гибкости.


Практический пример страницы

'use client'

import { useEffect } from 'react'
import AOS from 'aos'
import 'aos/dist/aos.css'

export default function Home() {
  useEffect(() => {
    AOS.init({ duration: 800 })
  }, [])

  return (
    <main>
      <section data-aos="fade-up">
        <h1>Заголовок</h1>
      </section>

      <section data-aos="fade-right">
        <p>Текст</p>
      </section>

      <section data-aos="zoom-in">
        <button>Кнопка</button>
      </section>
    </main>
  )
}

Поведение при гидрации

Во время гидрации React сопоставляет серверный HTML с клиентским. Поскольку AOS добавляет классы только после инициализации, возможна кратковременная “неанимированная” отрисовка.

Решение:

  • скрывать элементы до инициализации
  • использовать CSS:
[data-aos] {
  opacity: 0;
}

[data-aos].aos-animate {
  opacity: 1;
}

Управление повторными анимациями

Для повторного запуска:

AOS.init({
  once: false
})

Для ручного контроля:

element.classList.remove('aos-animate')

Использование с lazy loading

При подгрузке компонентов (например, через next/dynamic) требуется дополнительный refresh:

useEffect(() => {
  AOS.refresh()
}, [])

Итоговая архитектура интеграции

  1. Подключение стилей
  2. Инициализация в useEffect
  3. Использование data-aos атрибутов
  4. Обновление через AOS.refresh() при изменении DOM
  5. Учёт SSR и клиентских компонентов

Такая схема обеспечивает корректную работу анимаций в условиях серверного рендеринга и динамического интерфейса Next.js.