SlickからSwiperへの移行記事を以前書きましたが、もう一つの選択肢としてSplideも紹介しておきます。Splideは日本人の開発者が作った軽量スライダーで、jQuery非依存・アクセシビリティ対応・Lighthouseエラーゼロを売りにしています。Swiperと比べてファイルサイズが小さく、W3Cのカルーセルデザインパターンに準拠しているのが特徴です。

SwiperとSplideの違い

先にSwiperとの違いを簡単に整理しておきます。どちらもjQuery不要で高機能ですが、方向性が異なります。

// ===== Swiper vs Splide 比較 =====

// ファイルサイズ(bundle / gzip)
// Swiper: 約140KB / 39KB
// Splide: 約29KB / 12KB ← 約1/3

// jQuery依存
// Swiper: 不要
// Splide: 不要

// エフェクト
// Swiper: slide / fade / cube / coverflow / flip / cards / creative
// Splide: slide / fade のみ(WebGLエフェクトはスポンサー限定)

// アクセシビリティ
// Swiper: 基本対応
// Splide: W3C Carousel Design Pattern 準拠、Live Region対応

// HTML構造
// Swiper: .swiper > .swiper-wrapper > .swiper-slide(3階層)
// Splide: .splide > .splide__track > .splide__list > .splide__slide(4階層)

// breakpoints基準
// Swiper: min-width(モバイルファースト)
// Splide: v4からmax-width(CSSメディアクエリと同じ、mediaQueryオプションで変更可能)

// 拡張機能
// Swiper: 全モジュールがbundleに同梱
// Splide: Auto Scroll / Intersection / Grid / Video は別途Extension読み込みが必要

エフェクトの種類ではSwiperが圧倒的ですが、実務でcubeやcoverflowを使う機会はほぼありません。slideとfadeだけで十分な場合はSplideのほうが軽くて取り回しやすいです。

CDNで導入する

npmでも導入できますが、WordPress案件ではCDNが手軽です。バージョンを固定しておくのがポイントです。

<!-- Splide CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@splidejs/splide@4/dist/css/splide.min.css">

<!-- Splide JS -->
<script src="https://cdn.jsdelivr.net/npm/@splidejs/splide@4/dist/js/splide.min.js"></script>

テーマ付きCSSも用意されています。デフォルト以外にsplide-skyblue.min.csssplide-sea-green.min.cssがあります。スタイルを完全にカスタマイズする場合はsplide-core.min.cssを使うと、矢印やページネーションのデフォルトスタイルが含まれないのでオーバーライドの手間が減ります。

基本のHTML構造

Splideの構造はSlickやSwiperとも異なります。Slickは2階層、Swiperは3階層でしたが、Splideは.splide > .splide__track > .splide__list > .splide__slideの4階層になります。

<!-- コンテンツに関連するカルーセル(バナー、ギャラリー等)の場合 -->
<section class="splide" aria-label="メインスライダー">
  <div class="splide__track">
    <ul class="splide__list">
      <li class="splide__slide"><img src="slide1.jpg" alt=""></li>
      <li class="splide__slide"><img src="slide2.jpg" alt=""></li>
      <li class="splide__slide"><img src="slide3.jpg" alt=""></li>
    </ul>
  </div>
</section>

<!-- 装飾的なカルーセルの場合 -->
<div class="splide" role="group" aria-label="装飾スライダー">
  <div class="splide__track">
    <ul class="splide__list">
      <li class="splide__slide">Slide 01</li>
      <li class="splide__slide">Slide 02</li>
      <li class="splide__slide">Slide 03</li>
    </ul>
  </div>
</div>

Swiperとの違いとして、Splideではルート要素にaria-labelを設定することが推奨されています。<section>を使う場合と<div>を使う場合で構造が少し異なる点も注意してください。<ul><li>の代わりに<div>を使うこともできます。

基本の初期化

Swiperとほぼ同じ感覚ですが、Splideでは最後に.mount()を呼ぶ必要があります。

document.addEventListener('DOMContentLoaded', function () {
  new Splide('.splide', {
    type: 'loop',
    perPage: 1,
    speed: 400,

    autoplay: true,
    interval: 3000,
    pauseOnHover: true,

    pagination: true,
    arrows: true,
  }).mount();
});

Swiperではnew Swiper()だけで初期化が完了しますが、Splideではnew Splide().mount()の2ステップです。Extensionを使う場合は.mount({ AutoScroll })のように引数で渡します。

Slick → Splide オプション対応表

Swiper記事と同じく、Slickからの移行で迷いやすいオプションをまとめておきます。

// ===== Slick → Splide 対応表 =====

// Slick: autoplay: true, autoplaySpeed: 3000
autoplay: true, interval: 3000,
// ※ Swiperは autoplay: { delay: 3000 } とオブジェクト形式

// Slick: infinite: true
type: 'loop',
// ※ Swiperも loop: true

// Slick: speed: 300
speed: 300,
// ※ Swiperも speed: 300

// Slick: slidesToShow: 3
perPage: 3,
// ※ Swiperは slidesPerView: 3

// Slick: slidesToScroll: 1
perMove: 1,
// ※ Swiperは slidesPerGroup: 1

// Slick: dots: true
pagination: true,
// ※ Swiperは pagination: { el: '.swiper-pagination' }

// Slick: arrows: true
arrows: true,
// ※ Swiperは navigation: { nextEl: '...', prevEl: '...' }

// Slick: fade: true
type: 'fade', rewind: true,
// ※ fadeでループさせるにはrewindが必要(loopは使えない)
// ※ Swiperは effect: 'fade', fadeEffect: { crossFade: true }

// Slick: centerMode: true
focus: 'center',
// ※ Swiperは centeredSlides: true

// Slick: variableWidth: true
autoWidth: true,
// ※ Swiperは slidesPerView: 'auto'

// Slick: adaptiveHeight: true
autoHeight: true,
// ※ Swiperも autoHeight: true

// Slick: draggable: false
drag: false,
// ※ Swiperは allowTouchMove: false

// Slick: vertical: true
direction: 'ttb', height: '400px',
// ※ Splideは縦方向の場合heightまたはheightRatioの指定が必須
// ※ Swiperは direction: 'vertical'

// Slick: rtl: true
direction: 'rtl',
// ※ SwiperはHTML側に dir="rtl" を追加

Swiperとの比較コメントも入れておきました。Splideのほうがオプション名が直感的なものが多い印象です。特にナビゲーション周りは、Swiperのようにel要素を指定する必要がなく、true / falseだけで済むのが楽です。

主要オプション一覧

実務でよく使うものをカテゴリ別に整理しました。

基本オプション

new Splide('.splide', {
  // カルーセルのタイプ
  type: 'slide',   // 'slide'(デフォルト)| 'loop' | 'fade'
  
  // トランジション速度(ミリ秒)
  speed: 400,
  // 巻き戻し(loopと異なり、最後→最初へアニメーション遷移)
  rewind: true,
  rewindSpeed: 800,  // 巻き戻し時のみ速度を変更
  // 1ページに表示するスライド数
  perPage: 3,
  // 一度に移動するスライド数
  perMove: 1,
  // スライド間のギャップ(CSS形式可)
  gap: '1rem',
  // カルーセルの余白(CSS形式可)
  padding: { left: '5%', right: '5%' },
  // 開始インデックス
  start: 0,
  // スライドの向き
  direction: 'ltr',  // 'ltr' | 'rtl' | 'ttb'(縦)
  // 縦方向の場合は高さが必須
  // height: '400px',
  // heightRatio: 0.5,
}).mount();

ナビゲーション・ページネーション

new Splide('.splide', {
  // 矢印ナビゲーション
  arrows: true,
  // 矢印のSVGパスを変更(40×40のSVG)
  // arrowPath: 'm15.5 0.932-4.3 4.38 14.5 14.6-14.5...',
  // ページネーション(ドット)
  pagination: true,
  // ページネーションのキーボードショートカット
  paginationKeyboard: true,
}).mount();

オートプレイ

new Splide('.splide', {
  autoplay: true,
  // 自動再生の間隔(ミリ秒)
  interval: 5000,
  // ホバーで一時停止
  pauseOnHover: true,
  // フォーカス時に一時停止
  pauseOnFocus: true,
  // 再開時にプログレスをリセット
  resetProgress: true,
}).mount();

Splideはプログレスバーと再生/一時停止ボタンをHTML側で追加できます。

<section class="splide" aria-label="自動再生スライダー">
  <div class="splide__track">
    <ul class="splide__list">
      <li class="splide__slide">...</li>
    </ul>
  </div>
  <!-- プログレスバー -->
  <div class="splide__progress">
    <div class="splide__progress__bar"></div>
  </div>
  <!-- 再生/停止トグルボタン -->
  <button class="splide__toggle" type="button">
    <span class="splide__toggle__play">Play</span>
    <span class="splide__toggle__pause">Pause</span>
  </button>
</section>

ドラッグ・タッチ操作

new Splide('.splide', {
  // ドラッグの有効/無効
  drag: true,  // true | false | 'free'
  // ドラッグ開始の閾値
  dragMinThreshold: {
    mouse: 0,
    touch: 10,
  },
  // フリック時の飛び距離(大きいほど遠くへ)
  flickPower: 600,
  // フリックで移動できる最大ページ数
  flickMaxPages: 1,
  // ドラッグ除外セレクタ
  noDrag: 'input, textarea, .no-drag',
  // フリーモードでスナップさせる
  snap: true,
  // トランジション中の操作を無効にする
  waitForTransition: false,
}).mount();

レイアウト・サイズ

new Splide('.splide', {
  // カルーセルの最大幅(CSS形式可)
  width: '80%',
  // スライドの高さ
  height: '300px',
  // スライド幅を固定(perPageを無視)
  fixedWidth: '200px',
  // スライド高さを固定
  fixedHeight: '150px',
  // カルーセル幅に対する高さの比率
  heightRatio: 0.5,
  // スライドの幅を各要素の幅で決定
  autoWidth: false,
  // スライドの高さを各要素の高さで決定
  autoHeight: false,
  // フォーカスするスライドの位置
  focus: 'center',  // number | 'center'
  // 端の余白をトリムするか
  trimSpace: true,  // true | false | 'move'
  // 最後のページで不要なドットを省略(v4.1.0〜)
  omitEnd: false,
}).mount();

その他の便利オプション

new Splide('.splide', {
  // キーボード操作
  keyboard: false,  // false | 'global' | 'focused'
  // マウスホイールでのナビゲーション
  wheel: false,
  wheelMinThreshold: 5,
  wheelSleep: 200,
  // 遅延読み込み
  lazyLoad: false,  // false | 'nearby' | 'sequential'
  preloadPages: 1,  // nearbyモードで前後何ページ分読み込むか
  // img の src を親要素の background-image に変換
  cover: false,
  // is-active を移動開始時点で更新
  updateOnMove: true,
  // breakpoint時に破棄
  // destroy: true,
  // ライブリージョン(スクリーンリーダー向け)
  live: true,
  // アニメーション軽減設定への対応
  reducedMotion: {
    speed: 0,
    autoplay: false,
  },
}).mount();

レスポンシブ設定(breakpoints)

Splide v4のbreakpointsはCSSメディアクエリと同じmax-width基準です。Swiperがmin-width基準なのと逆なので、移行時は注意が必要です。

new Splide('.splide', {
  perPage: 3,
  gap: '1rem',
  // max-width基準(CSSと同じ感覚で書ける)
  breakpoints: {
    1024: {
      perPage: 2,
      gap: '0.75rem',
    },
    640: {
      perPage: 1,
      gap: '0.5rem',
    },
  },
}).mount();
// min-width基準にしたい場合
new Splide('.splide', {
  mediaQuery: 'min',
  perPage: 1,
  breakpoints: {
    640: {
      perPage: 2,
    },
    1024: {
      perPage: 3,
    },
  },
}).mount();

v4の重要な変更点として、breakpointsがCSSメディアクエリと同じようにマージされるようになりました。たとえば画面幅640px以下なら、1024640の両方のbreakpointが適用されます。v3以前はウィンドウリサイズ時と初期表示時で結果が異なるバグがありましたが、v4で解消されています。

PCのみ / SPのみでSplideを適用する

Swiperの記事でも紹介したmatchMediaパターンですが、Splideではbreakpointsdestroyオプションを使う方法もあります。

方法1: breakpoints の destroy を使う

// SPのみスライダー(768px以上で破棄)
new Splide('.splide-sp-only', {
  mediaQuery: 'min',
  perPage: 1,
  breakpoints: {
    768: {
      destroy: true,
    },
  },
}).mount();
// PCのみスライダー(767px以下で破棄)
new Splide('.splide-pc-only', {
  perPage: 3,
  gap: '1rem',
  breakpoints: {
    767: {
      destroy: true,
    },
  },
}).mount();

この方法が一番シンプルです。destroyを指定するだけで、Splideが自動でスライダーの初期化と破棄を切り替えてくれます。SwiperのようにmatchMediaで手動管理する必要がありません。

方法2: matchMedia で手動管理

より細かく制御したい場合は、Swiperと同様のmatchMediaパターンも使えます。

// SPのみ Splide(matchMedia版)
let splideSP = null;
const mqSP = window.matchMedia('(max-width: 767px)');
const handleSP = (e) => {
  if (e.matches) {
    if (!splideSP) {
      splideSP = new Splide('.splide-sp-only', {
        perPage: 1,
        gap: '0.5rem',
        pagination: true,
      });
      splideSP.mount();
    }
  } else {
    if (splideSP) {
      splideSP.destroy();
      splideSP = null;
    }
  }
};
handleSP(mqSP);
mqSP.addEventListener('change', handleSP);

基本的には方法1のdestroyオプションで十分です。matchMediaを使うのは、破棄後にDOM操作を挟みたいなど特別な要件がある場合だけで良いと思います。

サムネイル付きスライダーの実装

Splideではメインとサムネイルの2つのインスタンスを.sync()で連動させます。Swiperのthumbsオプションとは書き方が異なります。

<!-- メインスライダー -->
<section id="main-slider" class="splide" aria-label="メインスライダー">
  <div class="splide__track">
    <ul class="splide__list">
      <li class="splide__slide"><img src="slide1.jpg" alt=""></li>
      <li class="splide__slide"><img src="slide2.jpg" alt=""></li>
      <li class="splide__slide"><img src="slide3.jpg" alt=""></li>
      <li class="splide__slide"><img src="slide4.jpg" alt=""></li>
      <li class="splide__slide"><img src="slide5.jpg" alt=""></li>
    </ul>
  </div>
</section>
<!-- サムネイルスライダー -->
<div id="thumb-slider" class="splide" aria-label="サムネイル">
  <div class="splide__track">
    <ul class="splide__list">
      <li class="splide__slide"><img src="thumb1.jpg" alt=""></li>
      <li class="splide__slide"><img src="thumb2.jpg" alt=""></li>
      <li class="splide__slide"><img src="thumb3.jpg" alt=""></li>
      <li class="splide__slide"><img src="thumb4.jpg" alt=""></li>
      <li class="splide__slide"><img src="thumb5.jpg" alt=""></li>
    </ul>
  </div>
</div>
// メインスライダー
const main = new Splide('#main-slider', {
  type: 'fade',
  rewind: true,
  pagination: false,
  arrows: true,
});
// サムネイルスライダー
const thumb = new Splide('#thumb-slider', {
  perPage: 4,
  gap: '10px',
  rewind: true,
  pagination: false,
  isNavigation: true,  // ← クリックでメインと連動させるために必要
  arrows: false,
});
// sync() で連携してからmount()
main.sync(thumb);
main.mount();
thumb.mount();

ポイントはisNavigation: true.sync()です。Swiperではメイン側のthumbsオプションにサムネイルインスタンスを渡しますが、Splideでは.sync()メソッドで双方向に接続します。.sync().mount()の前に呼ぶ必要があるので注意してください。

Auto Scroll(無限スクロール)

マーキーのように自動で流れ続けるカルーセルは、SplideではAuto Scroll拡張機能を使います。別途CDNで読み込みが必要です。

<!-- Auto Scroll Extension -->
<script src="https://cdn.jsdelivr.net/npm/@splidejs/splide-extension-auto-scroll@0/dist/js/splide-extension-auto-scroll.min.js"></script>
new Splide('.splide', {
  type: 'loop',
  drag: 'free',
  focus: 'center',
  perPage: 3,
  gap: '1rem',
  arrows: false,
  pagination: false,
  autoScroll: {
    speed: 1,           // スクロール速度(ピクセル/フレーム)
    pauseOnHover: true,  // ホバーで一時停止
    pauseOnFocus: true,  // フォーカスで一時停止
  },
}).mount({ AutoScroll: window.splide.Extensions.AutoScroll });

実務ではお客様のロゴ一覧や実績バナーの横スクロールでよく使います。SwiperではautoplayspeedcssModeを組み合わせて擬似的に実現しますが、Splideのほうが専用Extensionがあるぶん実装がシンプルです。

ナビゲーションのCSSカスタマイズ

Splideのデフォルトスタイルを上書きする場合のCSSです。

/* ===== 矢印ボタンのカスタマイズ ===== */
.splide__arrow {
  background: #fff;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  opacity: 1;
}
.splide__arrow svg {
  width: 16px;
  height: 16px;
  fill: #333;
}
.splide__arrow:hover {
  background: #f5f5f5;
}
/* 矢印を外側に配置 */
.splide__arrow--prev {
  left: -24px;
}
.splide__arrow--next {
  right: -24px;
}
/* ===== ページネーション(ドット)のカスタマイズ ===== */
.splide__pagination__page {
  width: 10px;
  height: 10px;
  background: #ccc;
  opacity: 0.5;
  margin: 0 6px;
}
.splide__pagination__page.is-active {
  background: #333;
  opacity: 1;
  /* アクティブなドットを横長にする */
  width: 24px;
  border-radius: 5px;
  transition: width 0.3s;
}
/* ===== プログレスバーのカスタマイズ ===== */
.splide__progress__bar {
  height: 3px;
  background: #333;
}

Swiperと違い、SplideはCSS変数による上書きではなく、クラスを直接オーバーライドする形になります。完全にゼロからスタイルを作る場合はsplide-core.min.cssを読み込めば、矢印やドットのデフォルトスタイルが入っていないので書きやすいです。

メソッド一覧

const splide = new Splide('.splide');
splide.mount();
// スライド移動
splide.go(2);       // インデックス2へ
splide.go('+1');     // 1つ先へ
splide.go('-1');     // 1つ前へ
splide.go('>');      // 次のページへ(perPageを考慮)
splide.go('<');      // 前のページへ
// スライドの追加・削除
splide.add('
  • New
  • '); splide.add('
  • New
  • ', 2); // インデックス2に挿入 splide.remove(0); // インデックス0を削除 splide.remove([0, 1]); // 複数削除 // カルーセルのリフレッシュ splide.refresh(); // 破棄 splide.destroy(); // 完全に破棄 splide.destroy(true); // 軽量破棄(HTMLは保持、リサイズ監視を維持) // イベント splide.on('move', (newIndex, prevIndex) => { console.log(`${prevIndex} → ${newIndex}`); }); splide.on('moved', (newIndex) => { console.log(`移動完了: ${newIndex}`); }); splide.on('drag', () => console.log('ドラッグ中')); splide.on('dragged', () => console.log('ドラッグ終了')); splide.on('resize', () => console.log('リサイズ')); // イベント解除 splide.off('move'); splide.off('move.myNamespace'); // 名前空間で解除 // カルーセルタイプの判定 if (splide.is('loop')) { // ループモードの場合の処理 } // 現在のインデックス console.log(splide.index); // スライド数 console.log(splide.length);

    まとめ

    SlickからSplideへの移行ポイントをまとめておきます。HTML構造が4階層になること、.mount()を忘れないこと、breakpointsがmax-width基準であること、fadeでループさせるにはtype: 'fade'rewind: trueの組み合わせが必要なこと、の4つが引っかかりやすい点です。

    Swiperと比べると、エフェクトの種類は少ないですが、ファイルサイズが約1/3で、アクセシビリティへの配慮が手厚く、breakpointsのdestroyで簡単にレスポンシブ切り替えができるのが強みです。「slideとfadeだけで十分」「なるべく軽くしたい」「アクセシビリティを重視したい」という案件にはSplideが向いています。SwiperとSplideどちらを選ぶかは案件の要件次第ですが、選択肢として両方知っておくと便利です。