2026年9月5日
2026年9月5日
WordPressでQuill(リッチテキストエディター)を使う方法|投稿フォームへの組み込み手順
Quill(クイル)は、Webページにリッチテキスト(太字・見出し・リスト・リンクなど)の入力欄を追加できる、オープンソースのJavaScriptライブラリです。この記事では、WordPressのフロント側(サイトの表示側)に置く投稿フォームにQuillを組み込む手順を、小さなプラグインの形で説明します。内容は2026年10月時点のQuill 2.0.3を前提にしています。
Quillを使う場面
WordPressの投稿の編集画面は、ブロックエディターで動いています。ブロックエディターをQuillに置き換えることは現実的ではありません。Quillが向いているのは、ブロックエディターが使われない場所です。
- ログインした会員が、サイトの表示側から記事の下書きを送るフォーム
- 問い合わせや口コミなど、文字の装飾を許可したい入力欄
- 自作のプラグインの設定画面などにある、ちょっとした説明文の入力欄
WordPress用の「Quillを有効にするだけ」の定番プラグインは見当たらないため、この記事ではコードで組み込みます。
完成形と構成
ログインしている投稿者以上のユーザーが、固定ページに置いたフォームからタイトルと本文を送ると、「レビュー待ち」の投稿として保存される仕組みを作ります。
wp-content/plugins/my-quill-form/
├── my-quill-form.php … プラグイン本体(読み込み・フォーム・保存)
├── my-quill-form.js … Quill の初期化と送信時の処理
└── vendor/quill/
├── quill.js
└── quill.snow.css
ステップ1:Quillのファイルを置く
Quillのファイルは、CDN(jsDelivrなど)から読み込むこともできます。この記事では、外部のサービスに依存せず、訪問者のアクセス情報を第三者に送らないよう、ファイルをサイトに置きます。
cd wp-content/plugins
mkdir -p my-quill-form/vendor/quill
cd my-quill-form/vendor/quill
# npm で公開されている Quill 2.0.3 の配布ファイルを取得する
curl -fsSLO https://cdn.jsdelivr.net/npm/quill@2.0.3/dist/quill.js
curl -fsSLO https://cdn.jsdelivr.net/npm/quill@2.0.3/dist/quill.snow.css
quill.snow.css は、ツールバーが上に付く標準のテーマ(Snow)の見た目です。Quillのライセンスは BSD-3-Clause です。
ステップ2:プラグイン本体を作る
my-quill-form.php を作ります。スクリプトの読み込み、ショートコードでのフォーム表示、送信内容の保存をまとめています。
<?php
/**
* Plugin Name: My Quill Form
* Description: Quill を使った投稿フォーム(ショートコード my_quill_form)
* Version: 1.0.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// フォームがあるページだけで Quill を読み込む
add_action( 'wp_enqueue_scripts', function () {
$post = get_post();
if ( ! is_singular() || ! $post || ! has_shortcode( $post->post_content, 'my_quill_form' ) ) {
return;
}
wp_enqueue_style( 'quill-snow', plugins_url( 'vendor/quill/quill.snow.css', __FILE__ ), array(), '2.0.3' );
wp_enqueue_script( 'quill', plugins_url( 'vendor/quill/quill.js', __FILE__ ), array(), '2.0.3', true );
wp_enqueue_script( 'my-quill-form', plugins_url( 'my-quill-form.js', __FILE__ ), array( 'quill' ), '1.0.0', true );
} );
// フォームを表示するショートコード
add_shortcode( 'my_quill_form', function () {
if ( ! current_user_can( 'edit_posts' ) ) {
return '<p>投稿するには、投稿者以上の権限でログインしてください。</p>';
}
$notice = '';
if ( isset( $_GET['submitted'] ) ) {
$notice = '1' === $_GET['submitted']
? '<p>送信しました。レビュー後に公開されます。</p>'
: '<p>送信に失敗しました。もう一度お試しください。</p>';
}
ob_start();
echo $notice; // 固定の文字列のみ
?>
<form id="my-quill-form" method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
<p>
<label for="my-quill-title">タイトル</label><br>
<input type="text" id="my-quill-title" name="my_title" required style="width:100%">
</p>
<div id="my-quill-editor" style="height:240px"></div>
<input type="hidden" id="my-quill-content" name="my_content">
<input type="hidden" name="action" value="my_quill_submit">
<?php wp_nonce_field( 'my_quill_submit', 'my_quill_nonce' ); ?>
<p><button type="submit">送信する</button></p>
</form>
<?php
return ob_get_clean();
} );
// 送信内容を「レビュー待ち」の投稿として保存する(ログインユーザーのみ)
add_action( 'admin_post_my_quill_submit', function () {
if ( ! isset( $_POST['my_quill_nonce'] )
|| ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['my_quill_nonce'] ) ), 'my_quill_submit' ) ) {
wp_die( '不正なリクエストです。', '', array( 'response' => 403 ) );
}
if ( ! current_user_can( 'edit_posts' ) ) {
wp_die( '権限がありません。', '', array( 'response' => 403 ) );
}
$title = sanitize_text_field( wp_unslash( $_POST['my_title'] ?? '' ) );
$content = wp_kses_post( wp_unslash( $_POST['my_content'] ?? '' ) );
$post_id = 0;
if ( '' !== $title && '' !== trim( wp_strip_all_tags( $content ) ) ) {
// wp_insert_post() はスラッシュ付きのデータを受け取る前提のため wp_slash() を通す
$post_id = wp_insert_post( wp_slash( array(
'post_title' => $title,
'post_content' => $content,
'post_status' => 'pending',
'post_type' => 'post',
'post_author' => get_current_user_id(),
) ), true );
}
$ok = $post_id && ! is_wp_error( $post_id );
$back = wp_get_referer() ? wp_get_referer() : home_url( '/' );
wp_safe_redirect( add_query_arg( 'submitted', $ok ? '1' : '0', $back ) );
exit;
} );
ポイントは次のとおりです。
admin_post_{action}フックは、ログインしているユーザーの送信だけを受け付けます(未ログインの送信はadmin_post_nopriv_{action}に届きますが、ここでは用意していません)- 本文は、ブラウザー側でQuillが作ったHTMLをそのまま信用せず、
wp_kses_post()で投稿本文に使ってよいタグだけに絞ってから保存します - 投稿の状態を
pending(レビュー待ち)にして、管理者が確認してから公開します
ステップ3:Quillを初期化するJavaScript
my-quill-form.js を作ります。
// Quill を初期化し、送信時に本文の HTML を hidden の入力欄に入れる
document.addEventListener( 'DOMContentLoaded', function () {
var form = document.getElementById( 'my-quill-form' );
if ( ! form ) {
return;
}
var quill = new Quill( '#my-quill-editor', {
theme: 'snow',
placeholder: '本文を入力してください',
modules: {
toolbar: [
[ { header: [ 2, 3, false ] } ],
[ 'bold', 'italic', 'underline' ],
[ { list: 'ordered' }, { list: 'bullet' } ],
[ 'link' ],
[ 'clean' ]
]
}
} );
form.addEventListener( 'submit', function () {
// Quill 2.0.3 の getSemanticHTML() はすべての空白を にするため、通常の空白に戻す
var html = quill.getSemanticHTML().replace( / /g, ' ' );
document.getElementById( 'my-quill-content' ).value = html;
} );
} );
ツールバーに並べる項目は modules.toolbar で決めます。投稿本文に使わせたくない装飾(文字色など)は、ここに入れなければ選べません。ただし、貼り付けた文章からは装飾が入ることがあるため、保存側の wp_kses_post() は省かないでください。
本文のHTMLの取り出し方
Quill 2 で本文をHTMLとして取り出すには、公式APIの getSemanticHTML() を使います。quill.root.innerHTML でも取り出せますが、Quill 2 は箇条書きと番号付きリストをどちらも <ol> と独自の属性で表すなど、Quillの外で表示する前提のHTMLになっていません。
Quill 2.0.3 の getSemanticHTML() には、すべての空白が に置き換わる不具合が報告されています(GitHubのissue #4509)。上のコードでは、送信前に通常の空白に戻しています。新しいバージョンに更新したときは、この処理が必要かどうか確認してください。
ステップ4:フォームを置いて動作を確認する
フォームを置く固定ページには、ショートコードブロックで次の文字列を入れます。
[my_quill_form]
- 管理画面の「プラグイン」で「My Quill Form」を有効化する
- 固定ページを作り、ショートコードブロックに上の文字列を入れて公開する
- 投稿者以上の権限のユーザーでログインし、そのページでタイトルと本文を入力して送信する
- 「送信しました」と表示され、管理画面の「投稿」に「レビュー待ち」の投稿ができていることを確認する
- 本文に
<script>などを含むHTMLを貼り付けて送信し、保存された投稿から取り除かれていることを確認する
既存の本文をQuillで編集させる場合
保存済みの本文をQuillに読み込んで編集させる場合は、quill.clipboard.dangerouslyPasteHTML() を使います。名前のとおり、渡したHTMLをそのまま解釈するため、wp_kses_post() で無害化したHTMLだけを渡してください。PHP側から渡す場合は、wp_add_inline_script() と wp_json_encode() を使い、JavaScriptの文字列として安全に埋め込みます。
注意事項
- 未ログインの訪問者から受け付ける場合は、
admin_post_nopriv_{action}を使い、スパム対策(reCAPTCHAなど)と送信回数の制限を追加してください。保存先も公開済みの投稿にはしないでください - 画像はこの例では扱っていません。ツールバーに画像ボタン(
image)を追加すると、Quillは標準では画像をdata:で始まるURL(Base64)として本文に埋め込みますが、wp_kses_post()はdata:のURLを許可していないため、保存時に画像のURLが取り除かれます。画像を受け付ける場合は、メディアライブラリにアップロードする処理を別に作ります - Quillのファイルを更新するときは、ステップ1のバージョン番号と、
wp_enqueue_script()に渡しているバージョン番号をそろえて変更します
まとめ
- Quillはブロックエディターの代わりではなく、フロントの投稿フォームなどに使う
- ファイルはサイトに置き、フォームがあるページだけで読み込む
- 送信は nonce と権限を確認し、本文は
wp_kses_post()で無害化してから保存する - 本文の取り出しは
getSemanticHTML()。2.0.3 では の置き換えに注意する
関連記事: