← ブログ

どの国でも使える住所フォームを React で作る方法

国のドロップダウンを付けたアメリカの住所フォームは国際対応ではありません。React で作る国ごとのフィールド、郵便番号、自動入力トークン、住所検索、エラー。

Preet Suthar · · 6分で読めます

  • React
  • Forms
  • E-commerce
  • Accessibility

ほとんどの住所フォームは、上に国のドロップダウンを付けたアメリカの住所フォームです。誰に対しても、都市、州、ZIP コードがこの順序で並びます。するとロンドンの買い物客には選ぶ州がなく、ベルリンの買い物客は習慣で都市より先に郵便番号を入力し、東京の買い物客は住所を逆順に書かなければなりません。

住所フォームはほとんどのストアで最も長いフォームで、自動入力、バリデーション、海外のユーザーがすべて集まる場所です。ここでは、配送先のどの国でも機能する住所フォームを React で作る方法を紹介します。

まず国、それからレイアウト

どのフィールドが存在し、何と呼ばれ、どれが必須で、どの順序で並ぶかは国が決めます。だからまず国を尋ね、買い物客のロケールや IP から事前選択しておき、残りはデータから組み立てます。

どれほど違うかの例をいくつか:

国番地以降のフィールド地域郵便番号
アメリカ都市、州、ZIP コード必須12345 または 12345-6789
イギリス町または都市、郵便番号なしSW1A 1AA のような形式
ドイツ郵便番号、都市なし5 桁
日本郵便番号、都道府県、市区町村必須123-4567
カナダ都市、州、郵便番号必須K1A 0B1 のような形式

すべての国を手作業で調べる必要はありません。Google は libaddressinput ライブラリの裏にある住所メタデータを公開していて、世界のほとんどの国についてフィールドの順序、ラベル、必須フィールド、郵便番号のパターンがわかります。配送の多い国を選んで正確なフォーマットを与え、それ以外のすべての国にはシンプルなフォールバックを与えます。住所 2 行、都市、地域、郵便番号で、どれも厳密には検証しません。

type Field = "address1" | "address2" | "city" | "region" | "postalCode"

type CountryFormat = {
  rows: Field[][]
  labels: Partial<Record<Field, string>>
  required: Field[]
  postalCode?: { pattern: RegExp; example: string }
  regions?: { code: string; name: string }[]
}

const countryFormats: Record<string, CountryFormat> = {
  US: {
    rows: [["address1"], ["address2"], ["city", "region", "postalCode"]],
    labels: { region: "State", postalCode: "ZIP code" },
    required: ["address1", "city", "region", "postalCode"],
    postalCode: { pattern: /^\d{5}(-\d{4})?$/, example: "94110" },
  },
  GB: {
    rows: [["address1"], ["address2"], ["city"], ["postalCode"]],
    labels: { city: "Town or city", postalCode: "Postcode" },
    required: ["address1", "city", "postalCode"],
    postalCode: {
      pattern: /^[A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}$/i,
      example: "SW1A 1AA",
    },
  },
}

フォームコンポーネントは format.rows をレンダリングするだけで、国名には一切触れません。

フィールドを減らす

表示するフィールドが 1 つ増えるたびに、入力することが 1 つ増え、間違えることも 1 つ増えます。

  • 名前のフィールドは 1 つ。 姓と名ではなく「氏名」。2 つに分けられない名前はたくさんありますし、順序も国によって違います。
  • 2 行目はリンクの奥に。 「部屋番号・建物名などを追加」で開きます。ほとんどの人には不要です。自動入力や住所検索で埋まった場合は表示します。
  • 会社名もリンクの奥に。数少ない B2B の注文のために。
  • 電話番号は任意にし、理由を添える。 フィールドの下に「配送に関するお問い合わせにのみ使用します」と書くほうが、必須フィールドにするより本物の番号が多く集まります。

仕事の大半は自動入力がやってくれる

ブラウザの自動入力はワンタップでフォーム全体を埋められますが、それはすべての入力に name と正しい autocomplete トークンがある場合だけです。トークンには shipping か billing を前置して、保存済みのどの住所をどこに入れるかをブラウザに伝えます。

<Input name="name" autoComplete="shipping name" />
<Input name="address1" autoComplete="shipping address-line1" />
<Input name="address2" autoComplete="shipping address-line2" />
<Input name="city" autoComplete="shipping address-level2" />
<Input name="region" autoComplete="shipping address-level1" />
<Input
  name="postalCode"
  autoComplete="shipping postal-code"
  inputMode={country === "US" ? "numeric" : "text"}
/>
<Input name="phone" type="tel" autoComplete="shipping tel" />

address-level1 は州、県、都道府県で、address-level2 は都市です。請求先が配送先と異なる場合は billing トークンを使った 2 つ目のフォームをレンダリングし、ブラウザが請求先フォームに配送先の住所を入れないようにします。

郵便番号に type="number" は使わないでください。ZIP コードはゼロで始まることがあり、イギリスやカナダの郵便番号には文字が含まれ、number 入力にはスピナーやスクロールホイールでの値の変化が付いてきます。テキスト入力を使い、inputMode="numeric" は郵便番号が数字だけの国にだけ使います。

郵便番号からの住所検索

国によっては、郵便番号だけで市区町村と地域がわかります。典型的なのが日本です。住所は郵便番号から始まり、番号だけで都道府県と市区町村が特定できます。買い物客が郵便番号を入力し終えたら検索して市区町村と地域を埋めますが、すでに入力されたものは決して上書きしないでください。あなたのデータより本人の入力が優先です。

住所の書き出しを入力して候補を選ぶ住所検索は、モバイルではどんなフォームよりも速い方法です。Google Places も Mapbox も 2 つの呼び出しとして提供しています。入力された内容から住所を提案するものと、選ばれた候補を構造化されたフィールドに変換するものです。

組み込むときのルールをいくつか:

  • 他の検索ボックスと同じく、入力が止まるのを待ち、前のリクエストは中止します。
  • リストの最後には必ず「住所を手動で入力」を置きます。新しい通りかもしれませんし、候補が間違っているかもしれません。
  • 選ばれたらフィールドを埋めて表示し、買い物客が結果を確認して部屋番号を追加できるようにします。
  • Google の Places API は、1 回の検索における候補と詳細の呼び出しをセッショントークンでまとめます。使わないと、呼び出しが別々に課金されることがあります。

何が間違っているかを伝えるエラー

フィールドは買い物客がそこを離れたときに検証し、その後は直るまで入力に合わせてリアルタイムに検証します。エラーはその国に合わせて書きます。アメリカなら「5 桁の ZIP コードを入力してください」、イギリスなら「SW1A 1AA のような郵便番号を入力してください」。「無効な郵便番号です」では誰の役にも立ちません。

送信時は、フォームの上部のまとめにすべての問題を列挙し、それぞれをフィールドにリンクして、まとめにフォーカスを移します。各エラーは aria-describedby で入力に結び付け、aria-invalid を設定して、スクリーンリーダーがフィールドと一緒にメッセージを読み上げるようにします。

検証の前に正規化します。空白を取り除き、イギリスとカナダの郵便番号は大文字にし、スペースの有無にかかわらず受け付けます。直せるのに「sw1a1aa」を拒否するのは、買い物客ではなくあなたのバグです。

国の切り替え

住所を半分入力した後で国を変える人は、思っているより多くいます。そのときは、まだ使えるもの(名前、番地の行、電話番号)は残し、新しい国で地域のリストが違うなら地域をクリアし、すでに触れたフィールドだけを再チェックします。まだたどり着いていないフィールドに新しいエラーを出すと、やってもいないことで叱られているように感じます。

文字列ではなく構造を保存する

住所は整形済みの行ではなく、フィールドと ISO 国コードとして保存します。税、送料、配送ラベルにはその構造が必要になりますし、表示用の整形はいつでもできます。「12 Mission St, San Francisco, CA 94110」のような 1 行のまとめはデータの見え方の 1 つであって、データそのものではありません。

チェックリスト

  • まず国、それから国ごとのフォーマットに従ったフィールド、ラベル、順序。
  • 主要な国には正確なフォーマットを、それ以外には寛容なフォールバックを。
  • 名前のフィールドは 1 つ、2 行目と会社名はリンクの奥に、電話番号は理由を添えて任意に。
  • すべての入力に name と、shipping または billing 付きの autocomplete トークン。
  • 郵便番号にはテキスト入力、数字キーボードは郵便番号が数字の国だけ。
  • 入力済みの値を決して上書きしない郵便番号検索。
  • 住所検索、最後に手動入力の選択肢。
  • 国ごとのエラー文言と、送信時のリンク付きまとめ。

これを実装した HextaUI のブロック

HextaUI Pro の Address Form ブロックはこれらのルールに基づいて作られています。16 か国にはラベル、必須フィールド、地域リスト、郵便番号のルールを備えた専用のフォーマットがあり、それ以外のすべての国にはシンプルなフォールバックが用意されています。すべての入力には shipping または billing を前置した正しい autocomplete トークンがあります。onSearchAddress と onResolveAddress で住所検索をつなげられ、Google Places の例が付いています。同じ 2 つの呼び出しは Mapbox にも対応付けられます。onLookupPostalCode は入力済みのものを上書きせずに市区町村と地域を埋め、validate() はすべての問題をリンク付きのまとめに列挙します。SavedAddresses と AddressSummary は、サインイン済みの買い物客と、完了後の「配送先」の行をカバーします。

Checkout ブロックは配送にこれを使っています。そのページの残りを作っているなら、React と Next.js で作る 1 ページチェックアウト についても書きました。

よくある質問

国のフィールドは最初に置くべきですか? はい。どのフィールドが存在し、どの順序で並ぶかを決めるのは国なので、残りを意味のあるものにするには先に選ぶ必要があります。

姓と名のフィールドを使うべきですか? 配送業者や決済プロバイダーが分割を求めない限り、氏名のフィールドを 1 つにします。

郵便番号にはどの input type を使うべきですか? テキスト入力です。inputMode="numeric" は、郵便番号がすべて数字の国にだけ使います。

API による住所の検証は必要ですか? 役には立ちますが、それで注文をブロックしないでください。修正案を示し、買い物客が入力した内容のままにもできるようにします。