【Next.js】Lottie アニメーションの使い方を lottie-react の再生制御まで解説

公開日: 2026年10月8日 · 読了目安 8 分

Next.js で Lottie アニメーションを表示する手順です。lottie-react の導入、JSON の読み込み方(import と public)、動的インポートで最初の読み込みを軽くする方法、画面に入ったときだけ再生する制御、動きを減らす設定とアクセシビリティまで、コード付きで解説します。

デザイナーから届いた Lottie アニメーションを Next.js で動かす

デザイナーから、ヒーローに置くアニメーションが JSON ファイルで届いた。でも Next.js にどう載せればいいのか分からず、手が止まっている。

この JSON は、Lottie(ロッティー)という形式のアニメーションです。React では lottie-react というライブラリで表示できます。

答えは次のとおりです。JSON を public フォルダに置き、lottie-react の Lottie 部品にパスを渡せば動きます。

そのうえで読み込みを後回しにし、画面に入ったときだけ再生すると、ページを軽く保てます。

この記事で分かることは次のとおりです。

  • lottie-react の入れ方と、いちばん短い表示のコード

  • JSON を import する方法と、public から読む方法の違い

  • 動的インポートで、最初に読み込む JavaScript を減らす方法

  • 画面に入ったら再生し、外れたら止める制御

  • 動きを減らす設定への対応と、読み上げソフトへの配慮

コードは Next.js 15 の App Router と、lottie-react 3 を前提にしています。

Lottie アニメーションを使う理由

Lottie は、After Effects などで作った動きを JSON に書き出したアニメーション形式です。After Effects からは、Bodymovin というプラグインで書き出すのが一般的です。

GIF や動画と比べた利点は、主に次の3つです。

  • 図形を数値で持つので、拡大しても線がにじまない

  • 再生・停止・特定のコマへの移動を、コードから操作できる

  • 線や図形が中心の動きなら、同じ見た目の動画よりファイルが小さくなりやすい

反対に、写真や実写の映像には向きません。その場合は動画のほうが軽く済みます。

動画を背景にしたヒーローの作り方は、https://design-layer.com/ja/articles/nextjs-video-hero-guide で解説しています。

LottieFiles などのサイトでは、公開されている JSON を探せます。使う前に、それぞれのライセンスを確認してください。

Next.js で Lottie を表示する3つのステップ

手数の少ない順に、3段階で進めます。

  • Step1: lottie-react を入れて、JSON を表示する

  • Step2: 動的インポートで、読み込みをページの表示より後にする

  • Step3: 画面の出入りに合わせて、再生と一時停止を切り替える

小さなアイコンの動きなら、Step1 だけで十分です。ヒーローに置く大きなアニメーションは、Step3 まで入れてください。

Step1:lottie-react で Lottie アニメーションを表示する

まず、npm i lottie-react でライブラリを入れます。描画を担当する lottie-web も、一緒に入ります。

届いた JSON は public/lottie/plant.json に置きます。public のファイルは、/lottie/plant.json という URL で読めます。

表示する部品は、ブラウザで動かす印("use client")を付けた別のファイルに分けます。あとで再生を操作するときも、このファイルだけを直せば済みます。

components/LottiePlayer.tsx
"use client";

import { Lottie } from "lottie-react";

type Props = {
  src: string | object;
  className?: string;
};

export default function LottiePlayer({ src, className }: Props) {
  return (
    <Lottie
      src={src}
      autoplay
      loop
      aria-hidden="true"
      className={className}
    />
  );
}

Lottie 部品の大きさは className で決める

Lottie の部品は、置かれた箱いっぱいに描かれます。高さのない箱では、何も見えません。

aspect-square や h-64 のように、className で大きさを決めてください。縦横比を決めておくと、表示の前後でレイアウトがずれません。

JSON を import する方法と public から読む方法

JSON は、ファイルとして import することもできます。この場合は、アニメーションのデータが JavaScript の中に入ります。

  • import:読み込み待ちがなく、すぐに描ける。そのかわり JavaScript が JSON の分だけ重くなる

  • public のパス:JavaScript は軽いまま。表示の直前に、JSON を別に取りに行く

数 KB の小さなアイコンなら import、数十 KB を超えるヒーロー用なら public が目安です。

components/PlantIcon.tsx
"use client";

import LottiePlayer from "./LottiePlayer";
import plant from "@/animations/plant.json";

export function PlantIcon() {
  return <LottiePlayer src={plant} className="size-12" />;
}

Step2:Lottie を動的インポートして最初の表示を軽くする

lottie-react の描画エンジン(lottie-web 5.13 の全機能版)は、圧縮前で約 300KB あります。そのまま読み込むと、ページの最初の JavaScript がその分だけ増えます。

Next.js の dynamic を使うと、この部品だけを別のファイルに分けて後から読めます。ssr: false を付けると、サーバーでは描画せずにブラウザでだけ読み込みます。

古いバージョンの組み合わせでは、サーバーでの描画中に「document is not defined」というエラーが出ることがありました。ブラウザでだけ読み込めば、この心配もなくなります。

components/LazyLottie.tsx
"use client";

import dynamic from "next/dynamic";

const LottiePlayer = dynamic(() => import("./LottiePlayer"), {
  ssr: false,
});

type Props = {
  src: string;
};

export function LazyLottie({ src }: Props) {
  return (
    <div className="mx-auto aspect-square w-full max-w-sm">
      <LottiePlayer src={src} className="h-full w-full" />
    </div>
  );
}

ssr: false はブラウザで動かすファイルの中に書く

Next.js 15 では、サーバーで組み立てる部品(サーバーコンポーネント)の中で ssr: false を使えません。ビルドのときにエラーになります。

そのため、上のように印を付けた LazyLottie の中で dynamic を呼びます。ページの側は、この部品を普通に置くだけです。

app/page.tsx
import { LazyLottie } from "@/components/LazyLottie";

export default function Page() {
  return (
    <section className="mx-auto grid max-w-5xl items-center gap-10 px-6 py-24 md:grid-cols-2">
      <div>
        <h1 className="text-4xl font-semibold tracking-tight">
          植物のように育つ、チームのナレッジ
        </h1>
        <p className="mt-4 text-zinc-600">
          書いたメモが自動でつながり、必要なときに見つかります。
        </p>
      </div>
      <LazyLottie src="/lottie/plant.json" />
    </section>
  );
}

Lottie を読み込む前に箱の大きさを確保する

動的インポートでは、部品が届くまで何も描かれません。そこで外側の div に縦横比を付け、場所を先に確保しています。

これがないと、アニメーションが現れた瞬間に、下の要素が押し下げられます。このずれは、表示の安定性を測る指標(CLS)を悪くします。

Step3:Lottie の再生を画面に入ったときだけにする

画面の外でアニメーションが回り続けると、見えない動きのために処理が使われます。スマホでは、電池の減りにもつながります。

lottie-react 3 には、画面に入ったら再生し、出たら一時停止する lottieInView という仕組みがあります。LottieInteractions で包み、autoplay は false にします。

Step1 の LottiePlayer を、次のように書き換えます。LazyLottie の Props にも label と stillFrame を足して、そのまま渡してください。

components/LottiePlayer.tsx
"use client";

import { useRef } from "react";
import {
  Lottie,
  LottieInteractions,
  lottieInView,
  type LottieHandle,
} from "lottie-react";
import { useReducedMotion } from "motion/react";

type Props = {
  src: string | object;
  className?: string;
  /** 意味のあるアニメーションなら説明文。飾りなら省略する */
  label?: string;
  /** 動きを減らす設定のときに、止めて見せるコマ */
  stillFrame?: number;
};

export default function LottiePlayer({ src, className, label, stillFrame = 0 }: Props) {
  const lottieRef = useRef<LottieHandle>(null);
  const reduceMotion = useReducedMotion();

  return (
    <LottieInteractions interactions={reduceMotion ? [] : [lottieInView({ amount: 0.3 })]}>
      <Lottie
        src={src}
        lottieRef={lottieRef}
        autoplay={false}
        loop
        className={className}
        role={label ? "img" : undefined}
        aria-label={label}
        aria-hidden={label ? undefined : true}
        subscriptions={{
          ready: () => {
            if (reduceMotion) lottieRef.current?.seek(stillFrame);
          },
        }}
      />
    </LottieInteractions>
  );
}

コードのポイント

  • lottieInView は、画面から出ると一時停止する。戻ったときは、途中から続きを再生する

  • amount: 0.3 は、3割が見えたら「画面に入った」とみなす指定

  • 動きを減らす設定の人には再生せず、stillFrame で決めたコマを止めて見せる

  • label を渡したときだけ読み上げの対象にし、渡さないときは aria-hidden で隠す

ブラウザのタブを切り替えている間は、ブラウザ側が描画の更新を止めます。そのため、タブの切り替えに合わせた処理は通常いりません。

lottieRef で再生と一時停止のボタンを付ける

5秒を超えて自動で動き続けるアニメーションには、止める手段を用意します。アクセシビリティの指針である WCAG の 2.2.2 が求めている対応です。

lottieRef には、play や pause などの命令が入っています。これをボタンにつなぐと、読者が自分で止められます。

ボタンは 44px 四方にして、スマホでも押しやすくしています。

components/LottieWithToggle.tsx
"use client";

import { useRef, useState } from "react";
import { Lottie, type LottieHandle } from "lottie-react";

export function LottieWithToggle({ src }: { src: string }) {
  const lottieRef = useRef<LottieHandle>(null);
  const [playing, setPlaying] = useState(false);

  return (
    <div className="relative mx-auto aspect-square w-full max-w-sm">
      <Lottie
        src={src}
        lottieRef={lottieRef}
        autoplay
        loop
        aria-hidden="true"
        className="h-full w-full"
        subscriptions={{
          play: () => setPlaying(true),
          pause: () => setPlaying(false),
          stop: () => setPlaying(false),
        }}
      />
      <button
        type="button"
        aria-label={playing ? "アニメーションを一時停止" : "アニメーションを再生"}
        onClick={() => (playing ? lottieRef.current?.pause() : lottieRef.current?.play())}
        className="absolute bottom-3 right-3 grid size-11 place-items-center rounded-full bg-white/90 text-zinc-900 shadow focus-visible:outline focus-visible:outline-2 focus-visible:outline-zinc-900"
      >
        <span aria-hidden="true">{playing ? "❚❚" : "▶"}</span>
      </button>
    </div>
  );
}

Lottie アニメーションの動きを減らす設定とアクセシビリティ

OS で「視差効果を減らす」などの設定にしている人は、画面の動きで気分が悪くなることがあります。ブラウザには、この設定が prefers-reduced-motion として伝わります。

lottie-react 3 は、この設定のときに autoplay の再生を始めません。最初のコマで止まった状態になります。

ただし、最初のコマが空白から始まるアニメーションもあります。その場合は Step3 の stillFrame で、完成形に近いコマを指定してください。

飾りのアニメーションは読み上げから外す

背景の植物や、見出しを飾るだけの動きは、それ自体に意味がありません。aria-hidden を付けて、読み上げソフトから隠します。

伝えたい内容は、アニメーションではなく見出しや本文のテキストに書いておきます。

意味のあるアニメーションには説明を付ける

送信の完了を示すチェックマークのように、動きで情報を伝える場合もあります。そのときは role="img" と aria-label で、内容を言葉にします。

完了のような状態の変化は、画面上のテキストでも伝えておくと確実です。

Lottie アニメーションの公開前チェックと軽くするコツ

JSON の作り方によって、ファイルは数 KB から数 MB まで差が出ます。公開の前に、サイズと動きの両方を確認してください。

Lottie の JSON ファイルを軽くする

  • 写真を埋め込まない。画像が文字列として JSON に入ると、一気に重くなる

  • 使っていないレイヤーや、見えない図形を書き出す前に消す

  • 書き出す範囲を、実際に使う長さだけにする

  • サーバーで gzip などの圧縮がかかっているかを確かめる

After Effects の式(エクスプレッション)を使っていないなら、軽量版の LottieLight も選べます。描画エンジンは約 170KB で、全機能版の半分強です。

拡張子が .lottie のファイルは zip 形式で、lottie-react 3 ではそのまま読めません。JSON 形式で書き出してもらってください。

よくある失敗と直し方

  • 箱に高さがなく、何も表示されない。className で大きさを決める

  • ページ全体のファイルに、ブラウザで動かす印を書く。Lottie を表示する部品だけに分ける

  • 画面の外でも回し続ける。Step3 の lottieInView で止める

  • 見出しや画像を、アニメーションが終わるまで隠す。文章は最初から見える状態にする

  • 大きなアニメーションを、1ページにいくつも並べる。主役は1つに絞る

4つ目は、表示速度の指標である LCP を遅らせます。ヒーローの動きの付け方は、https://design-layer.com/ja/articles/nextjs-animated-hero-section-guide で解説しています。

完成した Lottie 入りのヒーローをコピーして使う方法

DesignLayer の「グリーン・スクロールヒーロー」の詳細ページ。左でライブプレビューを確かめ、右の「Cursor 用にコピー」でソースをコピーできる
DesignLayer の「グリーン・スクロールヒーロー」の詳細ページ。左でライブプレビューを確かめ、右の「Cursor 用にコピー」でソースをコピーできる

スクロールに合わせて、植物のアニメーションの後ろから見出しが現れる。こうした演出は、再生の制御とスクロールの計算を組み合わせる必要があります。

自分で組むと、細かな調整に時間がかかります。その場合は、完成したセクションを動かして確かめてから使う方法もあります。

DesignLayer のカタログにある「グリーン・スクロールヒーロー」は、画面に固定したままのスクロールで見出しが現れるヒーローです。中央の草木には lottie-react を使っています。

詳細ページで「Cursor 用にコピー」を押すと、組み込みの指示・ライブプレビューの URL・本体の TSX・必要な npm パッケージがまとめてコピーされます。Cursor や Claude に貼って、自分の Next.js に載せます。

閲覧とライブプレビューは無料で、アカウントもカードも要りません。無料のソースコピーは、日本時間で1日3回までです。https://design-layer.com/ja/components

載せたあとは、見出し・ロゴ・アニメーションの JSON を差し替えます。環境やテック系の LP の先頭に向いています。

Lottie アニメーションについてよくある質問

  • lottie-react を npm で入れれば使えます。JSON を public に置き、Lottie 部品の src にパスを渡します。

    表示する部品のファイルには、ブラウザで動かす印("use client")を付けます。

まとめ:Next.js で Lottie アニメーションを使う手順

  • Lottie は、After Effects などの動きを JSON にしたアニメーション形式

  • lottie-react を入れ、JSON を public に置いて src にパスを渡す

  • dynamic と ssr: false で読み込みを後回しにし、箱の大きさは先に決める

  • lottieInView で、画面に入ったときだけ再生する

  • 動きを減らす設定では止めたコマを見せ、飾りは aria-hidden にする

まずは Step1 のコードに、手元の JSON を1つ入れて表示してみてください。

スクロールに合わせた動き全般は https://design-layer.com/ja/articles/nextjs-scroll-reveal-guide で解説しています。ページを開いたときの演出は https://design-layer.com/ja/articles/nextjs-loading-screen-guide をご覧ください。

【Next.js】Lottie アニメーションの使い方を lottie-react の再生制御まで解説 | DesignLayer