【Next.js】ポートフォリオにモーションを付ける方法を解説
公開日: 2026年9月12日 · 読了目安 13 分
Next.js で作るポートフォリオサイトに、作品を引き立てるモーションを付ける方法をコード付きで解説。大きな1件+グリッド、スクロールで作品が切り替わるギャラリー、ケーススタディの入場、モーションデザイナー向けの動画作品の載せ方まで。
ポートフォリオのモーションは作品を見る順番に使う
Next.js でポートフォリオサイトを作っていて、制作実績のページが「ただのサムネイル一覧」に見える。動きを足したいけれど、何を動かせばよいか分からない。
そんなときは、作品そのものではなく「作品を見る順番」を動きで作ってください。
パーティクルの背景やカーソル追従のような演出が先に目に入ると、案件の写真が負けます。ポートフォリオで効くのは、スクロールで作品が切り替わるギャラリーと、ケーススタディの控えめな入場です。
この記事で分かることは次のとおりです。
制作実績が弱く見える理由
大きな1件と静的なグリッドで、見せたい作品を決める方法
スクロールに合わせて作品が切り替わる、ピン留めギャラリーの作り方
ケーススタディのページに付ける、弱い入場アニメーション
モーションデザイナーが動画作品を載せるときの注意
ポートフォリオの制作実績が弱く見える3つの理由
1つ目は、同じ大きさのサムネイルが並んでいるだけなことです。どの作品も同じ扱いだと、見る人は一番の自信作がどれか分かりません。
1件だけ大きくするだけで、ページに入口ができます。
2つ目は、自動で流れるカルーセルに作品を入れていることです。依頼を考えている人は、気になった作品で止まりたいのに、勝手に次へ進みます。
作品の切り替えは、見る人の操作(スクロールやクリック)に任せます。
3つ目は、演出がケーススタディの本文より長いことです。ページを開いてから課題や役割が読めるまでに何秒も待たされると、採用担当や依頼主は離れます。
動きは、読み始めを邪魔しない長さに抑えます。
方法1:ポートフォリオを大きな1件+グリッドで見せる
一番見せたい案件を大きく置き、残りは静的なグリッドに並べます。動きは、マウスを乗せたときに画像がわずかに拡大するだけです。
サーバーコンポーネント(サーバーで組み立てる部品)のまま作れるので、"use client"(ブラウザで動かす印)は要りません。
画像は next/image で読み込み、sizes を実際の表示幅に合わせます。大きな1件だけ priority を付けると、最初の表示が速くなります。
import Image from "next/image";
import Link from "next/link";
type Work = { slug: string; title: string; role: string; image: string; alt: string };
export function WorkGrid({ works }: { works: Work[] }) {
const [featured, ...rest] = works;
return (
<section className="mx-auto max-w-6xl px-6 py-24">
<h2 className="text-3xl font-semibold">制作実績</h2>
<div className="mt-10 grid gap-6 md:grid-cols-3">
<Link href={"/works/" + featured.slug} className="group md:col-span-2 md:row-span-2">
<div className="relative aspect-[4/3] overflow-hidden rounded-2xl bg-zinc-100">
<Image
src={featured.image}
alt={featured.alt}
fill
priority
sizes="(min-width: 768px) 66vw, 100vw"
className="object-cover transition-transform duration-500 ease-out group-hover:scale-[1.03] motion-reduce:transition-none"
/>
</div>
<p className="mt-3 font-medium">{featured.title}</p>
<p className="text-sm text-zinc-500">{featured.role}</p>
</Link>
{rest.map((work) => (
<Link key={work.slug} href={"/works/" + work.slug} className="group">
<div className="relative aspect-[4/3] overflow-hidden rounded-2xl bg-zinc-100">
<Image
src={work.image}
alt={work.alt}
fill
sizes="(min-width: 768px) 33vw, 100vw"
className="object-cover transition-transform duration-500 ease-out group-hover:scale-[1.03] motion-reduce:transition-none"
/>
</div>
<p className="mt-3 font-medium">{work.title}</p>
<p className="text-sm text-zinc-500">{work.role}</p>
</Link>
))}
</div>
</section>
);
}作品カードのホバーは画像だけを拡大する
カードごと膨らませると、隣の作品と重なって見えたり、文字までぼやけたりします。
overflow-hidden の枠の中で画像だけを 1.03 倍程度に拡大すると、落ち着いた反応になります。
並べ方の工夫は https://design-layer.com/ja/articles/nextjs-bento-grid-guide 、ホバーの調整は https://design-layer.com/ja/articles/nextjs-hover-card-guide で詳しく解説しています。
方法2:ポートフォリオのピン留めギャラリー
セクションを画面に固定し、スクロールした量に合わせて「今見せている作品」を切り替えます。作品が1枚ずつ主役になるので、案件の数が少なくても見応えが出ます。
枚数は4〜8件が扱いやすい範囲です。
スクロールの進み具合は、Motion(旧 Framer Motion)の2つの仕組みで扱います。
useScroll:スクロールがどこまで進んだかを読み取る
useMotionValueEvent:読み取った値を、今見せる作品の番号に変える
外側のセクションの高さは「作品数 × 画面の高さ」にします。内側を sticky(スクロールしても止まって見える配置)で固定するのが基本の形です。
"use client";
import { useRef, useState } from "react";
import Image from "next/image";
import { motion, useMotionValueEvent, useScroll } from "motion/react";
const works = [
{ slug: "brand-site", title: "ブランドサイト制作", image: "/works/brand.jpg", alt: "ブランドサイトのトップページ" },
{ slug: "app-ui", title: "アプリの UI デザイン", image: "/works/app.jpg", alt: "スマホアプリの画面3枚" },
{ slug: "motion-reel", title: "モーションリール 2026", image: "/works/reel.jpg", alt: "モーションリールの1場面" },
{ slug: "ec-renewal", title: "EC サイトのリニューアル", image: "/works/ec.jpg", alt: "EC サイトの商品一覧ページ" },
];
export function PinnedGallery() {
const ref = useRef<HTMLElement>(null);
const [active, setActive] = useState(0);
const { scrollYProgress } = useScroll({ target: ref, offset: ["start start", "end end"] });
useMotionValueEvent(scrollYProgress, "change", (value) => {
setActive(Math.min(works.length - 1, Math.floor(value * works.length)));
});
return (
<section ref={ref} className="relative" style={{ height: works.length * 100 + "vh" }}>
<div className="sticky top-0 flex h-screen flex-col items-center justify-center px-6">
<div className="relative aspect-[4/3] w-full max-w-4xl overflow-hidden rounded-2xl bg-zinc-100">
{works.map((work, i) => (
<motion.div
key={work.slug}
className="absolute inset-0"
initial={false}
animate={{ opacity: i === active ? 1 : 0 }}
transition={{ duration: 0.4, ease: "easeOut" }}
>
<Image src={work.image} alt={work.alt} fill sizes="(min-width: 1024px) 896px, 100vw" className="object-cover" />
</motion.div>
))}
</div>
<p className="mt-6 text-lg font-medium">{works[active].title}</p>
<p className="text-sm text-zinc-500">
{active + 1} / {works.length}
</p>
</div>
</section>
);
}ギャラリーが画面に固定されないときの確認点
親の要素に overflow: hidden や overflow: auto が付いていると、ギャラリーは固定されません。
レイアウト全体を包む要素に overflow-hidden を付けていないか、最初に確認します。
スマホでは、画面の高さがアドレスバーの出入りで変わります。h-screen の代わりに h-dvh を使うと、ずれが出にくくなります。
ギャラリーの作り方全体は https://design-layer.com/ja/articles/nextjs-scroll-gallery-guide で、スマホ対応も含めて解説しています。
ポートフォリオのケーススタディは弱い入場だけにする
作品の詳細ページは、課題と役割、工夫したことと結果を読んでもらう場所です。ここでの動きは、段落や画像が画面に入ったときに少し浮き上がる程度で十分です。
使うのは、Motion の whileInView(画面に入ったら動かす指定)です。
1回だけ動かすには、viewport に once: true を指定します。
動きを減らす設定の人には、useReducedMotion を使って最初から表示します。
"use client";
import type { ReactNode } from "react";
import { motion, useReducedMotion } from "motion/react";
export function FadeIn({ children }: { children: ReactNode }) {
const reduce = useReducedMotion();
return (
<motion.div
initial={reduce ? false : { opacity: 0, y: 12 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-10% 0px" }}
transition={{ duration: 0.5, ease: [0.22, 1, 0.36, 1] }}
>
{children}
</motion.div>
);
}ケーススタディの見出しと最初の画像は動かさない
ページを開いて最初に見える見出しとメインの画像には、入場を付けません。
最初の数秒を待たせず、すぐに作品が見える方が、依頼主にも採用担当にも伝わります。FadeIn で包むのは、スクロールした先の段落と補足の画像だけにします。
モーションデザイナーのポートフォリオに動画を載せる
モーションデザイナーや映像制作者のポートフォリオでは、作品そのものが動いています。
サイト側の演出は、さらに控えめにしてください。サイトの動きが作品の動きと競うと、どちらも伝わりにくくなります。
動画をたくさん並べるときは、全部を同時に再生しないことが大事です。画面に入った動画だけを再生し、外れたら止めます。
muted と playsInline を付けると、スマホでも多くの場合は自動で再生できます。
省電力モードなどで止められることもあるので、poster の静止画だけでも作品が伝わるようにしておきます。
poster に静止画を指定し、preload は metadata に留めると、ページの読み込みが重くなりません。
"use client";
import { useEffect, useRef } from "react";
export function WorkVideo({ src, poster, label }: { src: string; poster: string; label: string }) {
const ref = useRef<HTMLVideoElement>(null);
useEffect(() => {
const video = ref.current;
if (!video) return;
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
video.play().catch(() => {});
} else {
video.pause();
}
},
{ threshold: 0.5 }
);
observer.observe(video);
return () => observer.disconnect();
}, []);
return (
<video
ref={ref}
src={src}
poster={poster}
muted
loop
playsInline
preload="metadata"
aria-label={label}
className="aspect-video w-full rounded-2xl bg-zinc-900 object-cover"
/>
);
}長い動画作品は短いループと本編に分ける
一覧には 5〜10 秒程度の短いループを置き、本編はケーススタディのページで再生ボタン付きで見せます。一覧に長い動画を並べると、通信量が増え、スマホでの表示が遅くなります。
ポートフォリオのモーションでよくある失敗
カーソル追従やパーティクルを全ページに置き、作品より演出が目立つ
自動カルーセルで作品が流れ、見たい作品で止められない
画像の sizes を指定せず、スマホでも大きな画像を読み込んで重くなる
作品画像の alt が空、またはファイル名のままになっている
クライアントワークの画像を、掲載の許可を確認せずに載せている
最後の2つは、動きとは関係ありませんが、公開前に必ず確認してください。alt には「何の案件の、どの画面か」を短く書きます。
完成したポートフォリオ用ギャラリーを使う方法

ピン留めと切り替えのタイミングを自分で調整すると、スマホでの確認まで含めて半日かかることがあります。作品の準備に時間を使いたいなら、完成したギャラリーを土台にする方法もあります。
DesignLayer は、人が組んだ React + Tailwind CSS のモーション付きセクションを、ブラウザでライブプレビューしてからコピーできるカタログです。
ギャラリーを実際にスクロールして動きを確かめ、画像とタイトルだけを自分の作品に差し替えます。
閲覧にアカウントもカードも要りません。https://design-layer.com/ja/components
無料アカウントでは、無料サンプル10個を無料アカウントで回数制限なくコピーでき、動画プロンプトは月1回です。Free では MCP に接続できません。カタログ全体は30日ビルドパス($19の1回払い、自動更新なし)、Lifetime Access(先着100名の$79の1回払い)、Plus(月額 $9、いつでも解約可)です。無料サンプルなら回数の上限なくコピーできます。それ以外は30日ビルドパス($19)、Lifetime($79)、Plus(月額 $9)です。
ポートフォリオのモーションについてよくある質問
大きな1件+グリッドだけなら不要です。Tailwind CSS のホバーで足ります。
スクロールで作品が切り替わるギャラリーや、画面に入ったときの入場を付けるときに使います。
導入は https://design-layer.com/ja/articles/nextjs-framer-motion-guide を参考にしてください。
まとめ:Next.js のポートフォリオにモーションを付ける手順
ポートフォリオのモーションは、作品を見る順番を作るために使う
一番の作品を大きく置き、残りは静的なグリッドにする
作品が少ないなら、スクロールで切り替わるピン留めギャラリーが効く
ケーススタディは弱い入場だけにし、最初の見出しと画像は動かさない
動画作品は、画面に入ったものだけを muted で再生する
まずは制作実績のページで、一番見せたい1件を大きくするところから始めてください。それだけで、どこから見ればよいかが伝わるページになります。


