- 技術 💻
Swiperの使い方|オプション早見表・React/Vue・動かない原因を実演
結論
Swiperは、CSSとJSを読み込み、決まったHTMLに new Swiper() を書くと動くスライダーです。jQuery は要りません。
この記事でわかること
- コピペで動く最小コードと、オプション早見表27項目
- ループ・自動再生・breakpointsの動きの実測値
- 動かない・崩れる原因12種のエラー原文と直し方
Swiper は、.swiper の中の .swiper-wrapper を横に動かして、中の .swiper-slide を1枚ずつ見せる仕組みです。初期化すると、Swiper がクラス・幅・transform を書き込みます。
flowchart TD
A[HTML の3つの入れ子<br>swiper・wrapper・slide] --> C[new Swiper で初期化]
B[CSS と JS<br>を読み込む] --> C
C --> D[クラスと幅を書き込む]
D --> E[wrapper を transform で動かす]
E --> F[スライダーとして表示]
急いでいる人向けに、よく調べられることの答えを先にまとめます。
| やりたいこと | 書き方 | 詳しい節 |
|---|---|---|
| 最新版を CDN で読む | swiper@14 の bundle の CSS と JS |
導入方法 |
| 無限にループさせる | loop: true |
ループ |
| 自動再生する | autoplay: { delay: 3000 } |
自動再生 |
| 触ったら自動再生を止める | disableOnInteraction: true を明記 |
自動再生 |
| 点のページ送り・矢印 | pagination・navigation |
ページネーション |
| 3枚並べる・間をあける | slidesPerView: 3, spaceBetween: 20 |
複数枚表示 |
| 画面幅で枚数を変える | breakpoints: { 768: {...} } |
レスポンシブ |
| 幅が数千万 px になる | 親の flex・grid に min-width: 0 |
動かないとき |
| React で使う | swiper/react の Swiper・SwiperSlide |
React |
確認環境:Ubuntu 24.04.4 LTS・Chromium 141.0.7390.37(Playwright 1.56.0 から headless で操作)・Node.js 22.22.2。Swiper 14.3.0(npm の latest)を npm で入れ、CDN と同じファイル(swiper-bundle.min.js・.css・.mjs)を手元の HTTP サーバーから配信して、この記事の HTML と JavaScript(Next.js と WordPress の例を除く)を Chromium で実行し、初期化前後の DOM・クラス・transform・コンソールの警告とエラーの原文を記録しました。React 19.3.1・Vue 3.5.44 の例は Vite 8.3.5 でビルドして Chromium で表示し、ファイルの大きさは esbuild 0.28.2 でまとめて測りました。以前の版との違いは、npm の 6.8.4〜11.0.0 のパッケージの中身で確かめています。既定値はパッケージのソース(modules・shared の .mjs)と型定義、導入方法・オプション・変更履歴は swiperjs.com の公式ドキュメント(Get Started・Swiper API・React・Vue・Element・Changelog)、CDN の版の書き方は jsDelivr の README、Next.js の Client Component は nextjs.org、自動再生の停止の決まりは W3C の WCAG 2.2 で確かめました(確認日 2026年10月23日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Swiperの使い方|オプション早見表・React/Vue・動かない原因を実演」
Swiperとは(できること・最新版・ライセンス)
この節では、Swiper が何で、何ができ、いまの版がどれかが分かります。
Swiper(スワイパー)は、画像やカードを横(または縦)にスライドさせて見せる「スライダー(カルーセル)」を作る JavaScript のライブラリです。 スマホの指のスワイプ、マウスのドラッグ、矢印、点のページ送り、自動再生などを、オプションを書くだけで付けられます。
1jQuery が要らない
素の JavaScript だけで動く。読み込むのは Swiper の CSS と JS の2つ。
2部品を足していく作り
矢印・点・自動再生・フェードなどは「モジュール」。npm では使う分だけ読み込める。
3React・Vue・Web Components
同じパッケージに React と Vue の部品、タグで書ける Swiper Element が入っている。
4MIT ライセンス
商用のサイトでも無料で使える。パッケージの LICENSE は The MIT License。
最新版は 14 系(13 は欠番)
2026年10月23日時点の最新版は 14.3.0(2026年9月28日公開)です。公式の変更履歴(Changelog)によると、12.2.0 の次が 14.0.0 で、「We skipped v13.」と書かれています。
| 版 | 公開 | 主な変化(公式の変更履歴) |
|---|---|---|
| 14.0.0 | 2026年6月26日 | TypeScript で書き直し。動作の変更はなし。対応ブラウザを Chrome・Edge 110 以降、Safari 16.4 以降、Firefox 110 以降に |
| 12.0.0 | 2025年9月11日 | 矢印を SVG アイコンに。LESS・SCSS のスタイルを廃止して CSS だけに |
| 11.0.0 | 2023年9月24日 | ループを作り直し。loopedSlides を loopAdditionalSlides に置き換え |
14.0.0 の変更履歴には「Upgrading from v12 requires no code changes」とあり、12 からはコードを変えずに上げられます。上の対応ブラウザより以前のブラウザを支える必要があるなら 12 系を使うよう、同じ変更履歴に書かれています。
Swiper を使うか、CSS だけで作るか
スライダーの作り方を選ぶ
横に並べてスワイプできれば済むなら CSS、動きの種類が多いなら Swiper
CSS だけで作る横スクロールのカルーセルは、関連記事「CSS scroll-snapで横スクロールカルーセル|JSなしの完成版」で扱っています。
Swiperの導入方法(CDN・npm・ダウンロード)
この節では、Swiper を読み込む3つの方法と、どれを選ぶかが分かります。
| 方法 | 向いている場面 | 読み込むもの |
|---|---|---|
| CDN | 静的なサイト・WordPress・試作 | bundle の CSS と JS(全部入り) |
| npm | Vite・webpack・React・Vue | 本体と、使うモジュールだけ |
| ダウンロード | 外部の CDN を使えないサイト | bundle のファイルを自分のサーバーに置く |
CDN で読み込む
公式の Get Started にある CDN の例は、2026年10月23日時点で swiper@12 と書かれたままです(サイトの見出しの版表示は v14.3.0)。CDN の jsDelivr は npm の版をそのまま配るので、14 系を使うなら @12 を @14 に変えます。
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@14/swiper-bundle.min.css">
<script src="https://cdn.jsdelivr.net/npm/swiper@14/swiper-bundle.min.js"></script>
jsDelivr の README では、@3 のような版の範囲の指定は「version-fallback」の仕組みで動き続け、@latest や版なしの指定は本番に向かない(大きな版の変更で動きが変わることがある)とされています。本番では swiper@14 か、swiper@14.3.0 のように版を固定します。
ES モジュールで読むときは、.mjs のファイルを使います。
<script type="module">
import Swiper from 'https://cdn.jsdelivr.net/npm/swiper@14/swiper-bundle.min.mjs';
const swiper = new Swiper('.swiper', { loop: true });
</script>
npm で入れる
npm install swiper
npm の swiper から読み込む本体(import Swiper from 'swiper')は、矢印や点などのモジュールが入っていない「core」だけです。使うモジュールを swiper/modules から読み込み、modules に渡し、CSS も別に読み込みます。
import Swiper from 'swiper';
import { Navigation, Pagination, Autoplay } from 'swiper/modules';
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
const swiper = new Swiper('.swiper', {
modules: [Navigation, Pagination, Autoplay],
loop: true,
autoplay: { delay: 3000 },
pagination: { el: '.swiper-pagination', clickable: true },
navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev' },
});
全部のモジュールをまとめて読み込むなら、import Swiper from 'swiper/bundle' と import 'swiper/css/bundle' です。
ファイルの大きさ(実測)
14.3.0 のファイルと、esbuild で minify してまとめた結果です。
| まとめた内容 | minify 後 | gzip 後 |
|---|---|---|
| 本体(core)だけ | 66,367 バイト | 20,597 バイト |
| 本体+Navigation+Pagination | 79,284 バイト | 24,455 バイト |
| swiper/bundle(全モジュール) | 153,233 バイト | 45,650 バイト |
使うモジュールが少ないなら、npm で選んで読み込むと bundle の約半分になりました。
基本のHTMLと初期化(コピペで動く最小コード)
この節では、そのまま貼って動く最小のスライダーと、初期化で DOM がどう変わるかが分かります。
コピペで動く最小コード
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@14/swiper-bundle.min.css">
<style>
.swiper { width: 100%; max-width: 600px; height: 300px; }
.swiper-slide { display: flex; align-items: center; justify-content: center; background: #eee; }
</style>
</head>
<body>
<div class="swiper">
<div class="swiper-wrapper">
<div class="swiper-slide">スライド1</div>
<div class="swiper-slide">スライド2</div>
<div class="swiper-slide">スライド3</div>
</div>
<div class="swiper-pagination"></div>
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
<div class="swiper-scrollbar"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/swiper@14/swiper-bundle.min.js"></script>
<script>
const swiper = new Swiper('.swiper', {
pagination: { el: '.swiper-pagination' },
navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev' },
scrollbar: { el: '.swiper-scrollbar' },
});
</script>
</body>
</html>
CSS を head で読み込む
CSS が無いと、スライドが縦に積み重なって全部見える(後の節で実測)。
3つのクラスを入れ子にする
外側が swiper、その子が swiper-wrapper、さらにその子が swiper-slide。名前は変えない。
大きさを CSS で決める
横向きは幅、縦向きは高さを指定する。縦向きで高さが無いと崩れる。
JS を読み込んでから初期化する
Swiper の script より前に new Swiper を書くと「Swiper is not defined」になる。
| クラス名 | 役割 | 必須か |
|---|---|---|
swiper |
外枠。はみ出した部分を隠す | 必須 |
swiper-wrapper |
スライドを並べて動かす帯 | 必須 |
swiper-slide |
1枚1枚のスライド | 必須 |
swiper-pagination |
点(ページ送り)を入れる場所 | 使うときだけ |
swiper-button-prev・swiper-button-next |
前へ・次への矢印 | 使うときだけ |
swiper-scrollbar |
スクロールバー | 使うときだけ |
初期化で DOM はこう変わる(実測)
上のコードを Chromium で開き、初期化の後の DOM を取り出しました(矢印の SVG は省略)。
<div class="swiper swiper-initialized swiper-horizontal swiper-backface-hidden">
<div class="swiper-wrapper" id="swiper-wrapper-59d8eefd85c26243" aria-live="polite">
<div class="swiper-slide swiper-slide-active" role="group" aria-label="1 / 3" style="width: 600px;">…</div>
<div class="swiper-slide swiper-slide-next" role="group" aria-label="2 / 3" style="width: 600px;">…</div>
<div class="swiper-slide" role="group" aria-label="3 / 3" style="width: 600px;">…</div>
</div>
<div class="swiper-pagination swiper-pagination-bullets swiper-pagination-horizontal">
<span class="swiper-pagination-bullet swiper-pagination-bullet-active" aria-current="true"></span>
<span class="swiper-pagination-bullet"></span><span class="swiper-pagination-bullet"></span>
</div>
<div class="swiper-button-prev swiper-button-disabled" tabindex="-1" role="button" aria-label="Previous slide" aria-disabled="true"><svg class="swiper-navigation-icon" …></svg></div>
<div class="swiper-button-next" tabindex="0" role="button" aria-label="Next slide"><svg …></svg></div>
…
</div>
swiper.slideNext() で1枚進めた後の変化です。
activeIndex : 0 → 1
wrapper の transform: matrix(1, 0, 0, 1, 0, 0) → matrix(1, 0, 0, 1, -600, 0)
スライドのクラス : [active, next, ―] → [prev, active, next]
Swiper は、スライドを動かしているのではなく、swiper-wrapper をスライド1枚分(600px)ずつ transform で左にずらしています。 今のスライドには swiper-slide-active、前後には swiper-slide-prev・swiper-slide-next が付くので、今のスライドだけ色を変えるといった装飾は、このクラスに CSS を書きます(クラス名は slideActiveClass などのオプションで変えられます)。矢印の中の SVG アイコンは 12.0.0 から Swiper が自動で入れるようになりました(navigation.addIcons の既定値 true)。
よく使うオプション早見表
この節では、よく使うオプションの意味と既定値が一覧で分かります。既定値は 14.3.0 のソースと公式の Swiper API で確かめました。
| オプション | 既定値 | 意味・よく使う値 |
|---|---|---|
loop |
false | 最後の次を最初にする無限ループ |
rewind |
false | 最後の次で最初に巻き戻す(ループしない) |
speed |
300 | 切り替えにかける時間(ミリ秒) |
autoplay.delay |
3000 | 自動再生の間隔(ミリ秒) |
autoplay.disableOnInteraction |
false(実測) | true で操作後に自動再生を止める |
autoplay.pauseOnMouseEnter |
false | マウスが乗っている間は止める |
autoplay.reverseDirection |
false | 逆向き(前へ)に自動再生する |
pagination.el |
― | 点を入れる要素のセレクター |
pagination.type |
bullets | bullets・fraction・progressbar・custom |
pagination.clickable |
false | 点をクリックで移動できる |
navigation.nextEl・prevEl |
― | 次へ・前への要素のセレクター |
slidesPerView |
1 | 一度に見せる枚数。1.2 や auto も可 |
spaceBetween |
0 | スライドの間(px) |
slidesOffsetBefore・After |
0 | 最初の前・最後の後の余白(px) |
slidesPerGroup |
1 | 1回で進む枚数 |
centeredSlides |
false | 今のスライドを中央に置く |
breakpoints |
― | 画面幅ごとに上の値を変える |
direction |
horizontal | vertical で縦向き |
effect |
slide | fade・cube・coverflow・flip・cards・creative |
initialSlide |
0 | 最初に見せるスライドの番号(0始まり) |
autoHeight |
false | 高さを今のスライドに合わせる |
freeMode |
false | 指を離した位置で止める(吸着しない) |
grabCursor |
false | マウスを乗せると手の形のカーソル |
scrollbar.hide |
false(実測) | true で操作の後にスクロールバーを隠す |
scrollbar.draggable |
false | スクロールバーをドラッグで動かせる |
mousewheel.forceToAxis |
false | 向きの合うホイールの回転だけで動く |
watchOverflow |
true | 枚数が足りないと止めて矢印を隠す |
autoplay.disableOnInteraction は、公式の Swiper API と型定義では既定値が true と書かれていますが、14.3.0 のソースの既定値は false で、実際に操作しても自動再生は止まりませんでした(後の節で実測)。止めたいときも、止めたくないときも、この値は明記します。 scrollbar.hide も同じで、型定義では true ですが、ソースの既定値は false で、操作して2秒たってもスクロールバーの不透明度は 1 のままでした(hide: true では 0 になりました)。
全部のオプションとメソッドは、公式の Swiper API のページにあります。ここからは、よく使うものを1つずつ動かして見ていきます。
ループ(loop)の使い方と動き
この節では、loop: true で DOM がどう変わるか、枚数が足りないと何が起きるかが分かります。
const swiper = new Swiper('.swiper', {
loop: true,
});
いまのループは「複製」せずに並べ替える
5枚のスライドで loop: true にし、1枚目から「前へ」を2回押したときの DOM の並びです(* が今のスライド、カッコは data-swiper-slide-index)。
初期化直後 : S1(0)* S2(1) S3(2) S4(3) S5(4) realIndex=0
前へ1回 : S5(4)* S1(0) S2(1) S3(2) S4(3) realIndex=4
前へ2回 : S4(3)* S5(4) S1(0) S2(1) S3(2) realIndex=3
次へ1回 : S4(3) S5(4)* S1(0) S2(1) S3(2) realIndex=4
スライドの数は5枚のまま増えず、端に来る前に後ろのスライドを前へ(または前を後ろへ)付け替えていました。 以前の版(8 まで)は、ループのためにスライドを複製し、swiper-slide-duplicate というクラスを付けていました。14.3.0 のソースにはこのクラス名がありません。
8 までのループ
前後にスライドを複製する
複製には duplicate のクラスが付く
複製の分だけ DOM のスライドが増える
9 以降のループ
複製しない。スライドの順番を付け替える
各スライドに data-swiper-slide-index が付く
何枚目かは activeIndex でなく realIndex で読む
ループ中は DOM の順番が変わるので、activeIndex(DOM の中で何番目か)は0のまま、本当の番号の realIndex が4になる、ということが起きます。「今は何枚目か」を表示するときは realIndex を使います。
枚数が足りないときの警告
ループに必要な枚数は、公式の説明では「slidesPerView + slidesPerGroup 以上(centeredSlides ならさらに足す)」で、slidesPerGroup で割り切れる必要があります。いくつかの組み合わせで動かしました。
| 枚数と設定 | 結果 | コンソール |
|---|---|---|
| 5枚・1枚表示 | ループする | なし |
| 2枚・1枚表示 | S1→S2→S1 と交互にループ | なし |
| 4枚・3枚表示 | ループする | なし |
| 4枚・3枚表示・中央寄せ | 動くが並びが乱れる | 足りない警告(移動のたび) |
| 3枚・3枚表示 | 動かない(枚数が足りず停止) | なし |
| 5枚・2枚ずつ進む | 空のスライドが1枚足される | なし |
| 5枚・2枚ずつ・空の追加なし | 端で止まり方がずれる | 割り切れない警告 |
出た警告の原文です。
Swiper Loop Warning: The number of slides is not enough for loop mode, it will be disabled or not function properly. You need to add more slides (or make duplicates) or lower the values of slidesPerView and slidesPerGroup parameters
Swiper Loop Warning: The number of slides is not even to slidesPerGroup, loop mode may not function properly. You need to add more slides (or make duplicates, or empty slides)
- 「not enough」は、枚数が足りない警告です。
slidesPerViewを下げるか、同じスライドを複製して枚数を増やします。 - 「not even to slidesPerGroup」は、割り切れない警告です。既定(
loopAddBlankSlides: true)では空のスライド(swiper-slide-blank)が自動で足されて警告は出ません。loopAddBlankSlides: falseにしたときに出ました。 - 3枚で3枚表示のように、全部が一度に見えていると、
watchOverflow(既定 true)が働いてスライダー自体が止まります(swiper.isLockedが true)。
枚数が少ないときに JavaScript で複製してからループさせる例です。
const wrapper = document.querySelector('.swiper-wrapper');
const originals = [...wrapper.children];
if (originals.length < 6) {
originals.forEach((slide) => wrapper.append(slide.cloneNode(true))); // 2倍にする
}
const swiper = new Swiper('.swiper', { loop: true, slidesPerView: 3 });
3枚を6枚に複製して3枚表示でループさせると、警告は出ずに「次へ」で進み、swiper.slides.length は 6 でした。複製すると点の数も2倍になるので、点で「何枚目か」を見せるときは realIndex % originals.length で元の番号に直します。
ループの代わりに rewind
rewind: true は、最後のスライドの次で最初へ巻き戻します。3枚で動かすと、S3 で「次へ」を押したとき S1 へ戻り、DOM の並び替えは起きませんでした。公式の説明では、rewind は loop と一緒に使わないこととされています。
自動再生(autoplay)の使い方と止め方
この節では、自動再生の書き方、何秒ごとに動くか、触ったら止める・止めない設定、停止ボタンの作り方が分かります。
const swiper = new Swiper('.swiper', {
loop: true,
autoplay: {
delay: 3000, // 3秒ごと
disableOnInteraction: true, // 触ったら止める(止めないなら false)
pauseOnMouseEnter: true, // マウスが乗っている間は一時停止
},
});
npm で core を使うときは、modules に Autoplay を入れます。
何秒ごとに切り替わるか(実測)
delay: 1000・speed: 300(既定)のとき、切り替わった時刻を記録しました。
1,016ms → 2枚目
2,333ms → 3枚目
3,653ms → 1枚目
間隔は約1.3秒で、delay(1秒)に切り替えの時間(0.3秒)を足した長さでした。 autoplay: true とだけ書いたときは、3,016ms・6,325ms で切り替わり、既定の delay は 3000 でした。
触ったら止まるか(disableOnInteraction の実測)
ドラッグ(スワイプ)と矢印のクリックで、自動再生が止まるかを見ました。
| 設定 | ドラッグした後 | 矢印を押した後 |
|---|---|---|
| 書かない(14.3.0 の既定) | 続く | 続く |
disableOnInteraction: true |
止まる(autoplayStop が発生) | 止まる |
disableOnInteraction: false |
続く | 続く |
公式の Swiper API と型定義では既定値が true と書かれていますが、14.3.0 のソース(Autoplay モジュール)の既定値は disableOnInteraction: false で、上の表のとおり何も書かないと止まりませんでした。「触ったら止める」動きにしたいなら、true を明記します。
ほかの設定の実測です。
stopOnLastSlide: true(ループなし):3枚目で止まり、1枚目へ戻りませんでした。ループなしで書かないと、3枚目の次は1枚目へ戻りました。pauseOnMouseEnter: true:マウスを乗せた後は2.5秒たっても切り替わらず、swiper.autoplay.pausedが true でした。
停止・再開のボタンを付ける
自動で5秒より長く動き続ける表示には、止める手段を用意することが、Web のアクセシビリティの基準 WCAG 2.2 の達成基準 2.2.2(レベル A)で求められています。
<button type="button" id="toggle">自動再生を停止</button>
<script>
const button = document.getElementById('toggle');
button.addEventListener('click', () => {
if (swiper.autoplay.running) {
swiper.autoplay.stop();
button.textContent = '自動再生を再開';
} else {
swiper.autoplay.start();
button.textContent = '自動再生を停止';
}
});
</script>
このボタンを開いて1秒後に押すと、3秒待っても1枚目のまま(swiper.autoplay.running は false)で、もう一度押すと再開し、2.6秒後に2枚目へ進みました。React で作った同じボタンの結果は、後の React の節にあります。
次まで何秒かを表示する
autoplayTimeLeft イベントで、次に切り替わるまでの残り時間と割合を受け取れます。delay: 2000 で1秒間記録すると82回呼ばれ、最初は 2000ms / 1.00、1秒後は 686ms / 0.34 でした。円や棒のタイマーの表示に使えます。
const swiper = new Swiper('.swiper', {
autoplay: { delay: 2000 },
on: {
autoplayTimeLeft(s, time, progress) {
document.querySelector('.timer').style.setProperty('--progress', 1 - progress);
},
},
});
止まらずに流れ続けるスライダー(ロゴの帯など)
一定の速さで流れ続ける帯は、delay: 0 と長い speed、swiper-wrapper の動きを等速(linear)にする CSS で作ります。
.logo-slider .swiper-wrapper { transition-timing-function: linear; }
.logo-slider .swiper-slide { width: 150px; }
new Swiper('.logo-slider', {
loop: true,
slidesPerView: 'auto',
spaceBetween: 10,
speed: 5000,
allowTouchMove: false,
autoplay: { delay: 0, disableOnInteraction: false },
});
1秒ごとに transform の位置を読むと、-10px・-43px・-75px・-107px と約32px ずつ動き、5秒で1枚分(160px)の等速でした。
ページネーションと矢印(pagination・navigation)のカスタマイズ
この節では、点の種類・クリックで移動・数字の点、矢印の色や大きさ、矢印をスライダーの外に置く方法が分かります。
ページネーションの種類(実測)
type |
表示 | 3枚目のときの中身 |
|---|---|---|
| bullets(既定) | 点 | 3つ目の点に active のクラス |
| fraction | 「3 / 5」 | current の span が 3、total が 5 |
| progressbar | 進み具合の棒 | 棒が scaleX(0.6) で 60% |
| custom | 自分で HTML を返す | renderCustom の戻り値 |
pagination: { el: '.swiper-pagination', type: 'fraction' }
点をクリックで動かす(clickable)
clickable を書かずに4つ目の点をクリックすると、スライドは動きませんでした(activeIndex は 0 のまま)。clickable: true では 3 に移り、点に tabindex="0" role="button" aria-label="Go to slide 1" が付いて、キーボードでも操作できるようになりました。
pagination: { el: '.swiper-pagination', clickable: true }
枚数が多いときは dynamicBullets: true で、今の点の前後だけが大きく表示されます。10枚で6枚目のとき、active-prev-prev・active-prev・active-main・active-next・active-next-next のクラスが5つの点に付きました。
点に数字を入れる(renderBullet)
pagination: {
el: '.swiper-pagination',
clickable: true,
renderBullet: (index, className) => `<span class="${className}">${index + 1}</span>`,
}
色・大きさを変える(CSS 変数)
Swiper の CSS は、色や大きさを CSS 変数で受け取ります。.swiper に書くと、そのスライダーだけに効きます。
.swiper {
--swiper-theme-color: #e60033; /* 点と矢印の色 */
--swiper-navigation-size: 24px; /* 矢印の大きさ */
--swiper-pagination-bullet-size: 12px;/* 点の大きさ */
}
Chromium で計算後のスタイルを読むと、今の点の背景と矢印の色が rgb(230, 0, 51)、矢印の高さが 24px、点の幅が 12px になりました。ほかに --swiper-pagination-bottom・--swiper-navigation-sides-offset・--swiper-pagination-bullet-inactive-color など、bundle の CSS には38個の --swiper- の変数があります。CSS 変数の使い方は、関連記事「CSS変数(カスタムプロパティ)の使い方|コピペ実例とダークモード切替」で扱っています。
矢印をスライダーの外に置く
navigation のセレクターは、.swiper の外の要素も指せます。外側に枠を作り、その中に矢印を置きます。
<div class="slider-wrap">
<div class="swiper">…</div>
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
</div>
.slider-wrap { position: relative; padding: 0 50px; }
この形で「次へ」を押すと 2枚目に移り、矢印はスライダーの右端(700px)より外側の 752px の位置に表示されました。1ページに複数のスライダーを置くときは、.slider1 .swiper-button-next のように親のクラスで区別したセレクターを渡します。
端の矢印は自動で無効になる
ループなしで1枚目のとき、前への矢印に swiper-button-disabled・aria-disabled="true"・tabindex="-1" が付き、不透明度が 0.35 になりました。最後の1枚では、次への矢印が同じ状態になります。
複数枚表示とレスポンシブ(slidesPerView・spaceBetween・breakpoints)
この節では、何枚並べるか・間のあけ方・画面幅ごとに変える方法と、それぞれの実際の幅が分かります。
slidesPerView と spaceBetween(実測)
幅 1000px のスライダーで、各スライドの幅と左端の位置を測りました。
| 設定 | スライドの幅 | 左端の位置 |
|---|---|---|
slidesPerView: 3, spaceBetween: 20 |
320px | 0・340・680・1020 |
slidesPerView: 'auto'(CSS で幅 200px) |
200px | 0・210・420・630 |
slidesPerView: 1.2, centeredSlides: true(幅 390px) |
322.3px | 34・372・710 |
Swiper は (1000 − 20 × 2) ÷ 3 = 320px を計算し、各スライドに width: 320px; margin-right: 20px を書き込んでいました。 slidesPerView: 1.2 は、次のスライドを少し見せて「横に続いている」ことを伝える形で、スマホでよく使われます。
CSS の margin で間をあけてはいけない理由
スライドの間をあける
間は spaceBetween で書く。margin は書かない
3枚表示で4枚目(slideTo(3))へ動かした後の、各スライドの左端の位置です。
CSS で margin-right: 20px : -1000, -647, -293, 60, 413, 767 ← 4枚目が 60px ずれた
spaceBetween: 20 : -1020, -680, -340, 0, 340, 680 ← 4枚目がちょうど左端
breakpoints で画面幅ごとに変える
const swiper = new Swiper('.swiper', {
slidesPerView: 1, // 767px 以下
spaceBetween: 10,
breakpoints: {
768: { slidesPerView: 2, spaceBetween: 20 }, // 768px 以上
1024: { slidesPerView: 4, spaceBetween: 30 }, // 1024px 以上
},
});
ブラウザの幅を変えながら読んだ値です。
| 画面の幅 | 効いた設定 | 枚数・間 | スライドの幅 |
|---|---|---|---|
| 390px | 基本(currentBreakpoint は max) | 1枚・10px | 390px |
| 767px | 基本 | 1枚・10px | 767px |
| 800px | 768 | 2枚・20px | 390px |
| 1100px | 1024 | 4枚・30px | 252.5px |
- キーの数値は「その幅以上」で効きます(min-width と同じ向き)。 いちばん小さいキーより狭いときは、外側に書いた基本の値が使われました。
- 既定ではブラウザの幅(window)で判定します。
breakpointsBase: 'container'にすると、スライダーの外枠の幅で判定します(公式の説明では beta)。 - 公式の説明では、
loopやeffectのように仕組みが変わるオプションは breakpoints の中で変えられません。
切り替え効果(effect)とそのほかの機能
この節では、フェードなどの効果、縦向き、サムネイル連動、複数行、マウスホイールなどの書き方と、動かした結果が分かります。
effect の種類(実測)
effect |
見え方 | 初期化後の外枠のクラス |
|---|---|---|
| slide(既定) | 横に流れる | swiper-horizontal |
| fade | 重なって入れ替わる | swiper-fade |
| cube | 立方体が回る | swiper-cube swiper-3d |
| coverflow | 奥行きのある並び | swiper-coverflow swiper-3d |
| flip | カードが裏返る | swiper-flip swiper-3d |
| cards | カードの束をめくる | swiper-cards swiper-3d |
| creative | 動きを自分で決める | swiper-creative swiper-3d |
fade は mode を指定する
effect: 'fade' の切り替え中(1秒の切り替えの途中)に、前と次のスライドの不透明度を読みました。
| 設定 | 25% の時点 | 75% の時点 | 終わり |
|---|---|---|---|
| fade だけ(mode: default) | 前 1.00・次 0.41 | 前 1.00・次 0.96 | 前 1.00・次 1.00 |
mode: 'cross-fade' |
前 0.59・次 0.41 | 前 0.04・次 0.96 | 前 0.00・次 1.00 |
mode: 'out-in' |
前 0.20・次 0.00 | 前 0.00・次 0.83 | 前 0.00・次 1.00 |
既定の fade は、前のスライドが不透明度 1 のまま残ります。 スライドの背景が透明だと、前と次の文字が重なって見えます。重ねたくないときは fadeEffect: { mode: 'cross-fade' }(同時に入れ替わる)か 'out-in'(消えてから出る。14.2.0 で追加)にします。以前の crossFade: true も動きますが、型定義では非推奨になり mode: 'cross-fade' が案内されています。
new Swiper('.swiper', {
effect: 'fade',
fadeEffect: { mode: 'cross-fade' },
});
coverflow など 3D の効果の細かい設定
効果ごとの設定は coverflowEffect・cubeEffect・flipEffect・cardsEffect・creativeEffect に書きます。coverflow の既定値(14.3.0 のソース)は次のとおりです。
new Swiper('.swiper', {
effect: 'coverflow',
centeredSlides: true,
slidesPerView: 'auto',
coverflowEffect: {
rotate: 50, // 隣のスライドの回転(度)
stretch: 0, // スライド同士の詰め具合(px)
depth: 100, // 奥行き(px)
scale: 1,
modifier: 1, // 効果の強さの倍率
slideShadows: true,
},
});
このコードで幅 200px のスライド5枚を初期化すると、2枚目に translate3d(0px, 0px, -100px) rotateX(0deg) rotateY(-50deg) scale(1) が書き込まれ、depth の 100 と rotate の 50 がそのまま使われていました。slideShadows の影の要素は、各スライドの左右に1つずつ、合わせて10個作られました。
縦向き(direction: vertical)
.swiper { height: 300px; } /* 縦向きは高さが必須 */
new Swiper('.swiper', { direction: 'vertical', mousewheel: true });
高さを指定せずに縦向きにすると、外枠の高さが 33,554,432px になり、各スライドの高さも 3.35544e+07px になりました。高さ 200px を指定すると、各スライドは 200px でした。mousewheel: true でホイールを1回回すと次のスライドへ進みました。
サムネイル連動(thumbs)
<div class="swiper main">…</div>
<div class="swiper thumbs">…</div>
const thumbs = new Swiper('.thumbs', { slidesPerView: 4, spaceBetween: 8, watchSlidesProgress: true });
const main = new Swiper('.main', { spaceBetween: 10, thumbs: { swiper: thumbs } });
.thumbs .swiper-slide { opacity: .4; }
.thumbs .swiper-slide-thumb-active { opacity: 1; }
3つ目のサムネイルをクリックすると、メインが3枚目(activeIndex 2)に移り、サムネイルの3つ目だけに swiper-slide-thumb-active が付きました(○○●○○)。サムネイル側を先に作り、メイン側の thumbs.swiper に渡します。
そのほかのよく使う機能(実測)
| 書き方 | 動き | 実測の結果 |
|---|---|---|
grid: { rows: 2 }(3列) |
2段に並べる | 上の段 S1・S3・S5(既定の fill は column) |
grid: { rows: 2, fill: 'row' } |
横に詰める | 上の段 S1・S2・S3 |
autoHeight: true |
高さを合わせる | 100px → 250px に変化 |
dir="rtl" を外枠に |
右から左へ | 次へで wrapper が +600px |
freeMode: true |
吸着しない | 途中の -250px で止まる |
zoom: { maxRatio: 3 } |
ダブルタップで拡大 | zoom.in() で 3倍 |
parallax: true |
中身を遅れて動かす | 2枚目の文字が 300px ずれて待機 |
keyboard: { enabled: true } |
矢印キーで動く | → キーで次へ |
拡大は、画像を <div class="swiper-zoom-container"> で包みます。パララックスは、動かしたい要素に data-swiper-parallax="-300" のように書きます。
スライドを後から足す・消す
swiper.appendSlide('<div class="swiper-slide">新しいスライド</div>'); // 末尾に足す
swiper.prependSlide('<div class="swiper-slide">先頭</div>'); // 先頭に足す
swiper.removeSlide(0); // 0番目を消す
swiper.removeAllSlides(); // 全部消す
swiper-wrapper に DOM でスライドを足しただけでは、swiper.slides.length も点の数も 3 のままでした。swiper.update() を呼ぶと 4 に、appendSlide() ではすぐに 5 になりました。DOM を自分で書き換えたら update() を呼びます。 使い終わったスライダーは swiper.destroy() で、イベントと Swiper が書き込んだスタイルを取り除けます。
画像の遅延読み込み
9 以降、遅延読み込みは画像の loading="lazy" 属性を使います。読み込み中の印を出すなら、スライドの中に <div class="swiper-lazy-preloader"></div> を置きます。
<div class="swiper-slide">
<img src="photo1.jpg" loading="lazy" width="600" height="400" alt="…">
<div class="swiper-lazy-preloader"></div>
</div>
6枚の画像で試すと、開いた時点で読み込まれたのは近い3枚で、次へ進むと4枚目が読み込まれました。読み込みが終わった画像の印は消え、残りの3枚には印が残りました。
ReactでSwiperを使う(Next.jsも)
この節では、React での書き方、インスタンスの取り方、Next.js(App Router)で使うときの注意が分かります。
React の部品は、同じ swiper パッケージの swiper/react に入っています。次のコードを Vite 8.3.5・React 19.3.1 でビルドし、Chromium で表示しました。
import { useRef, useState } from 'react';
import { Swiper, SwiperSlide, useSwiper } from 'swiper/react';
import { Navigation, Pagination, Autoplay, A11y } from 'swiper/modules';
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
function NextButton() {
const swiper = useSwiper(); // Swiper の中に置いた部品からインスタンスを取る
return <button className="my-next" onClick={() => swiper.slideNext()}>次へ</button>;
}
export default function App() {
const swiperRef = useRef(null);
const [index, setIndex] = useState(0);
const [playing, setPlaying] = useState(true);
const items = ['A', 'B', 'C', 'D'];
const toggle = () => {
const s = swiperRef.current;
if (playing) s.autoplay.stop(); else s.autoplay.start();
setPlaying(!playing);
};
return (
<>
<Swiper
modules={[Navigation, Pagination, Autoplay, A11y]}
slidesPerView={1}
spaceBetween={16}
loop
navigation
pagination={{ clickable: true }}
autoplay={{ delay: 4000, disableOnInteraction: true }}
breakpoints={{ 768: { slidesPerView: 2 } }}
onSwiper={(s) => { swiperRef.current = s; }}
onSlideChange={(s) => setIndex(s.realIndex)}
style={{ width: 600, height: 160 }}
>
{items.map((t) => <SwiperSlide key={t}>スライド{t}</SwiperSlide>)}
<NextButton />
</Swiper>
<p id="state">表示中:{index + 1} / {items.length}</p>
<button id="toggle" onClick={toggle}>{playing ? '自動再生を停止' : '自動再生を再開'}</button>
</>
);
}
✓ 53 modules transformed.
dist/index.html 0.27 kB │ gzip: 0.21 kB
dist/assets/index-DjMxu4j7.css 13.48 kB │ gzip: 2.24 kB
dist/assets/index-Bu6wd9x0.js 323.13 kB │ gzip: 99.40 kB
Chromium で開くと、navigation と書くだけで矢印の要素が作られ、各スライドに width: 292px; margin-right: 16px が付きました(幅 1200px の画面で breakpoints の2枚表示)。自作の「次へ」ボタンを押すと表示が「2 / 4」になり、「自動再生を停止」を押した後は5秒たっても「2 / 4」のままでした。
| 書き方 | 意味 |
|---|---|
modules={[Navigation, ...]} |
使うモジュールを渡す(npm の core と同じ) |
navigation・pagination |
true にすると矢印・点の要素を自動で作る |
onSwiper={(s) => ...} |
初期化されたインスタンスを受け取る |
onSlideChange・onReachEnd |
イベントは on + イベント名 |
useSwiper() |
Swiper の中に置いた部品からインスタンスを取る |
useSwiperSlide() |
スライドの中から isActive などを読む |
slot="container-start" 等 |
子要素を置く場所(container-start・container-end・wrapper-start・wrapper-end) |
Next.js(App Router)では 'use client' を付けた部品にする
14.3.0 の swiper/react のファイルには 'use client' の指示がありません。Next.js のドキュメントでは、状態やイベント、ブラウザの機能を使うサードパーティの部品を Server Component で直接使うとエラーになり、'use client' を付けた自分の部品で包むよう案内されています。
// app/ui/hero-slider.jsx
'use client';
import { Swiper, SwiperSlide } from 'swiper/react';
import { Pagination } from 'swiper/modules';
import 'swiper/css';
import 'swiper/css/pagination';
export default function HeroSlider({ slides }) {
return (
<Swiper modules={[Pagination]} pagination={{ clickable: true }}>
{slides.map((s) => <SwiperSlide key={s.id}>{s.title}</SwiperSlide>)}
</Swiper>
);
}
page.jsx(Server Component)からは <HeroSlider slides={...} /> として使い、データの取得はサーバー側で行えます。サーバーでの描画(React の renderToString)で swiper/react を出力すると、<div class="swiper"><div class="swiper-wrapper"><div class="swiper-slide">A</div>… のように、初期化前の HTML になりました。ブラウザで JavaScript が動くまでは各スライドが外枠の幅いっぱい(CSS の既定)なので、slidesPerView: 2 などで幅が変わる場合は、読み込み直後に一瞬幅が変わることがあります。App Router の基本は、関連記事「Next.js App Router 入門|v16対応・実際のビルド結果つき」で扱っています。
VueでSwiperを使う(Swiper Elementも)
この節では、Vue での書き方と、タグだけで書ける Swiper Element の使い方が分かります。
Vue(swiper/vue)
<script setup>
import { ref } from 'vue';
import { Swiper, SwiperSlide } from 'swiper/vue';
import { Navigation, Pagination } from 'swiper/modules';
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
const modules = [Navigation, Pagination];
const index = ref(0);
const items = ['A', 'B', 'C', 'D'];
const onSlideChange = (s) => { index.value = s.realIndex; };
</script>
<template>
<swiper
:modules="modules"
:slides-per-view="1"
:space-between="16"
:loop="true"
navigation
:pagination="{ clickable: true }"
:breakpoints="{ 768: { slidesPerView: 2 } }"
@slide-change="onSlideChange"
>
<swiper-slide v-for="t in items" :key="t">スライド{{ t }}</swiper-slide>
</swiper>
<p>表示中:{{ index + 1 }} / {{ items.length }}</p>
</template>
Vue 3.5.44 と Vite 8.3.5 でビルドすると JS は 161.05 kB(gzip 53.52 kB)で、Chromium では各スライドが 292px、点が4つ、矢印の「次へ」で「表示中:2 / 4」になりました。オプションはテンプレートではケバブケース(:slides-per-view)、イベントは @slide-change のように書きます。
Swiper Element(Web Components)
Swiper Element は、9 から入った <swiper-container> と <swiper-slide> のタグで書く形です。CDN の swiper-element-bundle.min.js を読み込むと自動で登録されます。
<script src="https://cdn.jsdelivr.net/npm/swiper@14/swiper-element-bundle.min.js"></script>
<swiper-container slides-per-view="2" space-between="10" pagination="true" navigation="true" loop="true">
<swiper-slide>S1</swiper-slide>
<swiper-slide>S2</swiper-slide>
<swiper-slide>S3</swiper-slide>
<swiper-slide>S4</swiper-slide>
</swiper-container>
Chromium で開くと、<swiper-container> の中に Shadow DOM が作られ、その中に swiper の外枠と4つの点ができました。イベントは swiperslidechange のように、小文字で頭に swiper を付けた名前の DOM イベントで受け取ります。breakpoints のように属性で書きにくい設定は、init="false" を付けて JavaScript で渡します。
<swiper-container id="b" init="false">…</swiper-container>
<script>
const el = document.getElementById('b');
Object.assign(el, { slidesPerView: 1, breakpoints: { 768: { slidesPerView: 3 } } });
el.initialize();
</script>
幅 1200px で開くと、slidesPerView は 3、currentBreakpoint は 768 でした。
swiper/react・swiper/vue
各フレームワークの部品として書く
イベントは onSlideChange・@slide-change
スライドの中身は普通の DOM で、CSS がそのまま効く
Swiper Element
フレームワーク無しでタグだけで書ける
イベントは swiperslidechange(addEventListener)
点や矢印は Shadow DOM の中にできる
Swiperが動かない・表示が崩れるときの原因と直し方
この節では、よくある「動かない」「崩れる」を、エラーの原文と実測の結果から探せます。
まず確かめること
- 1コンソールに赤いエラーが出ていないかDevTools の Console を開く。「Swiper is not defined」なら読み込みの順番
- 2CSS と JS の版がそろっているかCDN の URL の @14 や npm の版を、CSS と JS で同じにする
- 3swiper・swiper-wrapper・swiper-slide の入れ子になっているか以前の swiper-container のままだと、14 の CSS が当たらない
- 4npm の core にモジュールを渡しているかmodules に Navigation・Pagination 等を入れ、CSS も読み込む
- 5外枠に幅(縦向きなら高さ)があるか親が flex・grid なら min-width: 0 を足す
Console で typeof Swiper と打つと、bundle が読み込まれていれば "function" が返りました。"undefined" なら、JS が読み込まれていないか、読み込みより前です。ブラウザの Console の見方は、関連記事「Chrome DevToolsの使い方|デバッグ手順と便利機能一覧」で扱っています。
症状から原因を絞る流れです。
flowchart TD
A{エラーが<br>出るか} -->|出る| B[読み込み順<br>クラス名]
A -->|出ない| C{縦に並ぶ}
C -->|はい| D[CSS を<br>読み込む]
C -->|いいえ| E{点が無い}
E -->|はい| F[modules<br>に渡す]
E -->|いいえ| G[親に<br>min-width 0]
症状・エラーの原文と直し方(実測)
| 症状・エラー | 原因 | 直し方 |
|---|---|---|
Uncaught ReferenceError: Swiper is not defined |
Swiper の JS より前に new Swiper を書いた | 読み込みの後に初期化を書く |
Failed to resolve module specifier "swiper" |
ブラウザで import 'swiper' と書いた |
CDN の .mjs の URL か import map を使う |
| スライドが縦に積み重なって全部見える | CSS を読み込んでいない | bundle の CSS か swiper/css を読む |
| 点や矢印が出ない(エラーなし) | npm の core にモジュールを渡していない | modules: [Pagination] を足す |
| 何も起きない(エラーなし) | セレクターに合う要素が無い | クラス名と書いた場所を確かめる |
Failed to execute 'getComputedStyle' |
swiper-wrapper が無い | 3つの入れ子にする |
| 横にはみ出す・ページが横に伸びる | 以前の swiper-container のまま |
外枠のクラスを swiper にする |
| 幅が 33,554,432px になる | 親の flex・grid で縮まない | 親の子に min-width: 0 |
| 縦向きで高さが 33,554,432px | 縦向きで高さが無い | 外枠に height を書く |
| 進むほど位置がずれる | スライドに CSS の margin | spaceBetween で間をあける |
| 前のスライドの文字が重なる | fade の既定は前が残る | fadeEffect: { mode: 'cross-fade' } |
| ループしない・並びが乱れる | 枚数が足りない(警告あり) | 枚数を増やすか slidesPerView を下げる |
いくつかを詳しく見ます。
CSS が無いとき:幅 600px の外枠で JS だけを読み込むと、3枚のスライドの位置は上から 8px・26px・44px と縦に並び、.swiper の overflow は visible、wrapper の display は block でした。Swiper の CSS が .swiper-wrapper を横並び(flex)にし、.swiper ではみ出しを隠しています。
以前のクラス名 swiper-container:6 までは外枠が swiper-container でした。7 以降の CSS は .swiper に当たるので、new Swiper('.swiper-container') で初期化はされても(initialized は true)、overflow が visible のままで、ページの横幅が 3,560px まで伸びました。
同じセレクターで2つのスライダー:.swiper が2つあるページで new Swiper('.swiper', { pagination: { el: '.swiper-pagination' } }) と書くと、戻り値は2つのインスタンスの配列になり、点はそれぞれ 3個・5個と正しく作られました。矢印と点は、それぞれの .swiper の中に置けば同じクラス名でも混ざりません。 外に置くときは親のクラスで区別します。
親が flex・grid のとき幅が 33,554,432px になる
左にサイドバー(200px)、右にスライダーを置くレイアウトで、親の CSS を変えて測りました(画面の幅 1000px、slidesPerView: 2)。
| 親の CSS | スライダーの幅 | ページの横幅 |
|---|---|---|
display: flex と main { flex: 1 } |
33,554,432px | 33,554,432px |
上に main { min-width: 0 } を足す |
800px | 1000px |
grid-template-columns: 200px 1fr |
33,554,232px | 33,554,432px |
grid-template-columns: 200px minmax(0, 1fr) |
800px | 1000px |
flex の子と grid の 1fr は、既定では中身より狭くならない(最小の幅が中身の幅になる)ので、横に並んだスライドの幅に引っ張られて広がりました。 子に min-width: 0、grid なら minmax(0, 1fr) を書くと、800px に収まりました。
.layout { display: flex; }
.layout main { flex: 1; min-width: 0; } /* これが無いとスライダーが広がり続ける */
flex の子が縮まない仕組みは、関連記事「CSS flexboxの使い方早見表|横並び・中央寄せのコピペ集と効かない原因」で扱っています。
タブ・モーダルなど非表示の中で初期化したとき
display: none の中で初期化してから表示した結果です。
| 設定 | 表示した直後の幅 | 点の数 | 「次へ」 |
|---|---|---|---|
| 既定(resizeObserver: true) | 600px(自動で直る) | 4 | 動く |
resizeObserver: false |
幅なし | 1 | 動かない |
上に observer: true, observeParents: true |
600px | 4 | 動く |
14.3.0 の既定では、外枠の大きさの変化を ResizeObserver で見張っているので、表示された時点で自動で計算し直しました。 非表示の間は swiper.width が null で、点も1個でした。resizeObserver: false にしていると直らず、activeIndex は 1 に進んでも transform は 0 のままでした。この場合は、表示した直後に swiper.update() を呼ぶと、幅 600px・点4個に直りました。
tabButton.addEventListener('click', () => {
panel.hidden = false;
swiper.update(); // 表示した直後に計算し直す
});
jQuery と一緒に使うとき
Swiper は jQuery を使わないので、jQuery の後でも前でも読み込めます。$(function () { new Swiper('.swiper'); }) のように、DOM ができた後に初期化します。jQuery の読み込みと書き方は、関連記事「jQueryとは|使い方・Ajax・fetchとの対応表を4.0の実行結果で解説」で扱っています。
Swiperのアクセシビリティ(a11y・キーボード操作)
この節では、Swiper が自動で付ける読み上げ用の属性と、日本語にする方法、キーボード操作が分かります。
bundle には A11y モジュールが入っていて、既定で有効です(npm の core では modules に A11y を入れます)。初期化後の DOM には次の属性が付いていました。
| 付く場所 | 属性 | 既定の文言 |
|---|---|---|
| 各スライド | role="group"・aria-label |
1 / 3 |
| 前への矢印 | role="button"・aria-label |
Previous slide |
| 次への矢印 | role="button"・aria-label |
Next slide |
| 点(clickable のとき) | role="button"・aria-label |
Go to slide 1 |
| wrapper | aria-live |
polite(自動再生中は off) |
読み上げの文言は a11y で日本語にできます。
const swiper = new Swiper('.swiper', {
keyboard: { enabled: true },
pagination: { el: '.swiper-pagination', clickable: true },
navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev' },
a11y: {
prevSlideMessage: '前のスライド',
nextSlideMessage: '次のスライド',
paginationBulletMessage: '{{index}}枚目へ',
slideLabelMessage: '{{index}} / {{slidesLength}}枚目',
},
});
Chromium で確かめた結果です。
スライドの aria-label : 1 / 4枚目 | 2 / 4枚目 | 3 / 4枚目 | 4 / 4枚目
Tab キーの順番 : 1枚目へ → 2枚目へ → 3枚目へ → 4枚目へ → 次のスライド
次の矢印で Enter : activeIndex 0 → 1
→ キー(keyboard) : activeIndex 1 → 2
読み上げ用の要素 : <span class="swiper-notification" aria-live="assertive" aria-atomic="true">次のスライド</span>
1枚目では「前のスライド」が無効(tabindex="-1")なので、Tab キーでは飛ばされました。
動きを減らす設定の人には自動再生を止める
14.3.0 のソースに prefers-reduced-motion(OS の「視差効果を減らす」などの設定)を読む処理は無いので、自分で読み取って切り替えます。
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const swiper = new Swiper('.swiper', {
loop: true,
speed: reduce ? 0 : 300,
autoplay: reduce ? false : { delay: 3000 },
});
delay を 1000 にして、Chromium で動きを減らす設定を再現して開くと、2.5秒後も1枚目のまま(speed 0・自動再生なし)で、設定が無いときは3枚目まで進んでいました。
Swiperのバージョンの違いと移行(v6〜v14)
この節では、以前の版の書き方を見つけたときに、何を直せばよいかが分かります。
| 以前の書き方・状態 | いまの書き方 | 変わった版 |
|---|---|---|
外枠 swiper-container |
swiper |
7 |
import Swiper, { Navigation } from 'swiper' |
モジュールは swiper/modules から |
10 |
| ループでスライドを複製(duplicate) | 複製せず並べ替え | 9(11 で作り直し) |
loopedSlides |
loopAdditionalSlides |
11 |
Lazy モジュール(swiper-lazy・data-src) |
画像の loading="lazy" |
9 |
| 矢印はアイコンフォント | SVG のアイコンを自動で入れる | 12 |
| LESS・SCSS のスタイル | CSS だけ | 12 |
fadeEffect.crossFade |
fadeEffect.mode: 'cross-fade' |
14.2 で mode 追加 |
外枠のクラス名・複製・Lazy モジュール・swiper/modules が変わった版は、npm の 6.8.4・7.0.0・8.4.7・9.0.0・10.0.0 のパッケージを開いて、CSS のクラス名・swiper-slide-duplicate の有無・lazy のモジュールの有無・package.json の exports で確かめました。loopedSlides・アイコン・スタイル・fade の mode は公式の変更履歴で確かめました。
14.0.0 は書き方の変更が無く、12 からはそのまま上げられます。 変わったのは対応ブラウザ(Chrome・Edge 110 以降など)と、開発に Node.js 20.19.0 以降が要ることです。
よくある質問
Q. Swiperの最新バージョンは? A. 2026年10月23日時点で 14.3.0(2026年9月28日公開)です。13 は欠番で、12.2.0 の次が 14.0.0 です。12 からはコードを変えずに上げられると公式の変更履歴に書かれています。
Q. Swiperは商用利用できますか? A. できます。ライセンスは MIT で、パッケージの LICENSE ファイルに The MIT License と書かれています。配布するファイルに含まれる著作権表示は消さずに残します。
Q. SwiperにjQueryは必要ですか? A. 要りません。Swiper の CSS と JS だけで動きます。jQuery を使っているサイトにも、そのまま追加できます。
Q. CDNとnpm、どちらを使えばいいですか? A. HTML に直接書くサイトや WordPress は CDN(bundle)、Vite・React・Vue で作るなら npm です。npm で使うモジュールだけを読み込むと、bundle の約半分(gzip 後 24KB 対 46KB)になりました。
Q. Swiperのループで最初に戻るとき、一瞬止まる・ずれるのは?
A. 多い原因は枚数不足です。Console に「Swiper Loop Warning: The number of slides is not enough for loop mode」が出ていないかを見て、枚数を増やすか slidesPerView を下げます。
Q. 自動再生を止める・止めないの設定は?
A. 触ったら止めるなら autoplay: { disableOnInteraction: true } を明記します。14.3.0 では書かないと止まりませんでした。ボタンで止めるなら swiper.autoplay.stop()、再開は start() です。
Q. スマホだけ1枚、PCでは3枚にするには?
A. 基本を slidesPerView: 1 にして、breakpoints: { 1024: { slidesPerView: 3 } } と書きます。キーの数値は「その幅以上」で効きます。
Q. Swiperが表示されない・動かないのはなぜ? A. 読み込みの順番(Swiper is not defined)、CSS の読み忘れ、npm の core にモジュールを渡していない、親が flex・grid で幅が広がり続ける、の4つが多い原因です。「動かない・表示が崩れるとき」の表で症状から探せます。
Q. WordPressでSwiperを使うには?
A. テーマの functions.php で wp_enqueue_style と wp_enqueue_script を使い、Swiper の CSS と JS、初期化を書いた自分の JS を読み込みます。自分の JS の依存($deps)に Swiper のハンドル名を入れると、読み込みの順番が守られます。WordPress 6.3 以降は $args で 'strategy' => 'defer' も指定できます。
Q. Next.jsでSwiperがエラーになるのは?
A. App Router の Server Component で swiper/react を直接使うと、クライアントの機能を使う部品なのでエラーになります。先頭に 'use client' を書いた自分の部品で包み、その部品を page から使います。
Q. 矢印の色や大きさを変えるには?
A. CSS 変数で変えます。.swiper { --swiper-theme-color: #e60033; --swiper-navigation-size: 24px; } で、矢印と点の色が rgb(230, 0, 51)、矢印の高さが 24px になりました。
まとめ
- Swiper は CSS と JS を読み込み、
swiper・swiper-wrapper・swiper-slideの入れ子にnew Swiper()を書くと動く。最新は 14.3.0(13 は欠番)。 - CDN は版を固定する(
swiper@14かswiper@14.3.0)。npm の core はmodulesにモジュールを渡す。 - ループは 11 以降、複製せずに DOM を並べ替える。何枚目かは
realIndexで読む。枚数が足りないと警告が出る。 - 自動再生の間隔は
delay+speed。14.3.0 ではdisableOnInteractionを書かないと触っても止まらない。 - 間は CSS の margin でなく
spaceBetween。画面幅ごとの設定はbreakpoints(その幅以上で効く)。 - 幅が 33,554,432px になったら、親の flex・grid の子に
min-width: 0。縦向きは高さを書く。 - React は
swiper/react、Next.js の App Router では'use client'の部品で包む。Vue はswiper/vue、タグで書くなら Swiper Element。 - 自動再生には停止の手段を付け、
a11yの文言を日本語にする。
参考・出典
確認日はいずれも 2026年10月23日です。
- Swiper「Getting Started With Swiper」:https://swiperjs.com/get-started
- Swiper「Swiper API」:https://swiperjs.com/swiper-api
- Swiper「Swiper React Components」:https://swiperjs.com/react
- Swiper「Swiper Vue.js Components」:https://swiperjs.com/vue
- Swiper「Swiper Element (WebComponent)」:https://swiperjs.com/element
- Swiper「Changelog」:https://swiperjs.com/changelog
- npm「swiper」:https://www.npmjs.com/package/swiper
- GitHub「nolimits4web/swiper」:https://github.com/nolimits4web/swiper
- GitHub「jsdelivr/jsdelivr(README)」:https://github.com/jsdelivr/jsdelivr
- Next.js「Server and Client Components」:https://nextjs.org/docs/app/getting-started/server-and-client-components
- W3C「Understanding Success Criterion 2.2.2: Pause, Stop, Hide」:https://www.w3.org/WAI/WCAG22/Understanding/pause-stop-hide.html
- WordPress Developer Resources「wp_enqueue_script()」:https://developer.wordpress.org/reference/functions/wp_enqueue_script/
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Swiperの使い方|オプション早見表・React/Vue・動かない原因を実演」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。