LINE Flex Messageは、CSS Flexboxの考え方でレイアウトを組めるMessaging APIのメッセージタイプです。テキスト・画像・ボタンを1つの吹き出しに自由に配置でき、JSONで記述します。この記事では、公式リファレンスに書かれた構造と上限値、Flex Message SimulatorでJSONを作って取り出す手順、Node.jsから送るコードまでを順に説明します。掲載したTypeScriptは、2026年10月時点の公式SDK「@line/bot-sdk」11.3.0で型検査を通し、送信前チェックの関数を実行して動作を確認しています(APIへの実送信は行っていません)。
まとめ:LINE Flex Messageの構造・作り方・上限値の要点
- 構造はコンテナ(バブル/カルーセル)→ブロック(header・hero・body・footer)→コンポーネントの3層です。
- カルーセルに入れられるバブルは最大12個。JSONの最大サイズはバブル30KB、カルーセル50KBです。
- Flex Message Simulatorの「View as JSON」で取り出せるのはコンテナ部分だけです。送るときは
"type": "flex"とaltText(最大1,500文字)で包まないと400エラーになります。 - 送信前は
validateエンドポイントで形式を検証できます。成功時は200と空のJSONが返ります。 - 料金は送信先の人数で数えます。Flex Messageだから高くなることはなく、応答メッセージは通数に数えません。
- ノーコードで済ませたいなら、LINE公式アカウントマネージャーのカードタイプメッセージ(最大9枚)が近い役割を担います。
Flex Messageの3層構造:コンテナ・ブロック・コンポーネント
通常のテキストメッセージや画像メッセージは1種類の素材しか送れません。Flex Messageは1つの吹き出しの中に見出し・画像・本文・ボタンを並べられる点が違います。JSONは「コンテナの中にブロック、ブロックの中にコンポーネント」という入れ子で書きます。
バブルとカルーセルの上限:12個・30KB・50KB
コンテナはバブル(吹き出し1つ)とカルーセル(バブルを横スクロールで並べる)の2種類です。Messaging APIリファレンスに記載された上限は次のとおりです。
| 項目 | 上限・既定値 |
|---|---|
| バブルのJSONサイズ | 30KB |
| カルーセルのJSONサイズ | 50KB |
| カルーセル内のバブル数 | 12個 |
| バブルの size | nano・micro・deca・hecto・kilo・mega・giga(既定はmega) |
| altText | 1,500文字 |
| 画像 | HTTPS・JPEGかPNG・最大1024×1024px |
| 画像のファイルサイズ | 10MB(animated指定時は300KB) |
カルーセルには、size の違うバブルを混ぜられません。また各バブルのbodyは、いちばん背の高いバブルに合わせて伸びます。商品ごとに説明文の長さが大きく違うと、短いカードの下に空白ができるので、文字量をそろえるか maxLines で切りそろえます。
size の deca と hecto は、iOS版・Android版LINE 13.6.0以降、PC版LINE 7.17.0以降でしか効きません。それより古い版ではkiloとして表示されます。
バブルの中のブロックは、上からheader・hero・body・footerの順に表示されます。どれも省略でき、1つのバブルに各ブロックは1つずつしか置けません。
- header:件名や見出し。ボックスを指定します。
- hero:主役の画像や動画。ボックス・画像・動画のいずれかを指定できる唯一のブロックです。
- body:本文。ボックスを指定します。
- footer:ボタンや補足情報。ボックスを指定します。
動画は条件が厳しく、heroの直下に置く、バブルのsizeをkilo・mega・gigaのどれかにする、カルーセルに入れない、の3つをすべて満たす必要があります。動画に対応していない版のLINE(iOS・Android 11.22.0未満、PC 7.7.0未満)では altContent に指定した画像が代わりに表示されます。
コンポーネント9種と非推奨のフィラー
ブロックの中に置ける部品は、ボックス・ボタン・画像・動画・アイコン・テキスト・スパン・セパレータ・フィラーの9種です。部品を入れ子にできるのはボックスで、layout に horizontal(横並び)・vertical(縦並び)・baseline(文字の基準線で横に揃える)のいずれかを指定します。アイコンを置けるのはbaselineボックスの中だけで、逆にボックス・ボタン・画像・セパレータはbaselineボックスに置けません。スパンはボックスではなくテキストの contents に入れる部品で、1つの文の中で色や太さを部分的に変えるときに使います。
フィラーは公式に「非推奨」とされています。余白は margin・spacing・paddingAll などのプロパティで作ってください。古いサンプルを流用するとフィラーが残っていることがあるので、見つけたら置き換えます。
Flex Message Simulatorの使い方:作成からJSONの取り出しまで
Flex Message Simulatorは、実際に送らずに描画結果を確認できる公式ツールです。開くとLINE Developersコンソールへのログインを求められます。画面はプレビューエリア・ツリービューエリア・プロパティエリアの3つに分かれています。
- 右上の[Showcase]で定義済みレイアウトを選び[作成]を押す。ゼロから作るなら[New]から bubble か carousel を選ぶ。
- ツリービューエリアで header や body を選び、上部の[+]から box・text・image などを追加する。
- プロパティエリアで色や文字を入力し、Enterキーで確定する。Enterを押すまでプレビューに反映されない。
- 右上の[</>View as JSON]を開き、[コピー]でJSONを取り出す。
手元にあるJSONを読み込むときは、同じ[View as JSON]のモーダルに貼り付けて[適用]を押します。既存メッセージの修正は、この貼り付けから始めると早く済みます。
コピーしたJSONをそのまま送ると400エラーになる理由
Simulatorが出力するのは {"type": "bubble", ...} から始まるコンテナだけです。Messaging APIの messages に入れるのはメッセージオブジェクトなので、次のように type・altText・contents で包みます。包まずに送ると形式不正として400が返ります。
{
"type": "flex",
"altText": "ご予約内容の確認",
"contents": {
"type": "bubble",
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{ "type": "text", "text": "ご予約ありがとうございます" }
]
}
}
}
altText は必須です。プッシュ通知・トークリスト・引用メッセージではFlexの代わりにこの文字列が表示されます。「Flex Message」のような汎用文字列にせず、通知だけ読んでも用件がわかる文にします。
Messaging APIでFlex Messageを送る手順
送信にはLINE公式アカウントでMessaging APIを有効にし、チャネルアクセストークンを発行しておく必要があります。準備の流れはMessaging APIとは?LINEでできること・料金の通数カウントと実装手順を解説にまとめています。
curlによるプッシュメッセージの送信
エンドポイントは通常のメッセージと同じです。messages 配列には1回のリクエストで最大5件まで入れられます。
curl -X POST https://api.line.me/v2/bot/message/push \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer {channel access token}' \
-d '{
"to": "U4af4980629...",
"messages": [{
"type": "flex",
"altText": "ご予約内容の確認",
"contents": { "type": "bubble", "body": { "type": "box", "layout": "vertical",
"contents": [{ "type": "text", "text": "ご予約ありがとうございます" }] } }
}]
}'
Node.js SDKによる上限検査と送信
Simulatorで作ったJSONを本番に流す前に、公式リファレンスの上限(バブル30KB・カルーセル50KB・バブル12個・size統一・altText1,500文字)に当たっていないかをコードで確かめると、配信直前の400を防げます。次のコードは@line/bot-sdk 11.3.0とTypeScript 7.0.2で型検査を通し、Node.js 26で検査関数を実行して、size混在のカルーセルを「kilo,mega」、30KBを超えるバブルを含むカルーセルを「1個目のバブルが 42098 バイト」と検出することを確認しました。
import { messagingApi } from "@line/bot-sdk";
import { readFileSync } from "node:fs";
// Simulator の「View as JSON」でコピーした中身(bubble か carousel)
const container = JSON.parse(
readFileSync("./simulator.json", "utf8"),
) as messagingApi.FlexContainer;
export function preflight(c: messagingApi.FlexContainer, altText: string): string[] {
const errors: string[] = [];
const bytes = Buffer.byteLength(JSON.stringify(c), "utf8");
if (altText.length > 1500) errors.push(`altText が ${altText.length} 文字(上限1500)`);
if (c.type === "bubble" && bytes > 30 * 1024) errors.push(`bubble が ${bytes} バイト(上限30KB)`);
if (c.type === "carousel") {
if (bytes > 50 * 1024) errors.push(`carousel が ${bytes} バイト(上限50KB)`);
if (c.contents.length > 12) errors.push(`バブルが ${c.contents.length} 個(上限12)`);
c.contents.forEach((b, i) => {
const n = Buffer.byteLength(JSON.stringify(b), "utf8");
if (n > 30 * 1024) errors.push(`${i + 1}個目のバブルが ${n} バイト(上限30KB)`);
});
const sizes = new Set(c.contents.map((b) => b.size ?? "mega"));
if (sizes.size > 1) errors.push(`バブルの size が混在: ${[...sizes].join(",")}`);
}
return errors;
}
const client = new messagingApi.MessagingApiClient({
channelAccessToken: process.env.LINE_CHANNEL_ACCESS_TOKEN!,
});
// Simulator の JSON は contents の中身だけなので type と altText で包む
const message: messagingApi.FlexMessage = {
type: "flex",
altText: "ご予約内容の確認",
contents: container,
};
const problems = preflight(container, message.altText);
if (problems.length > 0) throw new Error(problems.join("\n"));
// validatePush は形式が不正なら400で例外を投げる
await client.validatePush({ messages: [message] });
await client.pushMessage({ to: process.env.LINE_USER_ID!, messages: [message] });
検証環境はNode.js 26.5.0、TypeScript 7.0.2、@types/node で、tsconfig.jsonは "module": "nodenext"・"target": "es2022"・"types": ["node"]・"strict": true です。トップレベルで await を使っているので、package.jsonに "type": "module" も指定してください。CommonJSのままだとTypeScriptが「TS1309」でコンパイルを止めます。JSONのサイズはUTF-8のバイト数で測っていますが、公式は計測方法を明記していないため、上限ぎりぎりではなく余裕を持たせてください。
validateエンドポイントによる送信前の形式検証
/v2/bot/message/validate/push などのvalidate系エンドポイント(reply・push・multicast・narrowcast・broadcastの5種)は、メッセージを送らずに messages の中身が有効かだけを検証します。成功時はステータス200と空のJSONオブジェクトが返ります。宛先の to など messages 以外は検証対象外なので、ユーザーIDの誤りはここでは見つかりません。CIでテンプレートJSONを検証する用途に向いています。
レイアウトの既定値と端末ごとの表示差
Simulatorで整って見えても実機で崩れるときは、flexの既定値・テキストの折り返し・端末差の3点を順に確認します。
- flexの既定値が縦横で違う:水平ボックスの子要素は
flexの既定値が1で、幅を兄弟要素と分け合います。垂直ボックスでは既定値が0で、高さを分け合いません。横並びで片方だけ狭くしたいときは、その要素に"flex": 0を明示します。 - テキストは既定で折り返さない:幅を超えた部分は省略記号「…」に置き換わります。説明文には
"wrap": trueを付け、行数を制限したいときはmaxLinesを併用します。 - 描画は端末ごとに変わる:公式ドキュメントも、OS・LINEのバージョン・解像度・言語設定・フォントで描画結果が異なりうると明記しています。Simulatorの表示だけで確定させず、iOSとAndroidの実機に1回ずつ送って確認します。
テキスト・テンプレート・カードタイプとの違いと文字数上限
LINEのメッセージには、Flex以外にも選択肢を出せる形式があります。用途と上限を並べると、Flexが要るかどうかを判断しやすくなります。
| 形式 | 主な上限 | 作れる場所 |
|---|---|---|
| テキスト(Messaging API) | 5,000文字 | API |
| テキスト(管理画面) | 500文字 | 管理画面 |
| クイックリプライ | ボタン13個 | API |
| ボタンテンプレート | アクション4個・本文160文字 | API |
| 確認テンプレート | アクション2個・本文240文字 | API |
| カルーセルテンプレート | 10カラム・各3アクション | API |
| Flex Message | バブル12個・altText1,500文字 | API |
| カードタイプメッセージ | カード9枚 | 管理画面 |
テキストメッセージの文字数はUTF-16の符号単位で数えます。Unicode絵文字や一部の漢字は1文字で複数単位を使うため、5,000文字ぎりぎりの文章はその分だけ早く上限を超えます。一方、テンプレートの本文やアクションのラベルは書記素クラスタ単位(見た目の1文字)で数えるので、同じ文でも数え方が違います。ボタンテンプレートの本文は、画像かタイトルを付けると60文字に縮みます。
選択肢付きメッセージを作るときの形式の選び方
「はい/いいえ」の2択なら確認テンプレート、3〜4択なら画像付きのボタンテンプレートで足ります。返答を入力欄の上に並べたいならクイックリプライで、最大13個まで出せます。ボタンを5つ以上並べる、価格や日時を表のように見せる、1枚ごとに配色を変える、といった要件が出たときがFlex Messageの出番です。テンプレートで足りる場面でFlexを選ぶと、JSONの保守だけが増えます。
ノーコードで済む範囲:カードタイプメッセージ
LINE公式アカウントマネージャーのカードタイプメッセージは、プロダクトなど4種類の型から1種類を選び、最大9枚のカードと「もっと見るカード」をカルーセル形式で配信できる機能です。保存したカードはメッセージ配信・あいさつメッセージ・自動応答メッセージで使えます。レイアウトは型の範囲に限られ、スマートフォンの管理アプリではイメージタイプしか作れません。マーケティング担当者が自分で更新したいならカードタイプ、レイアウトを自由に組みたい・システムから動的に値を埋めたいならFlex Messageと分けると、迷いません。
Flex Messageの配信コストと通数の数え方
Messaging APIの通数は、送信対象の人数で数えます。公式の例では、メッセージオブジェクト4件を含むプッシュメッセージを5人のトークルームへ1回送ると5通です。Flex Messageだからといって1人あたりの通数が増えることはありません。ブロック中のユーザーなど、実際に届かない宛先は数えません。
| プラン(日本) | 月額固定費(税別) | 無料メッセージ通数 | 追加メッセージ |
|---|---|---|---|
| コミュニケーションプラン | 0円 | 200通 | 不可 |
| ライトプラン | 5,000円 | 5,000通 | 不可 |
| スタンダードプラン | 15,000円 | 30,000通 | 3円/通(20万通超は2.5円/通) |
プッシュ・マルチキャスト・ブロードキャスト・ナローキャストは通数に数え、応答メッセージは数えません。応答メッセージは、ユーザーの発言やポストバックなどで届いたWebhookイベントに含まれる応答トークン(replyToken)を使って返す方式です。予約ボタンのタップをポストバックで受け、その応答として確認のFlex Messageを返す構成にすれば、無料枠を消費しません。上限を超えるとエラーが返って送信されないため、配信量の多い運用では月中に利用状況を取得するエンドポイントで残数を確認します。プランと金額は改定されることがあるので、最新はLINE公式アカウント料金プランで確認してください。LINE Notifyからの移行で通数を試算したい場合はLINE Notify終了後の移行実装|Messaging APIへの置き換え手順と通知コスト試算も参考になります。
Flex Messageを選ばない方がよい場面
Flex Messageは見た目の自由度が高い反面、JSONの保守と端末ごとの表示確認が毎回ついて回ります。次の場面ではFlexを選ばない判断を勧めます。
- 通知の1行で用件が済む連絡:プッシュ通知に出るのはaltTextだけです。テキストメッセージで足ります。
- 非エンジニアが毎週差し替える販促:JSONを触れる人がいないと更新が止まります。カードタイプメッセージかリッチメッセージを使います。
- 入力フォームを作りたい:Flexには入力欄がありません。ボタンからLIFFとは?LINEアプリ内で動くWebアプリの仕組みと開発手順を実装目線で解説で扱うLIFFアプリを開く構成にします。
Messaging APIで送ったプッシュメッセージやマルチキャストメッセージの開封・クリックをメッセージ単位で追いたい場合は、送信リクエストの customAggregationUnits に集計単位名を1つ指定し、統計取得APIで集計します。管理画面の分析機能の見方はLINE公式アカウントの分析機能とは?見られる項目・見方・開封率の確認方法を参照してください。
よくある質問
Flex Message Simulatorを使うには何が必要ですか?
LINE Developersコンソールへのログインが必要です。開発環境やコードは不要で、ブラウザ上でレイアウトを作り、[View as JSON]からJSONをコピーできます。
LINE公式アカウントマネージャーでFlex Messageは作れますか?
マネージャーで作れるのは、カードタイプメッセージやリッチメッセージなどあらかじめ用意された型です。任意のJSONで組むFlex Messageは、Messaging APIを通して送ります。
LINEのメッセージの文字数制限はいくつですか?
テキストメッセージはMessaging APIからなら5,000文字、LINE公式アカウントの管理画面からなら500文字です。Flex MessageのaltTextは1,500文字、ボタンテンプレートの本文は160文字(画像かタイトルがあると60文字)、確認テンプレートの本文は240文字です。テキストとaltTextはUTF-16の符号単位、テンプレートの本文は見た目の1文字(書記素クラスタ)単位で数えます。
Flex Messageのカルーセルは何枚まで並べられますか?
1つのカルーセルに入れられるバブルは最大12個で、JSON全体は50KBまでです。カルーセルテンプレートの10カラム、カードタイプメッセージの9枚とは上限が異なります。
SimulatorのJSONを送ると400エラーになるのはなぜですか?
Simulatorが出力するのはコンテナ部分だけだからです。"type": "flex"、altText、contents の3つを持つメッセージオブジェクトで包んでから messages 配列に入れてください。送る前にvalidateエンドポイントで確認すると原因を切り分けられます。