Back

Краткое руководство по индикаторам загрузки в веб-приложениях

Краткое руководство по индикаторам загрузки в веб-приложениях

Пользователь нажал на кнопку. Что-то происходит — но что именно? Без чёткой обратной связи он предположит худшее: приложение сломалось, действие не выполнилось или нужно нажать ещё раз. Индикаторы загрузки решают эту проблему, но неправильный подход создаёт новые.

Это руководство охватывает современный UX индикаторов загрузки — от выбора правильного паттерна до реализации состояний загрузки React Suspense и соглашений Next.js App Router loading.tsx, при этом поддерживая здоровые показатели INP Core Web Vitals.

Ключевые выводы

  • Глобальные спиннеры блокируют взаимодействие и ухудшают как пользовательский опыт, так и показатели INP — используйте вместо них локализованную, прогрессивную обратную связь.
  • Подбирайте индикатор под ситуацию: спиннеры для кратких ожиданий, скелетоны для загрузки контента, прогресс-бары для измеримых операций и оптимистичный UI для действий, которые должны ощущаться мгновенными.
  • Границы React Suspense и loading.tsx в Next.js обеспечивают состояния загрузки с областью видимости сегмента, позволяя пользователям взаимодействовать с незатронутыми частями приложения.
  • Доступность не подлежит обсуждению: используйте aria-busy, живые регионы и учитывайте предпочтения по уменьшению анимации.

Почему глобальные спиннеры обычно неправильны

Полноэкранные спиннеры блокируют всё. Пользователи не могут читать контент, переходить в другие разделы или делать что-либо продуктивное. Хуже того, они ухудшают воспринимаемую производительность, даже когда фактическое время загрузки приемлемо.

Современный подход: локализованная, прогрессивная обратная связь. Показывайте состояния загрузки только там, где контент действительно загружается. Позвольте пользователям взаимодействовать со всем остальным.

Это важно для INP (Interaction to Next Paint) — метрики Core Web Vitals, которая измеряет отзывчивость. Глобальные оверлеи загрузки, блокирующие взаимодействие, могут негативно повлиять на ваши показатели INP. Локализованные индикаторы сохраняют остальную часть вашего UI отзывчивой.

Выбор правильного индикатора

Разные ситуации требуют разных паттернов:

Спиннеры

Лучше всего подходят для кратких, неопределённых ожиданий до 3 секунд. Используйте их инлайн — внутри кнопок, рядом с полями форм или внутри небольших областей контента. Избегайте центрирования спиннера на пустом экране.

Скелетоны (Skeleton Screens)

Идеальны для начальной загрузки страницы или разделов контента. Скелетоны показывают форму загружаемого контента, сокращая воспринимаемое время ожидания и предотвращая сдвиг макета. Они особенно хорошо работают для списков, карточек и текстовых областей.

Прогресс-бары

Резервируйте их для определённых процессов: загрузки файлов, многошаговых операций или чего-либо, где можно рассчитать фактический прогресс. Фальшивые прогресс-бары, не отражающие реальность, раздражают пользователей больше, чем честные спиннеры.

Оптимистичный UI

Для действий вроде сохранения, лайков или переключений обновляйте UI немедленно и синхронизируйтесь с сервером потом. Пользователи воспринимают мгновенную отзывчивость. Обрабатывайте сбои корректно с откатом и чёткими состояниями ошибок.

Рабочие паттерны фреймворков

Состояния загрузки React Suspense

Границы Suspense в React позволяют декларативно объявлять UI загрузки. Оберните асинхронные компоненты в <Suspense> с fallback, и React позаботится об остальном. React 19 улучшает это, делая асинхронные переходы и состояния ожидания UI первоклассными, уменьшая визуальные рывки при навигации между состояниями.

Ключевая идея: стратегически вкладывайте границы Suspense. Одна граница верхнего уровня даёт вам глобальный спиннер — именно то, чего вы пытаетесь избежать. Множество детализированных границ позволяют разным секциям загружаться независимо.

loading.tsx в Next.js App Router

Соглашение loading.tsx в App Router обеспечивает автоматический UI загрузки для каждого сегмента маршрута. Поместите файл loading.tsx в любую папку маршрута, и Next.js покажет его во время загрузки этого сегмента.

Критическая деталь: это область видимости сегмента, а не универсальный глобальный загрузчик. Каждый сегмент маршрута может иметь своё собственное состояние загрузки. loading.tsx в /dashboard влияет только на сегмент дашборда, а не на всю оболочку приложения.

Это естественно сочетается с потоковым SSR — контент рендерится прогрессивно по мере доступности данных, а loading.tsx заполняет пробелы.

View Transitions API

Для изменений страниц и маршрутов View Transitions API становится всё более жизнеспособной альтернативой традиционным спиннерам загрузки. Вместо показа загрузчика во время подготовки следующей страницы браузер может плавно анимировать переход между состояниями. Это ощущается быстрее, даже когда фактическое время загрузки схоже.

API работает во всех фреймворках и предоставляет CSS-хуки для настройки анимаций переходов. Он особенно эффективен для навигации в пределах одного источника, где вы контролируете обе страницы.

Требования к доступности

Индикаторы загрузки должны работать для всех:

  • aria-busy="true": Применяйте к контейнерам, контент которых загружается. Программы чтения с экрана объявляют состояние занятости.
  • role="status" с живыми регионами: Для сообщений о загрузке, которые должны быть объявлены. Используйте aria-live="polite", чтобы не прерывать пользователей.
  • Уменьшение анимации: Учитывайте prefers-reduced-motion. Заменяйте вращающиеся анимации статичными индикаторами или тонкими изменениями прозрачности.
  • Никогда не полагайтесь только на визуал: Сопровождайте анимированные индикаторы текстовыми метками или ARIA-объявлениями. «Загрузка вашего дашборда…» лучше молчаливого спиннера.

Распространённые ошибки

Показ загрузчиков для быстрых операций: Если что-то завершается менее чем за 100 мс, индикатор загрузки создаёт мерцание. Используйте debounce для состояний загрузки — показывайте их только после короткой задержки.

Ненужная блокировка интерактивности: Если действие не требует реального ожидания (например, подтверждение платежа), позвольте пользователям продолжать использовать приложение.

Вводящие в заблуждение прогресс-бары: Прогресс-бар, застрявший на 99% на две минуты, разрушает доверие. Если вы не можете измерить реальный прогресс, используйте неопределённый индикатор.

Скрытие уже загруженного контента: При обновлении данных показывайте устаревший контент с тонким индикатором обновления, а не заменяйте всё скелетоном.

Заключение

Хороший UX индикаторов загрузки сводится к честности и локальности. Сообщайте пользователям, что происходит, где это происходит, и позволяйте им делать всё остальное. Современные паттерны, такие как состояния загрузки React Suspense, loading.tsx в Next.js App Router и View Transitions API, делают это проще, чем когда-либо, — при этом поддерживая ваши показатели INP Core Web Vitals в норме.

Начните с аудита текущих состояний загрузки. Замените глобальные блокировщики локализованной обратной связью. Ваши пользователи — и ваши метрики производительности — будут вам благодарны.

Часто задаваемые вопросы

Используйте скелетоны при загрузке контента с предсказуемым макетом, например списков, карточек или текстовых блоков. Скелетоны сокращают воспринимаемое время ожидания, показывая форму загружаемого контента. Спиннеры лучше работают для кратких, непредсказуемых ожиданий или инлайн-обратной связи внутри кнопок и полей форм.

Добавьте короткую задержку перед показом индикатора загрузки, обычно от 100 до 200 миллисекунд. Если операция завершается до истечения задержки, пропустите индикатор полностью. Это предотвращает резкое мерцание, при этом всё ещё обеспечивая обратную связь для действительно медленных операций.

Нет. Файл loading.tsx имеет область видимости сегмента, то есть влияет только на сегмент маршрута, в котором он размещён. loading.tsx в папке dashboard показывается только во время загрузки этого сегмента. Остальная часть оболочки вашего приложения остаётся интерактивной и незатронутой.

Применяйте aria-busy true к контейнерам с загружаемым контентом. Используйте role status с aria-live polite для сообщений о загрузке, которые должны быть объявлены. Всегда сопровождайте визуальные индикаторы текстовыми метками или ARIA-объявлениями, чтобы пользователи, которые не видят анимации, всё равно получали обратную связь.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay