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() はすべての空白を &nbsp; にするため、通常の空白に戻す
		var html = quill.getSemanticHTML().replace( /&nbsp;/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() には、すべての空白が &nbsp; に置き換わる不具合が報告されています(GitHubのissue #4509)。上のコードでは、送信前に通常の空白に戻しています。新しいバージョンに更新したときは、この処理が必要かどうか確認してください。

ステップ4:フォームを置いて動作を確認する

フォームを置く固定ページには、ショートコードブロックで次の文字列を入れます。

[my_quill_form]
  1. 管理画面の「プラグイン」で「My Quill Form」を有効化する
  2. 固定ページを作り、ショートコードブロックに上の文字列を入れて公開する
  3. 投稿者以上の権限のユーザーでログインし、そのページでタイトルと本文を入力して送信する
  4. 「送信しました」と表示され、管理画面の「投稿」に「レビュー待ち」の投稿ができていることを確認する
  5. 本文に <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 では &nbsp; の置き換えに注意する

関連記事:

お気軽にご相談ください

お見積りへ お問い合わせへ
▲