結論
ショートコードの出力位置がずれる原因は、コールバック内で echo や get_template_part() などを直接出力していることです。ショートコードは「戻り値で本文の該当箇所を置き換える」仕組みなので、return で文字列を返します。
テンプレートファイルを読み込みたい場合は、出力バッファで包みます。
add_shortcode( 'tpl', function () {
ob_start();
get_template_part( 'parts/card' );
return ob_get_clean();
} );
原因・仕組み
the_content の途中で do_shortcode が実行され、本文の文字列の中の [タグ] が、コールバックの戻り値で置き換えられます。この処理が終わる前に、コールバック内で echo された内容は、その時点ですぐ画面に出力されます。一方、本文は処理が終わってから出力されるので、echo した部分が本文より前(多くは記事の先頭やテーマの上部)に表示されます。
公式のリファレンスにも、ショートコードの関数は置き換える文字列を返すもので、直接出力すると意図しない結果になる、と書かれています。
手順(サンプルコード)
1. 悪い例(そのまま使わない)
add_shortcode( 'bad_echo', function () {
echo '<span>ECHOED</span>'; // 本文より先に出力される
return '';
} );
2. 良い例:文字列を返す
add_shortcode( 'good_return', function () {
return '<span>RETURNED</span>';
} );
3. 関数が echo する場合は出力バッファで受ける
add_shortcode( 'tpl', function () {
ob_start();
echo '<p>from template</p>'; // 既存の echo 型の関数をここで呼ぶ
return ob_get_clean();
} );
get_template_part() や、echo するテンプレート関数を使うときの定番です。エラー時にバッファが残らないよう、ob_start() と ob_get_clean() は同じ関数内で対にします。
動作確認(検証環境と結果)
WordPress 7.1.2(ja)、PHP 8.2.12 の CLI 環境で実行しました。
apply_filters( 'the_content', 'before [bad_echo] after' )の戻り値は<p>before after</p>(ショートコードの位置には何も入らない)で、<span>ECHOED</span>は戻り値より先に標準出力へ出た。do_shortcode( 'before [bad_echo] after' )の戻り値はbefore after、ob_start で捕まえた echo 分は<span>ECHOED</span>。ob_start()で包んだ[tpl]はx<p>from template</p>yと、正しい位置に入った。
ログは testsshortcode-output-position-wrong-echo-vs-return.md に保存しています。
ショートコードの出力位置がずれる原因の注意点
- 出力バッファ内で例外が出ると、バッファが閉じられず出力が崩れることがあります。例外が起きうる処理には
try/finallyを検討します。 - 返す HTML は、ショートコード自身でエスケープします。属性の扱いはショートコードの作り方と属性を参照してください。
- 戻り値が長大になる場合は、
sprintfや配列のimplodeで組み立てると読みやすくなります。 - キャッシュしたい場合は、
set_transient()に戻り値の文字列を保存できます(echo型だとできません)。
ショートコードの出力位置がずれる原因でよくあるミス
- ショートコードの中で
the_title()などのecho型テンプレートタグを直接呼ぶ。get_the_title()のようなget_系を使います。 returnとechoの両方を書く。ob_start()したのにob_get_clean()を返さず、ob_end_flush()で出力してしまう。- 戻り値に
NULL(return忘れ)を返す。
ショートコードの出力位置がずれる原因のチェックリスト
- コールバック内に
echo/printが残っていないか - テンプレート読み込みは
ob_start()で包んだか the_*ではなくget_the_*を使っているか- 戻り値は常に文字列か
- 本文の途中に正しく入ることを確認したか
ショートコードの出力位置がずれる原因のFAQ(よくある質問)
Q. 既存プラグインのショートコードが先頭に出るのは、同じ原因ですか?
A. その可能性が高いです。作者に報告するか、ob_start で包んだラッパーのショートコードを作る方法があります。
Q. ウィジェットやテンプレートでも同じですか?
A. テンプレートで echo do_shortcode() する場合は、戻り値が文字列なので、同じ書き方で問題ありません。
Q. get_template_part() の戻り値は使えますか?
A. 戻り値は void または false で、HTML は返しません。出力バッファを使います。
筆者の見解(ショートコードの出力位置がずれる原因)
「echo 型のコードを無理に return 型に直す」のではなく、最初から get_ 系の関数と文字列の組み立てで書く方が、テストしやすいと考えます。出力バッファは便利ですが、使いすぎると流れが追いにくくなるため、既存のテンプレートを再利用したい場面に限るのが良いと思います。
ショートコードの出力位置がずれる原因の関連項目
- add_shortcode() の使い方|説明・引数・注意点
- do_shortcode() の使い方|説明・引数・注意点
- get_template_part() の使い方|説明・引数・注意点
- shortcode_atts() の使い方|説明・引数・注意点
- ショートコードの作り方と属性

