NewtとSvelteKitを利用して、問い合わせフォームを作成する
Table of contents
- 記事内で使用している主なソフトウェアのバージョン
- 前提条件
- 概要
- 1. Form Appを作成する
- 1-1. Form Appの追加
- 1-2. フォームの作成
- 2. リクエストの準備
- 2-1. 環境変数の設定
- 3. SvelteKitでシンプルな問い合わせフォームを作成する
- 3-1. HTMLフォームの追加
- 3-2. 自動返信メールの設定
- 3-3. 受信通知メールの設定
- 3-4. 挙動の確認
- 4. バリデーションを追加する
- 4-1. input要素の属性を利用したバリデーション
- 4-2. JavaScriptを利用したバリデーション
- 5. スパム対策を実装する
- 5-1. サイトキーとシークレットキーの取得
- 5-2. シークレットキーの登録
- 5-3. @types/grecaptchaのインストール
- 5-4. SvelteKitでの設定
- 6. リダイレクトをカスタマイズする
- 6-1. 送信成功ページ・送信失敗ページを用意する
- 6-2. リダイレクトを設定する
このチュートリアルでは、Newtの Form App と SvelteKit を利用して、問い合わせフォームを作成する手順を紹介します。
記事内で使用している主なソフトウェアのバージョン
- SvelteKit(
@sveltejs/kit
): 2.5.1 - Felte(
felte
): 1.2.14
前提条件
- SvelteKitのプロジェクトを作成済みであること
SvelteKitのセットアップについて知りたい場合は、以下のドキュメントをご確認ください。
- SvelteKitの プロジェクトを作成する のドキュメント
- NewtとSvelteKitを利用してブログを作成する
概要
SvelteKitで問い合わせフォームを作成し、Newtで問い合わせデータを確認できるようにします。
はじめに、自動返信・受信通知のついたシンプルなフォームを作成し、その後でバリデーション・スパム対策・リダイレクトの処理を実装しながら、カスタマイズの方法を学習していきます。
※ ここではスタイルについては扱いません。スタイルについて確認したい方は、Next.jsのコードとなりますが、問い合わせ用のテンプレート newt-starter-nextjs-contact を用意しているので、そちらのコードをご確認ください。
1. Form Appを作成する
はじめに、NewtでForm Appを作成します。
1-1. Form Appの追加
「Appを追加」をクリックして「タイプを選択して追加」を選択します。
「Form App」を選択して「追加」をクリックします。
App名・App UID・Appアイコンを設定して「追加」をクリックします。
これで「Contact」という名前のForm Appが追加されました。
1-2. フォームの作成
次にフォームを作成します。「フォームを作成」ボタンをクリックし、名前を付けてフォームを作成します。
作成後に表示されるエンドポイントは、2-1で利用します。
2. リクエストの準備
2-1. 環境変数の設定
SvelteKitの環境変数は、プロジェクトディレクトリの .env
ファイルから読み込めます。
ここでは、.env
ファイルを作成し、1-2で確認したエンドポイントの値を定義します。以下を、実際の値で置き換えて定義してください。
1PUBLIC_NEWT_FORM_ENDPOINT=https://xxxxxx.form.newt.so/v1/xxxxxx
上記のように定義しておくと、以下のような形で利用できます。
import { PUBLIC_NEWT_FORM_ENDPOINT } from '$env/static/public'
ここで使われている $env/static/public ですが、この static
は、これらの環境変数がビルド時に解決され、静的に置き換えられることを示しています。
クライアントサイドに公開したくない環境変数の場合、$env/static/private も利用できます。
またビルド時ではなく、実行時に環境変数の値を読みこむ必要がある場合、$env/dynamic/private や $env/dynamic/public も利用できます。
3. SvelteKitでシンプルな問い合わせフォームを作成する
続いて、SvelteKitでシンプルな問い合わせフォームを作成しましょう。
ここでは /contact
というパスにアクセスした時に、問い合わせフォームを表示するようにします。
3-1. HTMLフォームの追加
src/routes/contact/+page.svelte
を作成し、以下のように記述します。
1<script lang="ts">
2 import { PUBLIC_NEWT_FORM_ENDPOINT } from '$env/static/public'
3</script>
4
5<svelte:head>
6 <title>Newt・SvelteKitフォーム</title>
7 <meta name="description" content="NewtとSvelteKitを利用した問い合わせフォームです" />
8</svelte:head>
9
10<div>
11 <h1>Contact us</h1>
12 <form action={PUBLIC_NEWT_FORM_ENDPOINT} method="post">
13 <label for="name">Name</label>
14 <input id="name" name="name" />
15 <label for="email">Email</label>
16 <input id="email" name="email" type="email" />
17 <label for="message">Message</label>
18 <textarea id="message" name="message" />
19 <button type="submit">Submit</button>
20 </form>
21</div>
各属性について説明します。
label要素のfor属性と、input要素・textarea要素のid属性
label要素 の for
属性は、input要素 や textarea要素 の id
属性と一致するように設定します。
input要素・textarea要素のname属性
input要素・textarea要素のnameで設定された値をもとに、投稿データのスキーマが決まります。例えば上記の例では、name
・email
・message
フィールドを持ったスキーマが作成されます。
詳細については、フィールドの仕様 のドキュメントをご確認ください。
input要素のtype属性
お好みの入力形式に応じて、inputの型 を選択してください。デフォルトでは text
となります。
これで http://localhost:5173/contact
にアクセスすると、以下のような問い合わせフォームが作成できました。
※ スタイルについてはお好みで設定してください
3-2. 自動返信メールの設定
自動返信メール の設定をします。自動返信メールを設定することで、投稿を送信したエンドユーザーに対して、設定した内容のメールを自動で送信できます。
※ Newtは自動返信メールの送信先メールアドレスを検出するために email
フィールドを使います。input要素のname属性に email
を指定し、エンドユーザーのメールアドレスが入力されるようにしてください。
「App設定」から該当のフォームをクリックし、「メール設定」を選択します。
「自動返信メールを有効にする」にチェックを入れ、件名と本文を入力し、「保存」をクリックします。
自動返信メールでは {submission.name}
のように記述することで、メールの件名や本文に、投稿データの内容を埋め込めます。
3-1で、input要素・textarea要素のname属性に name
・email
・message
を指定したので、ここでは {submission.name}
・{submission.email}
・{submission.message}
を利用できます。
3-3. 受信通知メールの設定
受信通知メール の設定をします。受信通知メールを設定することで、エンドユーザーから新しいメッセージを受信した際に、メールで通知を受け取れます。
「App設定」から該当のフォームをクリックし、「メール設定」を選択します。
「受信通知メールを有効にする」にチェックを入れ、通知を受け取るメールアドレス・件名・本文を設定し、「保存」をクリックします。
受信通知メールでも、自動返信メールと同様に、投稿データの内容を埋め込めます。
3-4. 挙動の確認
これで、シンプルな問い合わせフォームを作成できました。
実際に動かして、挙動を確認してみましょう。
まず、開発サーバーを立ち上げて、問い合わせフォームを表示します。ここでは http://localhost:5173/contact
にアクセスします。
適当な内容を入力して送信すればよいですが、設定したEmail(name属性に email
を指定した入力値)に自動返信メールが届くので、ご自身のメールアドレスを入力するよう気をつけてください。
送信が成功すると、以下の画面が表示されます。
管理画面では、以下のようにデータが確認できます。
次に自動返信メールを確認します。以下のようなメールが飛んでいれば成功です。
最後に受信通知メールを確認します。以下のようなメールが飛んでいれば成功です。
これでシンプルなフォームの作成・自動返信メールの設定・受信通知メールの設定ができました。ここからはフォームのカスタマイズをしながら、より高度なフォームの作成方法を学んでいきましょう。
4. バリデーションを追加する
クライアントサイドのバリデーションについて、以下の順で紹介します。
- input要素の属性を利用したバリデーション
- JavaScriptを利用したバリデーション(Felteを利用したバリデーション)
4-1. input要素の属性を利用したバリデーション
HTML5のフォームバリデーションを利用することで、JavaScriptなしでバリデーションを実行できます。よく使われるものについては HTML5のフォームバリデーション をご確認ください。
例えば、name
を必須にしたい場合は、以下のように required
を指定します。
<input id="name" name="name" required />
もし name
を指定せず、送信を実行した場合は、以下のように表示されます。
4-2. JavaScriptを利用したバリデーション
HTMLネイティブのフォームバリデーションでは足りない場合、JavaScriptを利用することで、より柔軟なバリデーションを実行できます。
ここでは、バリデーションのタイミングを変え、さらにエラーメッセージをカスタマイズしてみましょう。
デフォルトのThanksページ・Errorページへのリダイレクトや カスタムリダイレクト は行われなくなりますので、ご注意ください。
ここでは Felte を利用してバリデーションを行います。まずは felte をインストールしましょう。
npm install felte
# or
yarn add felte
続いて、以下のように src/routes/contact/+page.svelte
を修正します。
1<script lang="ts">
2 import { PUBLIC_NEWT_FORM_ENDPOINT } from '$env/static/public'
3 import { createForm } from 'felte'
4
5 type FormValues = {
6 name: string
7 email: string
8 message: string
9 }
10
11 const { form, errors } = createForm<FormValues>({
12 validate: (values) => {
13 const errors: Record<string, string> = {}
14 if (!values.name) {
15 errors.name = 'Name is required'
16 }
17 return errors
18 },
19 onSubmit: async (values) => {
20 const formData = new FormData()
21 Object.entries(values).forEach(([key, value]) => {
22 formData.append(key, value)
23 })
24
25 await fetch(PUBLIC_NEWT_FORM_ENDPOINT, {
26 method: 'POST',
27 body: formData,
28 headers: {
29 Accept: 'application/json'
30 }
31 })
32 }
33 })
34</script>
35
36<svelte:head>
37 <title>Newt・SvelteKitフォーム</title>
38 <meta name="description" content="NewtとSvelteKitを利用した問い合わせフォームです" />
39</svelte:head>
40
41<div>
42 <h1>Contact us</h1>
43 <form use:form>
44 <label for="name">Name*</label>
45 <input id="name" name="name" aria-describedby="error-name-required" />
46 {#if $errors.name}
47 <span id="error-name-required" aria-live="assertive">
48 {$errors.name}
49 </span>
50 {/if}
51 <label for="email">Email</label>
52 <input id="email" name="email" type="email" />
53 <label for="message">Message</label>
54 <textarea id="message" name="message" />
55 <button type="submit">Submit</button>
56 </form>
57</div>
以下のことを行っています。
- 入力値の型として、
FormValues
を定義し、createForm
にジェネリクスとして渡します createForm
のvalidate
でバリデーション関数を定義できます。ここでは、name
がない場合にName is required
というエラーメッセージを設定しています
※ 詳細はFelteの Validation のドキュメントをご確認ください- 入力されたフォームのデータは
onSubmit
関数でvalues
として利用できます
※ 詳細はFelteの Submitting のドキュメントをご確認ください - FormData を利用して、送信するデータ
formData
を作成します - フェッチ API を利用して、Form Appのエンドポイントにデータを送ります。この時、Acceptヘッダーに
'application/json'
を指定します
また、以下の部分で validate
で設定したエラーメッセージを出しています。(ここでは name
がない場合に Name is required
というエラーメッセージを出しています)
他にも様々なバリデーションが設定できるので、Felteの Validation や Validators のドキュメントをご確認ください。
1<label for="name">Name*</label>
2<input id="name" name="name" aria-describedby="error-name-required" />
3{#if $errors.name}
4 <span id="error-name-required" aria-live="assertive">
5 {$errors.name}
6 </span>
7{/if}
以下のことを確認します。
- 入力変更があったタイミングでバリデーションが実行されること
name
が入力されていない場合にName is required
というエラーメッセージが出ること
5. スパム対策を実装する
続いて、スパム対策 として Google reCAPTCHA v3 の設置方法について説明します。
5-1. サイトキーとシークレットキーの取得
Google reCAPTHAのコンソール にアクセスし、新しいサイトを登録します。
reCAPTCHAタイプは reCAPTCHA v3
を選択します。
ここではローカル環境でのテストを行うので、ドメインには localhost
を追加します。本番環境で設定したい場合は、reCAPTCHAを導入したいサイトのドメインを設定してください。
全ての項目を入力して「送信」をクリックすると、サイトキーとシークレットキーが表示されます。
5-2. シークレットキーの登録
Newtの管理画面にアクセスし、「App設定」を開きます。該当のフォームをクリックし、「フォーム設定」を選択します。
「スパム対策」のセクションより、「Google reCAPTCHA v3を有効にする」にチェックを入れ、5-1で取得したシークレットキーを貼り付けて保存します。
5-3. @types/grecaptchaのインストール
後述の処理で必要になるため、@types/grecaptcha をインストールしておきます。
npm install --save-dev @types/grecaptcha
# or
yarn add -D @types/grecaptcha
5-4. SvelteKitでの設定
最後にSvelteKitでの設定を行います。
まず、環境変数に PUBLIC_RECAPTCHA_SITE_KEY
を追加し、5-1で取得したサイトキーを設定します。以下を、実際の値で置き換えて定義してください。
PUBLIC_NEWT_FORM_ENDPOINT=https://xxxxxx.form.newt.so/v1/xxxxxx
PUBLIC_RECAPTCHA_SITE_KEY=xxxx
3-2で作成した src/routes/contact/+page.svelte
を以下のように修正します。
1<script lang="ts">
2 import { PUBLIC_NEWT_FORM_ENDPOINT, PUBLIC_RECAPTCHA_SITE_KEY } from '$env/static/public'
3 import { createForm } from 'felte'
4
5 type FormValues = {
6 name: string
7 email: string
8 message: string
9 googleReCaptchaToken: string
10 }
11
12 const { form, errors } = createForm<FormValues>({
13 validate: (values) => {
14 const errors: Record<string, string> = {}
15 if (!values.name) {
16 errors.name = 'Name is required'
17 }
18 return errors
19 },
20 onSubmit: async (values) => {
21 grecaptcha.ready(() => {
22 grecaptcha
23 .execute(PUBLIC_RECAPTCHA_SITE_KEY, { action: 'submit' })
24 .then(async (token) => {
25 values.googleReCaptchaToken = token
26
27 const formData = new FormData()
28 Object.entries(values).forEach(([key, value]) => {
29 formData.append(key, value)
30 })
31
32 await sendRequest(formData)
33 })
34 })
35
36 const sendRequest = async (formData: FormData) => {
37 await fetch(PUBLIC_NEWT_FORM_ENDPOINT, {
38 method: 'POST',
39 body: formData,
40 headers: {
41 Accept: 'application/json'
42 }
43 })
44 }
45 }
46 })
47</script>
48
49<svelte:head>
50 <title>Newt・SvelteKitフォーム</title>
51 <meta name="description" content="NewtとSvelteKitを利用した問い合わせフォームです" />
52 <script
53 src="https://www.google.com/recaptcha/api.js?render={PUBLIC_RECAPTCHA_SITE_KEY}&hl=ja"
54 async
55 defer
56 ></script>
57</svelte:head>
58
59<div>
60 <h1>Contact us</h1>
61 <form use:form>
62 <label for="name">Name*</label>
63 <input id="name" name="name" aria-describedby="error-name-required" />
64 {#if $errors.name}
65 <span id="error-name-required" aria-live="assertive">
66 {$errors.name}
67 </span>
68 {/if}
69 <label for="email">Email</label>
70 <input id="email" name="email" type="email" />
71 <label for="message">Message</label>
72 <textarea id="message" name="message" />
73 <button type="submit">Submit</button>
74 </form>
75</div>
以下のことを行っています。
googleReCaptchaToken
というフィールド名でトークンを送信します(他の名前で送信した場合、正しく動作しないので注意してください)- サイトキーを使用して、reCAPTCHAのJavaScript APIを読み込みます
- reCAPTCHAのJavaScript APIの読み込みでは
hl
クエリにja
を設定しているため、問い合わせフォームの右下に表示されるreCAPTCHAのバッジが日本語になります。他の言語で設定したい方は、Googleの Language Codes のドキュメントを参考に設定してください
これでGoogle reCAPTCHA v3によるスパム対策が実装できました。
もし、バッジを非表示にしたい場合、.grecaptcha-badge { visibility: hidden; }
を適用すると非表示になりますが、この操作はGoogle公式のガイドに従った場合のみ許可されます。
詳細は、Googleの reCAPTCHA バッジを非表示にします。どうすればよいですか? のドキュメントをご確認ください。
6. リダイレクトをカスタマイズする
最後にフォーム送信後のリダイレクトをカスタマイズします。
送信が成功した場合のページと、失敗した場合のページを用意して、送信結果に応じてリダイレクトしてみましょう。
6-1. 送信成功ページ・送信失敗ページを用意する
送信成功ページとして、以下を用意します。
src/routes/thanks/+page.svelte
を作成します。
1<svelte:head>
2 <title>Thank you</title>
3 <meta name="description" content="問い合わせの送信が成功しました" />
4</svelte:head>
5
6<div class="Result">
7 <h1>Thank you!</h1>
8 <div>
9 <a href="/contact">Back to Previous Page</a>
10 </div>
11</div>
送信失敗ページとして、以下を用意します。
src/routes/error/+page.svelte
を作成します。
1<svelte:head>
2 <title>Error</title>
3 <meta name="description" content="問い合わせの送信が失敗しました" />
4</svelte:head>
5
6<div class="Result">
7 <h1>Error!</h1>
8 <div>
9 <a href="/contact">Back to Previous Page</a>
10 </div>
11</div>
6-2. リダイレクトを設定する
src/routes/contact/+page.svelte
を以下のように修正します。
import { goto } from '$app/navigation'
import { PUBLIC_NEWT_FORM_ENDPOINT, PUBLIC_RECAPTCHA_SITE_KEY } from '$env/static/public'
import { createForm } from 'felte'
(省略)
onSubmit: async (values) => {
grecaptcha.ready(() => {
grecaptcha
.execute(PUBLIC_RECAPTCHA_SITE_KEY, { action: 'submit' })
.then(async (token) => {
values.googleReCaptchaToken = token
const formData = new FormData()
Object.entries(values).forEach(([key, value]) => {
formData.append(key, value)
})
await sendRequest(formData)
try {
const response = await sendRequest(formData)
if (response.ok) {
goto('/thanks')
} else {
goto('/error')
}
} catch (err) {
goto('/error')
}
})
})
(省略)
送信が成功した場合(response.ok がtrueの場合)は goto を利用して /thanks
にリダイレクトし、送信成功ページを表示しています。
送信が失敗した場合(response.ok がfalseの場合、またはエラーが発生した場合)は /error
にリダイレクトし、送信失敗ページを表示しています。
これでリダイレクトをカスタマイズすることができました。
以上で、すべてのステップが終了となります。
Form Appでは、他にもさまざまな機能があるので、興味のある方はぜひ以下のドキュメントもご覧ください。