Contact Page — Form, Email, and Formspree Integration

Set up the Dax Pro contact page: Formspree integration for form submissions, email display, guarantee list, form field customization, and spam protection. Edit via Decap CMS or directly in content/contact.md.

What the Contact Page Does

The contact page is the conversion endpoint of your site. Every visitor who wants to ask a question, request a quote, or hire you lands here. Unlike other pages that are static content, the contact page has a working form that sends submissions somewhere — by default, to Formspree, a free form backend service that emails you each submission.

The contact page has two parts:

  • Left column — a headline, a subtext paragraph, a list of guarantees, and an email address
  • Right column — a form with configurable fields, submitting to a Formspree endpoint
  • Both parts are driven by content/contact.md and its frontmatter. The form submission URL is configured in the frontmatter, so you can change the destination without editing any template.

    The file is:

    content/contact.md
    

    This chapter covers:

    1. How the contact page is structured — what each part does

    2. Setting up Formspree — creating an endpoint and configuring the form

    3. Editing via Decap CMS — the recommended method for most users

    4. Editing directly — the developer method

    5. Customizing form fields — adding, removing, and configuring inputs

    6. Spam protection — honeypot field and other techniques

    7. Testing — confirming submissions arrive


    How the Contact Page Is Structured

    The contact page uses the contact.dax layout, which renders two columns inside a dark gradient box. The left column comes from the contact: section of the frontmatter; the right column comes from the contact: section too, but specifically from the contact.fields list.

    The frontmatter of content/contact.md looks like this:

    ---
    

    layout: "contact.dax"

    title: "Enterprise Consultation & Support Desk"

    breadcrumb: "Contact"

    description: "Discuss your .NET static architecture requirements, Decap CMS integrations, or procurement questions directly with our technical team."

    contact:

    kicker: "Direct Technical Channel"

    heading: "Speak With a .NET Architecture Specialist"

    subtext: "We provide hands-on architectural assessments for teams migrating from WordPress, Drupal, Sitecore, or Umbraco to pure .NET static generation with Decap CMS."

    guarantees:

    - text: "Guaranteed response time: Within 4 business hours"

    - text: "Direct Slack / Microsoft Teams channel for Corporate Pro tiers"

    - text: "Confidentiality: Mutual NDA provided upon request"

    email_label: "Primary Communications:"

    email: "[email protected]"

    hours: "Operating Hours: 08:00 – 18:00 UTC (Monday – Friday)"

    form_id: "dax-contact-page-form"

    form_action: "https://formspree.io/f/mnqkqkve"

    field1_id: "contact-fullname"

    field1_name: "name"

    field1_label: "Full Name"

    field1_type: "text"

    field1_placeholder: "e.g. David Sterling"

    field1_required: true

    field2_id: "contact-email-addr"

    field2_name: "email"

    field2_label: "Work Email"

    field2_type: "email"

    field2_placeholder: "[email protected]"

    field2_required: true

    field3_id: "contact-interest"

    field3_name: "tier"

    field3_label: "Inquiry Purpose"

    field3_type: "select"

    field3_opt1_value: "licensing"

    field3_opt1_label: "Commercial Theme Licensing"

    field3_opt2_value: "migration"

    field3_opt2_label: "CMS Migration to .NET SSG"

    field3_opt3_value: "decap"

    field3_opt3_label: "Decap CMS Customization"

    field3_opt4_value: "azure"

    field3_opt4_label: "Azure Static Web Apps Deployment"

    field3_opt5_value: "custom"

    field3_opt5_label: "Other Technical Request"

    field4_id: "contact-details"

    field4_name: "message"

    field4_label: "Project Details"

    field4_type: "textarea"

    field4_placeholder: "Describe your existing tech stack, timeline, or particular customization requirements..."

    submit: "Submit Enterprise Request"


    Every field explained:

    Field Purpose
    layout Always contact.dax. Do not change.
    title Page title for </code> and H1</td> <p></tr></p> <tr> <td><code>breadcrumb</code></td> <td>Short label for breadcrumb</td> <p></tr></p> <tr> <td><code>description</code></td> <td>Meta description for SEO</td> <p></tr></p> <tr> <td><code>contact.kicker</code></td> <td>Small uppercase label above the heading</td> <p></tr></p> <tr> <td><code>contact.heading</code></td> <td>The large heading in the left column</td> <p></tr></p> <tr> <td><code>contact.subtext</code></td> <td>Paragraph below the heading</td> <p></tr></p> <tr> <td><code>contact.guarantees</code></td> <td>List of three bullet points</td> <p></tr></p> <tr> <td><code>contact.email_label</code></td> <td>Small label above the email</td> <p></tr></p> <tr> <td><code>contact.email</code></td> <td>The email address shown</td> <p></tr></p> <tr> <td><code>contact.hours</code></td> <td>Optional operating hours line</td> <p></tr></p> <tr> <td><code>contact.form_id</code></td> <td>HTML id for the form element</td> <p></tr></p> <tr> <td><code>contact.form_action</code></td> <td>URL where form submissions are POSTed</td> <p></tr></p> <tr> <td><code>contact.field1_<em></code> through <code>contact.field4_</em></code></td> <td>The form fields</td> <p></tr></p> <tr> <td><code>contact.submit</code></td> <td>Submit button label</td> <p></tr></p> <p></tbody></table></div></p> <p><strong>The two most important fields are <code>form_action</code> and <code>form_id</code>.</strong> The first determines where submissions go; the second is just an HTML identifier (used for styling and potential JavaScript hooks). Change <code>form_action</code> to point at your own Formspree endpoint.</p> <hr/> <h2>Setting Up Formspree</h2> <p>The default <code>form_action</code> in <code>content/contact.md</code> points at a demo Formspree endpoint (<code>https://formspree.io/f/mnqkqkve</code>). That endpoint is not yours. Submissions go to the Dax Pro team, not to you.</p> <p>You must replace it with your own Formspree endpoint. The process takes about five minutes.</p> <h3>Step 1: Create a Formspree Account</h3> <p>1. Open <a href="https://formspree.io/">formspree.io</a> in a browser</p> <p>2. Click <strong>Sign Up</strong> in the top-right corner</p> <p>3. Enter your email address and a password</p> <p>4. Confirm your email if Formspree sends a verification link</p> <p>5. After login, you land on the Formspree dashboard</p> <h3>Step 2: Create a New Form</h3> <p>1. In the dashboard, click <strong>New Form</strong> (or <strong>+ New Form</strong> depending on the layout)</p> <p>2. Enter a name for the form — something like <code>Dax Pro Contact Form</code> or <code>Contact Requests</code></p> <p>3. Choose the plan. The <strong>Free plan</strong> allows <strong>50 submissions per month</strong>, which is enough for most small sites. If you expect more, choose <strong>Gold</strong> ($10/month for 1,000 submissions).</p> <p>4. Confirm the form</p> <h3>Step 3: Get Your Endpoint URL</h3> <p>After creating the form, Formspree shows you the endpoint URL. It looks like:</p> <pre><code>https://formspree.io/f/xyzabcd <p></code></pre></p> <p>The last part (<code>xyzabcd</code>) is your form ID. This is what you put in <code>contact.form_action</code>.</p> <p>Copy the full URL.</p> <h3>Step 4: Configure Where Emails Go</h3> <p>1. In the Formspree dashboard, open your form</p> <p>2. Go to <strong>Settings → Emails</strong></p> <p>3. Add the email address where you want submissions delivered</p> <p>4. If you want a copy sent to the person who submitted the form (a confirmation email), enable <strong>Auto-Responder</strong> and fill in the reply template</p> <p>5. Save the settings</p> <h3>Step 5: Add Your Domain (Recommended)</h3> <p>Formspree requires that submissions come from a domain you have verified. Otherwise, submissions are marked as "unverified" and may be rate-limited or blocked.</p> <p>1. In the form settings, go to <strong>Settings → Domains</strong></p> <p>2. Click <strong>Add Domain</strong></p> <p>3. Enter your production domain (e.g., <code>example.com</code>, without <code>www.</code> or <code>https://</code>)</p> <p>4. Formspree will show DNS or HTML verification instructions</p> <p><strong>DNS verification (recommended):</strong></p> <li>Formspree provides a TXT record</li> <li>Add it to your DNS provider (Cloudflare, Namecheap, GoDaddy, etc.)</li> <li>Wait 5–30 minutes for propagation</li> <li>Return to Formspree and click <strong>Verify</strong></li> <p><strong>HTML verification (alternative):</strong></p> <li>Formspree provides an HTML snippet</li> <li>You can add it to your site's <code><head></code> temporarily</li> <li>This only works after your site is deployed</li> <li>Not practical during initial setup</li> <p>If you skip domain verification, the form still works — submissions arrive — but Formspree marks them as "unverified" in the dashboard. For low-traffic sites this is fine. For production sites, verify the domain.</p> <h3>Step 6: Update <code>contact.form_action</code></h3> <p>Now update your site's contact page with your new Formspree endpoint.</p> <p><strong>Via Decap CMS:</strong></p> <p>1. Open <code>https://yoursite.com/admin/</code></p> <p>2. Click <strong>✉ Contact Page</strong></p> <p>3. Click <strong>Contact</strong></p> <p>4. Scroll to <strong>Form Action</strong></p> <p>5. Replace the URL with your Formspree endpoint (<code>https://formspree.io/f/xyzabcd</code>)</p> <p>6. Scroll to <strong>Email</strong></p> <p>7. Replace <code>creativebydre@gmail.com</code> with your own email</p> <p>8. Click <strong>Publish</strong></p> <p><strong>Directly in <code>content/contact.md</code>:</strong></p> <pre><code> form_action: "https://formspree.io/f/xyzabcd" <p> email: "hello@yourdomain.com"</p> <p></code></pre></p> <p>Save the file and rebuild.</p> <h3>Step 7: Test a Submission</h3> <p>After the change is live:</p> <p>1. Open <code>https://yoursite.com/contact/</code> (or <code>http://localhost:8080/contact/</code> if you are testing locally, and note that local submissions go directly to Formspree — the endpoint is not domain-restricted if you have not set up domain verification)</p> <p>2. Fill in the form with test data</p> <p>3. Click <strong>Submit Enterprise Request</strong></p> <p>4. Wait a few seconds</p> <p>5. Check the email inbox associated with your Formspree account</p> <p>6. If the email arrives, the form is working</p> <p>7. If it does not arrive, check the Formspree dashboard — submissions appear in <strong>Submissions</strong> tab, and any errors show there</p> <p><strong>Common issues during test:</strong></p> <li><strong>"Failed to fetch"</strong> — the endpoint URL is wrong (typo) or the domain is not verified</li> <li><strong>"Network error"</strong> — you are testing on <code>localhost</code> and the browser blocks the request due to CORS; test on the deployed site instead, or temporarily allow <code>localhost</code> in Formspree's allowed origins</li> <li><strong>Email does not arrive</strong> — check spam, check the Formspree dashboard to see whether the submission was received, and confirm the recipient address in Formspree's email settings</li> <hr/> <h2>Method 1 — Editing the Contact Page via Decap CMS</h2> <h3>Accessing the Contact Page</h3> <p>1. Open <code>https://yoursite.com/admin/</code> and log in</p> <p>2. Click <strong>✉ Contact Page</strong> in the left sidebar</p> <p>3. Click <strong>Contact</strong></p> <p>4. The editor opens</p> <h3>Editing the Header and Left Column</h3> <p>The left column shows the kicker, heading, subtext, guarantees list, email, and hours.</p> <p><strong>Fields:</strong></p> <div class="table-responsive"><table> <thead><tr> <th>Field</th> <th>Default</th> <th>Recommended</th> <p></tr></thead></p> <tbody> <tr> <td><strong>Title</strong></td> <td><code>Enterprise Consultation & Support Desk</code></td> <td>Your own page title</td> <p></tr></p> <tr> <td><strong>Breadcrumb</strong></td> <td><code>Contact</code></td> <td>Usually <code>Contact</code></td> <p></tr></p> <tr> <td><strong>Description</strong></td> <td><code>Discuss your .NET static architecture...</code></td> <td>One sentence describing what visitors can ask about</td> <p></tr></p> <tr> <td><strong>Contact → Kicker</strong></td> <td><code>Direct Technical Channel</code></td> <td>Small label above the heading</td> <p></tr></p> <tr> <td><strong>Contact → Heading</strong></td> <td><code>Speak With a .NET Architecture Specialist</code></td> <td>Your main heading</td> <p></tr></p> <tr> <td><strong>Contact → Subtext</strong></td> <td><code>We provide hands-on architectural assessments...</code></td> <td>One paragraph explaining what happens after they submit</td> <p></tr></p> <tr> <td><strong>Contact → Guarantees</strong></td> <td>Three text bullets</td> <td>Replace with your own (or reduce to one)</td> <p></tr></p> <tr> <td><strong>Contact → Email Label</strong></td> <td><code>Primary Communications:</code></td> <td>Small label above the email</td> <p></tr></p> <tr> <td><strong>Contact → Email</strong></td> <td><code>creativebydre@gmail.com</code></td> <td>Your actual email</td> <p></tr></p> <tr> <td><strong>Contact → Hours</strong></td> <td><code>Operating Hours: 08:00 – 18:00 UTC...</code></td> <td>Your hours, or leave empty</td> <p></tr></p> <p></tbody></table></div></p> <p><strong>Guarantees</strong> — the list of three bullet points. To edit:</p> <p>1. Expand <strong>Guarantees</strong> in the editor</p> <p>2. Each item has a <strong>Text</strong> field</p> <p>3. Edit each text</p> <p>4. To add a fourth guarantee, click <strong>Add Guarantees</strong></p> <p>5. To remove one, hover and click the trash icon</p> <p>A single guarantee ("We reply within 24 hours") is often better than three. Choose based on what is true.</p> <p><strong>Hours</strong> — optional. If you do not have set hours, clear the field. The line is hidden when empty.</p> <h3>Editing the Form Action and ID</h3> <div class="table-responsive"><table> <thead><tr> <th>Field</th> <th>Purpose</th> <p></tr></thead></p> <tbody> <tr> <td><strong>Form ID</strong></td> <td>HTML id attribute. Default: <code>dax-contact-page-form</code>. Leave as-is unless you have a specific reason to change it.</td> <p></tr></p> <tr> <td><strong>Form Action</strong></td> <td>URL where the form posts. This is your Formspree endpoint.</td> <p></tr></p> <p></tbody></table></div></p> <p><strong>To update form action:</strong></p> <p>1. Find the <strong>Form Action</strong> field</p> <p>2. Paste your Formspree endpoint (<code>https://formspree.io/f/your-id</code>)</p> <p>3. Click <strong>Publish</strong></p> <h3>Editing Form Fields</h3> <p>The form fields are numbered <code>field1</code> through <code>field4</code>. Each has several sub-fields.</p> <p><strong>Field 1 — Full Name (default)</strong></p> <div class="table-responsive"><table> <thead><tr> <th>Sub-field</th> <th>Default</th> <th>Notes</th> <p></tr></thead></p> <tbody> <tr> <td><strong>Field 1 ID</strong></td> <td><code>contact-fullname</code></td> <td>HTML id. Unique.</td> <p></tr></p> <tr> <td><strong>Field 1 Name</strong></td> <td><code>name</code></td> <td>HTML name attribute. Used in submission.</td> <p></tr></p> <tr> <td><strong>Field 1 Label</strong></td> <td><code>Full Name</code></td> <td>Visible label</td> <p></tr></p> <tr> <td><strong>Field 1 Type</strong></td> <td><code>text</code></td> <td>HTML input type</td> <p></tr></p> <tr> <td><strong>Field 1 Placeholder</strong></td> <td><code>e.g. David Sterling</code></td> <td>Placeholder text</td> <p></tr></p> <tr> <td><strong>Field 1 Required</strong></td> <td><code>true</code></td> <td>Whether the field is required</td> <p></tr></p> <p></tbody></table></div></p> <p><strong>Field 2 — Work Email (default)</strong></p> <p>Similar to Field 1 but with <code>type: "email"</code> and a different placeholder.</p> <p><strong>Field 3 — Inquiry Purpose (default, a select dropdown)</strong></p> <p>This field uses <code>type: "select"</code> and has options:</p> <div class="table-responsive"><table> <thead><tr> <th>Sub-field</th> <th>Default</th> <p></tr></thead></p> <tbody> <tr> <td><strong>Field 3 ID</strong></td> <td><code>contact-interest</code></td> <p></tr></p> <tr> <td><strong>Field 3 Name</strong></td> <td><code>tier</code></td> <p></tr></p> <tr> <td><strong>Field 3 Label</strong></td> <td><code>Inquiry Purpose</code></td> <p></tr></p> <tr> <td><strong>Field 3 Type</strong></td> <td><code>select</code></td> <p></tr></p> <tr> <td><strong>Field 3 Opt 1 Value</strong></td> <td><code>licensing</code></td> <p></tr></p> <tr> <td><strong>Field 3 Opt 1 Label</strong></td> <td><code>Commercial Theme Licensing</code></td> <p></tr></p> <tr> <td><strong>Field 3 Opt 2 Value</strong></td> <td><code>migration</code></td> <p></tr></p> <tr> <td><strong>Field 3 Opt 2 Label</strong></td> <td><code>CMS Migration to .NET SSG</code></td> <p></tr></p> <tr> <td><strong>Field 3 Opt 3 Value</strong></td> <td><code>decap</code></td> <p></tr></p> <tr> <td><strong>Field 3 Opt 3 Label</strong></td> <td><code>Decap CMS Customization</code></td> <p></tr></p> <tr> <td><strong>Field 3 Opt 4 Value</strong></td> <td><code>azure</code></td> <p></tr></p> <tr> <td><strong>Field 3 Opt 4 Label</strong></td> <td><code>Azure Static Web Apps Deployment</code></td> <p></tr></p> <tr> <td><strong>Field 3 Opt 5 Value</strong></td> <td><code>custom</code></td> <p></tr></p> <tr> <td><strong>Field 3 Opt 5 Label</strong></td> <td><code>Other Technical Request</code></td> <p></tr></p> <p></tbody></table></div></p> <p><strong>The <code>value</code> is what gets submitted; the <code>label</code> is what the user sees.</strong> For example, if a user selects "Commercial Theme Licensing," Formspree receives <code>tier: licensing</code>.</p> <p><strong>Field 4 — Project Details (default, a textarea)</strong></p> <div class="table-responsive"><table> <thead><tr> <th>Sub-field</th> <th>Default</th> <p></tr></thead></p> <tbody> <tr> <td><strong>Field 4 ID</strong></td> <td><code>contact-details</code></td> <p></tr></p> <tr> <td><strong>Field 4 Name</strong></td> <td><code>message</code></td> <p></tr></p> <tr> <td><strong>Field 4 Label</strong></td> <td><code>Project Details</code></td> <p></tr></p> <tr> <td><strong>Field 4 Type</strong></td> <td><code>textarea</code></td> <p></tr></p> <tr> <td><strong>Field 4 Placeholder</strong></td> <td><code>Describe your existing tech stack, timeline...</code></td> <p></tr></p> <p></tbody></table></div></p> <h3>Editing the Submit Button</h3> <div class="table-responsive"><table> <thead><tr> <th>Field</th> <th>Default</th> <p></tr></thead></p> <tbody> <tr> <td><strong>Submit</strong></td> <td><code>Submit Enterprise Request</code></td> <p></tr></p> <p></tbody></table></div></p> <p>Change to "Send Message", "Contact Us", or "Request a Quote" to suit your brand.</p> <h3>Publishing</h3> <p>After editing any field, click <strong>Publish</strong> in the top-right corner. Decap CMS commits the change, the host rebuilds, and the new contact page is live in 30–60 seconds.</p> <hr/> <h2>Method 2 — Editing the Contact Page Directly</h2> <p>For developers, editing <code>content/contact.md</code> directly in VS Code is faster than the CMS.</p> <h3>Opening the File</h3> <pre><code>code content/contact.md <p></code></pre></p> <h3>Editing Specific Fields</h3> <p><strong>Change the email address:</strong></p> <pre><code> email: "hello@yourdomain.com" <p></code></pre></p> <p><strong>Change the Formspree endpoint:</strong></p> <pre><code> form_action: "https://formspree.io/f/xyzabcd" <p></code></pre></p> <p><strong>Change the heading:</strong></p> <pre><code> heading: "Let's Talk About Your Project" <p></code></pre></p> <p><strong>Remove the hours line:</strong></p> <p>Delete the <code>hours:</code> line. The template hides the line when the field is missing or empty.</p> <p><strong>Change guarantees from three to two:</strong></p> <pre><code> guarantees: <p> - text: "We reply within one business day."</p> <p> - text: "All conversations are confidential."</p> <p></code></pre></p> <p>To add a fourth:</p> <pre><code> guarantees: <p> - text: "We reply within one business day."</p> <p> - text: "All conversations are confidential."</p> <p> - text: "No obligation to hire."</p> <p> - text: "Mutual NDA available on request."</p> <p></code></pre></p> <p><strong>Change a select field's options:</strong></p> <p>If you want the "Inquiry Purpose" dropdown to have different options, edit <code>field3_opt1_<em></code> through <code>field3_opt5_</em></code>. For example:</p> <pre><code> field3_opt1_value: "new-project" <p> field3_opt1_label: "Start a New Project"</p> <p> field3_opt2_value: "existing-project"</p> <p> field3_opt2_label: "Discuss an Existing Project"</p> <p> field3_opt3_value: "migration"</p> <p> field3_opt3_label: "Migrate from Another CMS"</p> <p> field3_opt4_value: "support"</p> <p> field3_opt4_label: "Technical Support"</p> <p> field3_opt5_value: "other"</p> <p> field3_opt5_label: "Something Else"</p> <p></code></pre></p> <p><strong>Remove a field entirely:</strong></p> <p>The template supports four fields. If you want only three, you can:</p> <li><strong>Option 1:</strong> Delete <code>field4_*</code> fields. The template checks whether each field is defined.</li> <li><strong>Option 2:</strong> Set <code>field4_id</code> to an empty string (<code>""</code>). The template skips empty fields.</li> <p>Option 1 is cleaner. Delete all <code>field4_id</code>, <code>field4_name</code>, <code>field4_label</code>, <code>field4_type</code>, <code>field4_placeholder</code>.</p> <p><strong>Add a fifth field:</strong></p> <p>This requires editing the template (<code>templates/partials/contact/box.dax</code>) to render field 5. Not covered in this chapter. For most sites, four fields are enough.</p> <h3>Rebuilding After Direct Edits</h3> <pre><code>dotnet run -- build <p></code></pre></p> <p>Or, if the development server is running:</p> <pre><code>dotnet run -- start <p></code></pre></p> <p>The server rebuilds automatically. Refresh the browser to see the change.</p> <p>For deployed sites:</p> <pre><code>git add content/contact.md <p>git commit -m "Update contact form endpoint"</p> <p>git push</p> <p></code></pre></p> <hr/> <h2>Spam Protection</h2> <p>Public contact forms attract spam. Formspree includes built-in spam filtering (Akismet and reCAPTCHA-style checks), but there are additional steps you can take.</p> <h3>The Honeypot Field (Already Included)</h3> <p>Dax Pro's contact form includes a honeypot field named <code>_gotcha</code>:</p> <pre><code><input type="text" name="_gotcha" style="display:none" tabindex="-1" autocomplete="off"> <p></code></pre></p> <p>This field is invisible to human users (via <code>display:none</code>), but bots that auto-fill every field will fill it. Formspree rejects submissions where <code>_gotcha</code> is not empty.</p> <p>You do not need to do anything to enable this — it is already in the template. Do not remove it.</p> <h3>Formspree's Built-In Protection</h3> <p>Formspree applies multiple anti-spam measures automatically:</p> <li><strong>Akismet</strong> — a machine learning spam filter</li> <li><strong>reCAPTCHA v3</strong> — invisible challenge for suspicious submissions</li> <li><strong>Rate limiting</strong> — limits submissions per IP per hour</li> <li><strong>Domain verification</strong> — rejects submissions from unverified origins (if you enabled domain verification)</li> <p>The free plan includes these protections. No configuration is needed.</p> <h3>Optional: Add reCAPTCHA</h3> <p>If spam becomes a problem, Formspree supports explicit reCAPTCHA v2 (the "I'm not a robot" checkbox). To enable:</p> <p>1. In Formspree's form settings, go to <strong>Spam</strong></p> <p>2. Enable <strong>reCAPTCHA</strong></p> <p>3. Formspree shows a <code><script></code> tag and a <code><div></code> element</p> <p>To add reCAPTCHA to your Dax Pro form, you would need to edit <code>templates/partials/contact/box.dax</code> to include the required HTML and JavaScript. This is a developer task — see the Developer Guide.</p> <p>For most sites, the default honeypot plus Formspree's automatic filtering is enough.</p> <h3>Optional: Filter Submissions in Formspree</h3> <p>Formspree has a "Rules" feature that lets you auto-delete submissions matching certain patterns:</p> <p>1. In Formspree, go to <strong>Settings → Rules</strong></p> <p>2. Add a rule for messages containing certain spam keywords</p> <p>3. Choose to mark as spam or auto-delete</p> <p>Use sparingly — aggressive rules may catch legitimate inquiries.</p> <hr/> <h2>Testing the Contact Form</h2> <p>You should test the form at least three times:</p> <p><strong>Test 1: Verify the endpoint works.</strong></p> <p>1. Fill in the form with obvious test data (name: "Test User", email: "test@example.com", message: "This is a test submission")</p> <p>2. Click submit</p> <p>3. Wait a few seconds</p> <p>4. Check the Formspree dashboard — the submission should appear under <strong>Submissions</strong></p> <p>5. Check the destination email — the notification should arrive</p> <p>If the submission does not appear in Formspree, the endpoint URL in <code>contact.form_action</code> is wrong. Re-copy it from Formspree.</p> <p><strong>Test 2: Verify required fields.</strong></p> <p>1. Leave the name field empty</p> <p>2. Fill in the other fields</p> <p>3. Click submit</p> <p>4. The browser should show a "Please fill in this field" message next to the name field</p> <p>5. The form should not submit</p> <p>This confirms that <code>field1_required: true</code> is working.</p> <p><strong>Test 3: Verify required email format.</strong></p> <p>1. Fill in the email field with invalid data (e.g., <code>notanemail</code>)</p> <p>2. Fill in the other fields</p> <p>3. Click submit</p> <p>4. The browser should show "Please enter an email address" or similar</p> <p>5. The form should not submit</p> <p>This confirms the <code>type: "email"</code> validation is working.</p> <p><strong>Test 4: Verify the honeypot.</strong></p> <p>1. Open the form in a browser</p> <p>2. Right-click the form and choose <strong>Inspect</strong> (or <strong>Inspect Element</strong>)</p> <p>3. Find the hidden input: <code><input type="text" name="_gotcha"...></code></p> <p>4. Use the DevTools to remove the <code>style="display:none"</code> attribute</p> <p>5. Fill in all fields <strong>including</strong> the <code>_gotcha</code> field</p> <p>6. Submit the form</p> <p>7. Formspree should reject the submission as spam</p> <p>If Formspree rejects the submission, the honeypot is working. Restore the page (refresh) after testing.</p> <hr/> <h2>Common Contact Page Mistakes</h2> <p><strong>Mistake 1: Leaving the demo Formspree endpoint.</strong></p> <p>The default <code>form_action: "https://formspree.io/f/mnqkqkve"</code> sends submissions to the Dax Pro team, not to you. Replace it with your own endpoint immediately.</p> <p><strong>Mistake 2: Not verifying the domain in Formspree.</strong></p> <p>Without domain verification, Formspree marks submissions as "unverified." They still arrive, but Formspree may apply stricter rate limits or reject some submissions. Verify your domain for production sites.</p> <p><strong>Mistake 3: Removing the <code>_gotcha</code> honeypot.</strong></p> <p>The honeypot is your first line of spam defense. Do not remove it. If you are editing the template, leave it in place.</p> <p><strong>Mistake 4: Using the same <code>field1_id</code> and <code>field2_id</code>.</strong></p> <p>Every field's <code>id</code> must be unique within the form. If you copy <code>contact-fullname</code> to another field, browsers produce undefined behavior (labels point to the wrong input).</p> <p><strong>Mistake 5: Mismatched select options.</strong></p> <p>If you edit <code>field3_opt1_label</code> but forget to update <code>field3_opt1_value</code>, the label shows one thing and the submission sends another. Both must be edited together.</p> <p><strong>Mistake 6: Setting <code>required: true</code> on the message field.</strong></p> <p>The message field (<code>field4</code>) is usually optional. If you make it required, some users abandon the form because they do not know what to write. Leave it optional.</p> <p><strong>Mistake 7: Forgetting to update the email in the template.</strong></p> <p>The contact page's template (<code>templates/partials/contact/box.dax</code>) uses <code>{{ contact.email }}</code>. Changing <code>contact.email</code> in the frontmatter updates the displayed email. But if you also hardcoded an email in the template (which you should not have), you must update both.</p> <p><strong>Mistake 8: Not testing before deploying.</strong></p> <p>Test submissions on the deployed site, not just locally. Local submissions may succeed while the deployed site fails (or vice versa) due to CORS, domain verification, or DNS issues.</p> <hr/> <h2>Verifying Your Contact Page</h2> <p>After editing, verify:</p> <p>1. <strong>URL works</strong> — navigate to <code>/contact/</code>. The page loads.</p> <p>2. <strong>Left column</strong> — kicker, heading, subtext, guarantees, email, and hours render correctly.</p> <p>3. <strong>Right column</strong> — all four form fields render with correct labels, placeholders, and required flags.</p> <p>4. <strong>Select field</strong> — clicking the "Inquiry Purpose" dropdown shows all five options.</p> <p>5. <strong>Submit button</strong> — labeled as configured.</p> <p>6. <strong>Form action</strong> — right-click a field, view source, confirm <code><form></code> has <code>action</code> attribute pointing to your Formspree endpoint.</p> <p>7. <strong>Test submission</strong> — submit valid data, confirm arrival in Formspree and inbox.</p> <p>8. <strong>Mobile</strong> — resize to mobile width. Columns stack vertically, form fields expand to full width, submit button is full width.</p> <p>If a submission fails or the email is wrong, check:</p> <li>Formspree dashboard for errors</li> <li>Formspree email settings for the destination address</li> <li>Domain verification status</li> <li><code>contact.form_action</code> in the frontmatter</li> <hr/> <h2>What You Have Now</h2> <p>After this chapter, you can:</p> <li>✅ Create a Formspree account and endpoint</li> <li>✅ Configure form submissions to go to your email</li> <li>✅ Verify your domain in Formspree</li> <li>✅ Edit contact page content via Decap CMS</li> <li>✅ Edit contact page content directly in <code>content/contact.md</code></li> <li>✅ Customize form fields (add, remove, reorder)</li> <li>✅ Customize select dropdown options</li> <li>✅ Understand spam protection (honeypot, Formspree's filters, optional reCAPTCHA)</li> <li>✅ Test submissions before deploying</li> <li>✅ Fix common form configuration problems</li> <p></ul>The contact page is done. The next chapter covers the <strong>Services Controller</strong> — the file that generates the <code>/services/</code> listing page and handles pagination for service entries.</p> <p>→ Continue to <a href="/docs/services-controller">Services Controller</a></p> </div> </div> </article> </main> <script> (function() { const docsToggle = document.getElementById('dax-docs-toggle'); const docsSidebar = document.querySelector('.dax-docs-sidebar'); const docsOverlay = document.getElementById('dax-docs-overlay'); if (!docsToggle || !docsSidebar) return; const closeDocs = () => { docsToggle.setAttribute('aria-expanded', 'false'); docsSidebar.classList.remove('open'); if (docsOverlay) docsOverlay.classList.remove('open'); document.body.classList.remove('dax-docs-open'); }; docsToggle.addEventListener('click', () => { const isOpen = docsSidebar.classList.contains('open'); if (isOpen) { closeDocs(); } else { docsToggle.setAttribute('aria-expanded', 'true'); docsSidebar.classList.add('open'); if (docsOverlay) docsOverlay.classList.add('open'); document.body.classList.add('dax-docs-open'); } }); if (docsOverlay) docsOverlay.addEventListener('click', closeDocs); document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeDocs(); }); document.querySelectorAll('.dax-docs-nav-link').forEach(link => { link.addEventListener('click', closeDocs); }); })(); </script> <footer class="dax-footer"> <div class="dax-container"> <div class="dax-footer-top"> <div class="dax-footer-brand-col"> <a href="/" class="dax-brand"> <span class="dax-brand-symbol">.NET</span> <span class="dax-brand-name" style="color: #FFFFFF;">DAX<span class="dax-brand-accent">PRO</span></span> </a> <p class="dax-footer-desc">The ultra-luxurious corporate theme designed for high-velocity Microsoft .NET Static Site Generators and Decap CMS. Powered by pure semantic HTML5, CSS3, and modern engineering principles.</p> <div class="dax-footer-compatibility"> <span>Microsoft .NET 8 / 9</span> <span>·</span> <span>Decap CMS 3.x</span> <span>·</span> <span>Pure HTML5</span> </div> </div> <div> <h4 class="dax-footer-col-title">Architecture</h4> <ul class="dax-footer-links"> <li><a href="/services/dotnet-ssg-architecture/" class="dax-footer-link">Static Compilation</a></li> <li><a href="/services/decap-cms-workflow/" class="dax-footer-link">Decap CMS Bridge</a></li> <li><a href="/services/azure-devops-github-actions/" class="dax-footer-link">Azure CI/CD Pipeline</a></li> <li><a href="/services/performance-wcag-audits/" class="dax-footer-link">Security Audits</a></li> <li><a href="/services/legacy-cms-migration/" class="dax-footer-link">Benchmark Telemetry</a></li> </ul> </div> <div> <h4 class="dax-footer-col-title">Resources</h4> <ul class="dax-footer-links"> <li><a href="/docs/" class="dax-footer-link">Documentation</a></li> <li><a href="/posts/" class="dax-footer-link">Technical Insights</a></li> <li><a href="/tags/" class="dax-footer-link">Architecture Directory</a></li> <li><a href="https://dax.axcora.com" class="dax-footer-link">Dax Core Project</a></li> </ul> </div> <div> <h4 class="dax-footer-col-title">Company</h4> <ul class="dax-footer-links"> <li><a href="/about/" class="dax-footer-link">About Dax Pro</a></li> <li><a href="/services/" class="dax-footer-link">Enterprise Services</a></li> <li><a href="/license/" class="dax-footer-link">Commercial License</a></li> <li><a href="/contact/" class="dax-footer-link">Contact Engineering</a></li> </ul> </div> </div> <div class="dax-footer-bottom"> <div>© 2026 Dax Pro. Developed for the Microsoft .NET SSG ecosystem and Decap CMS community. All rights reserved.</div> <div class="dax-footer-bottom-links"> <a href="/privacy/" class="dax-footer-link">Privacy Policy</a> <a href="/terms/" class="dax-footer-link">Terms of Service</a> <a href="/license/" class="dax-footer-link">License</a> </div> </div> </div> </footer> <script src="/js/main.js" defer></script> </body> </html>