【Next.js】テキストを順番に表示するアニメーションの作り方を解説
公開日: 2026年9月12日 · 読了目安 10 分
Next.js で見出しのテキストを行や単語ごとに順番に表示する reveal アニメーションの作り方。CSS の animation-delay、Framer Motion での単語分割、行のマスク表示、日本語の区切り方、SEO と読み上げへの配慮までコード付きで解説します。
テキストアニメーションは行か単語で区切って出す
Next.js で作った LP のヒーローで、キャッチコピーを順番にふわっと出したい。参考サイトのように1文字ずつ出してみたら読みにくく、スマホで折り返しも崩れた。
この記事は、そんな状況から始める人に向けています。
テキストが順に出るアニメーション(テキスト reveal)は、行か単語までで区切るのが答えです。
1文字ずつ出すと、コピーが読み終わるまで時間がかかります。読み上げソフトにも不親切です。
LP のヒーローなら、ラベル → 見出し → 本文の3段で順番に出すだけで十分に動きが伝わります。
この記事で分かること:
CSS の遅延指定(animation-delay)だけで、ブロックごとに順番に出す方法
Motion(旧 Framer Motion)で単語・文節ごとに出すコンポーネント
行を下からマスクで出す、ヒーロー向けの見せ方
日本語の見出しをどこで区切るか
SEO・読み上げ・動きを減らす設定への配慮
1文字ずつのテキストアニメーションが読みにくい理由
文字単位の演出は、映像のオープニングのように「見せる」ためのものです。LP の見出しは「読ませる」ためにあるので、相性がよくありません。
具体的には、次の3つの問題が起きます。
見出しが揃うまで内容が分からず、その前にスクロールされてしまう
1文字ずつ絶対配置したり幅を固定したりすると、画面幅を変えたときに折り返しが崩れる
読み上げソフトが1文字ずつ区切って読むことがあり、意味が通らなくなる
行や単語で区切れば、どれも起きにくくなります。全体の時間も短く済みます。
目安として、ファーストビューのテキストは1秒以内に全部出し終えるようにします。
CSS だけでテキストをブロックごとに順番に出す
最初に試したいのは、単語に分けずにブロックごとに遅らせる方法です。
ラベル・h1・本文・ボタンの4つを、0.12 秒ずつずらして下から出します。多くの LP は、これだけで十分に動きのある見た目になります。
JavaScript を使わないので、サーバーコンポーネントのままで動きます。まず globals.css に keyframes を1つ足します。
@keyframes reveal-up {
from {
opacity: 0;
transform: translateY(12px);
}
to {
opacity: 1;
transform: translateY(0);
}
}Tailwind CSS の任意値クラスで遅延をずらす
animate-[reveal-up_0.6s_ease-out_0.12s_both] は、CSS の動きの指定を1つのクラスにまとめたものです。中身は次のとおりです。
reveal-up:さきほど足した動きの名前
0.6s:動く時間
ease-out:最後をゆっくり止める動き方
0.12s:動き始めるまでの遅れ
both:動く前と後の見た目を保つ指定
最後の both があると、遅延の間も透明のまま待ちます。終わった後は、表示されたまま残ります。
export function HeroText() {
return (
<div className="mx-auto max-w-3xl px-6 pt-32 text-center">
<p className="animate-[reveal-up_0.6s_ease-out_both] text-sm font-medium text-indigo-600 motion-reduce:animate-none">
春の新機能を追加しました
</p>
<h1 className="mt-4 animate-[reveal-up_0.6s_ease-out_0.12s_both] text-4xl font-semibold tracking-tight motion-reduce:animate-none md:text-6xl">
動きのある LP を、今日のうちに公開する
</h1>
<p className="mt-6 animate-[reveal-up_0.6s_ease-out_0.24s_both] text-lg text-zinc-600 motion-reduce:animate-none">
セクションを選んで貼るだけ。文言を差し替えれば完成です。
</p>
<a
href="#start"
className="mt-10 inline-block animate-[reveal-up_0.6s_ease-out_0.36s_both] rounded-full bg-zinc-900 px-6 py-3 text-white motion-reduce:animate-none"
>
無料で始める
</a>
</div>
);
}ブロックごとのテキスト表示が向いている場面
見出しが2行以上ある、または文言を頻繁に変えるときに向いています。単語の分け方を気にしなくてよいので、文章を差し替えても壊れません。
Framer Motion でテキストを単語・文節ごとに出す
見出しそのものに動きを持たせたいときは、テキストを単語や文節に分け、それぞれを span で包んで遅延をずらします。Motion を使うと、遅延と動きを1か所で決められます。
次のコンポーネントは、分けた文字列の配列を受け取ります。英語なら空白で分け、日本語は自分で文節に分けて渡します。
"use client";
import { Fragment } from "react";
import { motion, useReducedMotion } from "motion/react";
export function SplitReveal({
parts,
spaced = false,
className,
}: {
parts: string[];
spaced?: boolean;
className?: string;
}) {
const reduce = useReducedMotion();
return (
<h1 className={className}>
{parts.map((part, i) => (
<Fragment key={i}>
<motion.span
className="inline-block"
initial={reduce ? false : { opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: i * 0.06, ease: "easeOut" }}
>
{part}
</motion.span>
{spaced && i < parts.length - 1 ? " " : null}
</Fragment>
))}
</h1>
);
}日本語のテキストは文節か読点で区切る
日本語には単語の間に空白がありません。そのため split(" ") は使えず、1文字ずつに分けるのも避けます。
見出しを文節(「動きのある」「LP を」のような意味のかたまり)か、読点のところで手で分けて配列にします。
span に inline-block を付けているので、折り返しは区切りの位置でだけ起きます。文節で区切ると、スマホで単語の途中で改行されることも防げます。
読み上げで区切られるのが気になる場合は、h1 に aria-label で全文を付け、分けた span には aria-hidden を付けます。
// 日本語: 文節ごとに手で区切る
<SplitReveal
parts={["動きのある", "LP を、", "今日のうちに", "公開する"]}
className="text-4xl font-semibold tracking-tight md:text-6xl"
/>
// 英語: 空白で区切り、spaced で単語の間に空白を戻す
<SplitReveal
parts={"Ship a landing page that moves".split(" ")}
spaced
className="text-4xl font-semibold tracking-tight md:text-6xl"
/>単語ごとの遅延は 0.06 秒前後にする
6つに区切った見出しなら、0.06 秒ずつで最後の文節は 0.3 秒後に出始めます。0.15 秒ずつにすると、全部出るまで1秒を超え、待たされている感じが出ます。
区切りが多いときほど、1つあたりの遅延を短くします。
テキストを行ごとに下からマスクで出す方法
大きな見出しで、行ごとに下から「せり上がる」ように出す演出もよく使われます。
仕組みは、行ごとにはみ出しを隠す箱(overflow-hidden)を作り、中の文字を箱の外(下)から元の位置へ動かすだけです。箱の外にある間は見えないので、マスクで切り抜いたように見えます。
スクロールした先のセクション見出しにも使えるよう、whileInView で画面に入ったときに再生します。
"use client";
import { motion } from "motion/react";
export function LineMaskReveal({ lines }: { lines: string[] }) {
return (
<h2 className="text-4xl font-semibold leading-tight md:text-6xl">
{lines.map((line, i) => (
<span key={i} className="block overflow-hidden pb-1">
<motion.span
className="block"
initial={{ y: "110%" }}
whileInView={{ y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.7, delay: i * 0.1, ease: [0.22, 1, 0.36, 1] }}
>
{line}
</motion.span>
</span>
))}
</h2>
);
}マスク表示するテキストの行は短めに決めておく
lines に渡す1行が長いと、スマホでは1つの行がさらに折り返されます。動き自体は壊れませんが、2行がまとめて出てくるため演出が弱まります。
スマホでも1行に収まる長さで区切ると、意図どおりに見えます。
pb-1 は、g や y のように下に伸びる文字が箱で切れないための余白です。
テキストアニメーションの SEO と読み上げへの配慮
テキストは最初から HTML に入れる
この記事のコードは、どれも見出しの文字がサーバーで出力される HTML に含まれます。Motion の initial で透明にしていても、文字そのものは HTML にあるので、検索エンジンにも読み上げソフトにも届きます。
避けたいのは、useEffect の中で innerText を1文字ずつ足していく作り方です。HTML の段階では見出しが空になり、内容が伝わりにくくなります。
動きを減らす設定では最初から全文を出す
端末の設定で動きを減らしている人には、最初から全文を表示しています。
CSS の方法では motion-reduce:animate-none を使います。SplitReveal では useReducedMotion(動きを減らす設定を読み取る仕組み)を使っています。
サイト全体でまとめて対応する方法もあります。共通のレイアウトを MotionConfig(動きの設定をまとめる部品)で包み、reducedMotion="user" を指定します。
ファーストビューは1秒以内に出し終える
透明(opacity: 0)の要素は、表示の速さを測る指標(LCP)の対象から外れます。そのため、見出しが見えるまで計測が遅れます。
ファーストビューの見出しは、遅延を含めて1秒以内に出し終えるのが安全です。
テキストが順に出る完成ヒーローを使う方法

行のマスク、文字を切り抜いた見出し、背景の動きと合わせたタイミング。ここまで揃ったヒーローは、この記事のコードを組み合わせても調整に時間がかかります。
DesignLayer は、人が組んだ React + Tailwind のモーション付きセクションを、ブラウザでライブプレビューしてからコピーできるカタログです。
閲覧はアカウントもカードも不要です。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)です。
ヒーロー全体の作り方は https://design-layer.com/ja/articles/nextjs-animated-hero-section-guide で解説しています。
テキストアニメーションについてよくある質問
見出しの文字が最初から HTML にあれば、問題になりにくいです。この記事の方法はすべてその形です。
ブラウザ側で後から文字を書き足す作り方は避けてください。
まとめ:Next.js でテキストを順番に表示する手順
テキストは行か単語(日本語は文節)で区切って出し、1文字ずつは避ける
まずは CSS の遅延指定で、ラベル・見出し・本文を順番に出す
見出しそのものを動かすなら、Motion で区切りごとに 0.06 秒ずつ遅らせる
行のマスク表示は、はみ出しを隠す箱と縦の移動で作れる
文字は最初から HTML に入れ、動きを減らす設定では全文をすぐに出す
まずは CSS の HeroText をそのまま貼り、遅延の値を変えて好みのテンポを探してください。
Motion の基本は https://design-layer.com/ja/articles/nextjs-framer-motion-guide 、ヒーロー全体の組み方は https://design-layer.com/ja/articles/nextjs-animated-hero-section-guide が参考になります。


