Реалізація слайдера/каруселі на сайті

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація слайдера/каруселі на сайті
Проста
~1 робочий день
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Реалізація слайдера/каруселі на вебсайті

Слайдер — один з найбільш зловживаних UI-елементів і одночасно джерело болю з продуктивністю. Нативний CSS Scroll Snap розв'язує 70% випадків без JS. Для решти — Swiper.js.

CSS Scroll Snap: без JS

<div class="slider" role="region" aria-label="Слайдер">
  <div class="slider__track">
    <div class="slider__slide" id="slide-1">
      <img src="/images/slide1.webp" alt="Слайд 1" loading="eager">
    </div>
    <div class="slider__slide" id="slide-2">
      <img src="/images/slide2.webp" alt="Слайд 2" loading="lazy">
    </div>
    <div class="slider__slide" id="slide-3">
      <img src="/images/slide3.webp" alt="Слайд 3" 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;       /* Firefox */
}

.slider__track::-webkit-scrollbar {
  display: none;               /* Chrome/Safari */
}

.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;
}
// Навігація через JS (опціонально)
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' })
}

// Визначаємо поточний слайд через IntersectionObserver
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))

Swiper.js: повнофункціональна карусель

npm install swiper
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>
  )
}

Карусель товарів з брейкпоінтами

export function ProductCarousel({ products }: { products: Product[] }) {
  return (
    <Swiper
      modules={[Navigation, FreeMode]}
      navigation
      freeMode={{ enabled: true, sticky: false }}
      slidesPerView={1.2}
      spaceBetween={16}
      breakpoints={{
        480: { slidesPerView: 2.2, spaceBetween: 16 },
        768: { slidesPerView: 3.2, spaceBetween: 20 },
        1024: { slidesPerView: 4,   spaceBetween: 24, freeMode: { enabled: false } },
        1280: { slidesPerView: 5,   spaceBetween: 24 },
      }}
      grabCursor={true}
    >
      {products.map(product => (
        <SwiperSlide key={product.id}>
          <ProductCard product={product} />
        </SwiperSlide>
      ))}
    </Swiper>
  )
}

Галерея з мініатюрами

export function GalleryWithThumbs({ images }: { images: string[] }) {
  const [thumbsSwiper, setThumbsSwiper] = useState<any>(null)

  return (
    <div className="gallery-with-thumbs">
      <Swiper
        modules={[FreeMode, Navigation, Thumbs]}
        thumbs={{ swiper: thumbsSwiper && !thumbsSwiper.destroyed ? thumbsSwiper : null }}
        spaceBetween={10}
        navigation={true}
        className="gallery-main"
      >
        {images.map((src, i) => (
          <SwiperSlide key={i}>
            <img src={src} alt={`Фото ${i + 1}`} loading={i === 0 ? 'eager' : 'lazy'} />
          </SwiperSlide>
        ))}
      </Swiper>

      <Swiper
        onSwiper={setThumbsSwiper}
        modules={[FreeMode, Thumbs]}
        spaceBetween={8}
        slidesPerView={5}
        freeMode={true}
        watchSlidesProgress={true}
        className="gallery-thumbs"
      >
        {images.map((src, i) => (
          <SwiperSlide key={i}>
            <img src={src} alt={`Мініатюра ${i + 1}`} loading="lazy" />
          </SwiperSlide>
        ))}
      </Swiper>
    </div>
  )
}

Продуктивність: запобігання CLS

Слайдер без явних розмірів викликає Layout Shift при ініціалізації:

/* Резервуємо висоту до завантаження JS */
.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;
}

Автоплей з паузою на hover та focus

// Swiper вбудована пауза працює для hover
// Додаємо паузу для доступності при фокусі на слайді
const swiper = document.querySelector('.swiper')?.swiper

swiper?.el.addEventListener('focusin', () => swiper.autoplay.stop())
swiper?.el.addEventListener('focusout', () => swiper.autoplay.start())

Терміни

CSS Scroll Snap слайдер з навігацією — 3–4 години. Swiper з брейкпоінтами, автоплеєм та кастомною навігацією — 1 день. Складна карусель з мініатюрами, lazy-завантаженням, запобіганням CLS та доступністю — 1,5–2 дні.