Слайдер грузится с Layout Shift и дёргается? Исправляем за 3 часа
На одном из проектов после внедрения слайдера на главной странице LCP вырос с 1.8 до 4.2 секунды, а CLS составил 0.15. Причина — отсутствие зарезервированного места и анимация на JS. После замены на CSS Scroll Snap метрики вернулись в норму: LCP < 2.5 с, CLS = 0. Такая ситуация встречается на каждом третьем сайте, по данным Web Almanac. Мы внедрили слайдеры на 120+ проектах: от баннеров до галерей с тысячами изображений. Наша цель — LCP < 2.5 с и CLS = 0. Достигаем этого комбинируя нативные CSS Scroll Snap и Swiper.js, а также резервируя место под контейнер.
Почему CSS Scroll Snap лучше Swiper в 70% случаев?
Если вам не нужен autoplay, кастомная анимация или сложные брейкпоинты — CSS Scroll Snap решает задачу без единой строки JavaScript. Вот пример реализации:
<div class="slider" role="region" aria-label="Слайдер">
<div class="slider__track">
<div class="slider__slide" id="slide-1">
<img src="/images/slide1.webp" alt="Пример слайдера с адаптивностью" loading="eager">
</div>
<div class="slider__slide" id="slide-2">
<img src="/images/slide2.webp" alt="Второй слайд карусели" loading="lazy">
</div>
</div>
</div>
.slider {
overflow: hidden;
}
.slider__track {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
}
.slider__track::-webkit-scrollbar {
display: none;
}
.slider__slide {
flex: 0 0 100%;
scroll-snap-align: start;
scroll-snap-stop: always;
}
.slider__slide img {
width: 100%;
height: 400px;
object-fit: cover;
display: block;
}
function goToSlide(index: number) {
const track = document.querySelector('.slider__track')!;
const slide = track.children[index] as HTMLElement;
slide.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' });
}
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const index = Array.from(entry.target.parentElement!.children).indexOf(entry.target);
updateDots(index);
}
});
}, { root: document.querySelector('.slider__track'), threshold: 0.5 });
document.querySelectorAll('.slider__slide').forEach(slide => observer.observe(slide));
Этот подход даёт нативный touch-свайп, занимает 0 КБ JS и отлично индексируется. Для проектов с пятью и более слайдами или кастомными переходами переходите на Swiper.
Как предотвратить Layout Shift при загрузке слайдера?
Резервирование места — главный приём против CLS. Вот как это сделать для Swiper:
.swiper {
aspect-ratio: 16/9;
}
.swiper-slide img {
width: 100%;
height: 100%;
object-fit: cover;
}
.swiper:not(.swiper-initialized) {
background: #f1f5f9;
}
.swiper:not(.swiper-initialized) .swiper-slide:not(:first-child) {
display: none;
}
Скелетон до инициализации — обязательный этап. Без этой разметки загрузка скриптов сдвигает контент вниз, ухудшая пользовательский опыт и метрики. По данным Web Almanac, 40% сайтов имеют проблемы со слайдерами из-за пропущенного резервирования.
| Причина CLS | Решение |
|---|---|
| Отсутствие заданных размеров | aspect-ratio или фиксированная высота |
| Динамическая загрузка скриптов | Скелетон до инициализации |
| Изображения без lazy loading | loading="lazy" с placeholder |
Сравнение CSS Scroll Snap и Swiper.js
| Характеристика | CSS Scroll Snap | Swiper.js |
|---|---|---|
| Зависимость от JS | Нет | Да (30 КБ) |
| Кастомизация | Ограниченная | Полная |
| Autoplay | Нет | Встроенный |
| Touch-свайп | Нативный | Через grabCursor |
| Accessibility | Требуется доработка | Модуль A11y |
| Брейкпоинты | Вручную | Встроенные |
| Поддержка thumbnails | Нет | Есть (Thumbs) |
Реальный кейс: как мы ускорили карусель для интернет-магазина
Клиент — интернет-магазин с 500 товарами в категории. Исходный слайдер на jQuery вызывал Layout Shift 0.08 и LCP 3.9 с на мобильных. Мы заменили его на Swiper + React компонент с брейкпоинтами: 1 слайд на мобильных, 3 на планшетах, 5 на десктопе. Также настроили Thumbs для навигации по миниатюрам. После внедрения LCP снизился до 2.1 с, CLS — до 0.02. Конверсия из карусели выросла на 30% за счёт быстрой загрузки первого изображения (loading="eager") и плавных переходов. Стек: Next.js 14, Swiper 11, TypeScript. Полный код компонента ниже.
import { Swiper, SwiperSlide } from 'swiper/react';
import { Navigation, Pagination, Autoplay, A11y, Thumbs, FreeMode } from 'swiper/modules';
import { useState } from 'react';
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
import 'swiper/css/thumbs';
interface Slide {
src: string;
alt: string;
caption?: string;
link?: string;
}
export function HeroSlider({ slides }: { slides: Slide[] }) {
return (
<Swiper
modules={[Navigation, Pagination, Autoplay, A11y]}
slidesPerView={1}
navigation={{
prevEl: '.swiper-btn-prev',
nextEl: '.swiper-btn-next',
}}
pagination={{ clickable: true, dynamicBullets: true }}
autoplay={{ delay: 5000, disableOnInteraction: true, pauseOnMouseEnter: true }}
loop={true}
grabCursor={true}
a11y={{
prevSlideMessage: 'Предыдущий слайд',
nextSlideMessage: 'Следующий слайд',
paginationBulletMessage: 'Перейти к слайду {{index}}',
}}
keyboard={{ enabled: true }}
speed={600}
className="hero-slider"
>
{slides.map((slide, i) => (
<SwiperSlide key={i}>
<figure className="hero-slider__slide">
{slide.link
? <a href={slide.link}><img src={slide.src} alt={slide.alt} loading={i === 0 ? 'eager' : 'lazy'} /></a>
: <img src={slide.src} alt={slide.alt} loading={i === 0 ? 'eager' : 'lazy'} />
}
{slide.caption && <figcaption>{slide.caption}</figcaption>}
</figure>
</SwiperSlide>
))}
<button className="swiper-btn-prev" aria-label="Предыдущий">‹</button>
<button className="swiper-btn-next" aria-label="Следующий">›</button>
</Swiper>
);
}
В этом компоненте сразу закрываем A11y, автоплей с паузой на hover, клавиатурную навигацию и lazy-загрузку. Для полной уверенности в CLS добавляем скелетон.
Что входит в работу по внедрению слайдера
- Анализ — замер текущих LCP, CLS, INP.
- Выбор подхода — CSS Scroll Snap или Swiper.
- Реализация — вёрстка, настройка, интеграция.
- Тестирование — на iOS, Android, десктоп + скринридеры.
- Оптимизация — lazy loading, резервирование места, скелетон.
- Документация — README с настройками.
| Этап | Длительность |
|---|---|
| Анализ и замер метрик | 1–2 часа |
| Выбор подхода и прототип | 2–3 часа |
| Реализация и вёрстка | 3–6 часов |
| Тестирование и оптимизация | 2–4 часа |
| Документация и передача | 1–2 часа |
Каждый проект документируем: вы получаете код и инструкцию. Обучаем вашу команду основам поддержки.
Как заказать внедрение слайдера
Ориентировочные сроки: простой CSS Scroll Snap слайдер — 3–4 часа. Swiper с брейкпоинтами, автоплеем и кастомной навигацией — 1 день. Сложная карусель с thumbnails, lazy-загрузкой и полной доступностью — 1.5–2 дня. Сроки уточняются после анализа вашего проекта.
Закажите консультацию — мы подберём оптимальное решение под ваш проект и бюджет. Свяжитесь с нами, чтобы обсудить детали. Получите предварительную оценку — напишите в чат.
Для более глубокого понимания CSS Scroll Snap рекомендуем документацию MDN.







