Contact form on a static site

Contact form on a static site6 min read

Add a contact form to your portfolio website

A portfolio contact form that asks the questions you'd ask on a first call: project, budget, timing and a link, with no server and replies from your inbox.

Preet SutharFounder, Ikiform

Ask for the four things you need before a first call: what the project is, a budget range, when it starts and a link to look at. Post the form to Ikiform from your static portfolio, so each enquiry lands in your inbox with Reply going to the client. Prefill the project type from the case study the visitor came from, so you know what sold them.

What's different about a portfolio form

On a portfolio, the contact form is the sales funnel. A bare name, email and message form brings in messages you have to answer with questions: what's the budget, when do you want to start, do you have a site already. Asking those up front saves a round of email, and the answers tell you which enquiries to answer first.

Portfolios also tend to live on free static hosting, built once and left alone for months. The form has to keep working without a server to patch, and without you checking a dashboard.

FieldWhy it's thereType
projectWho to answer and what to send backselect, so it's safe to filter on
budgetRanges filter better than an open number, and people answer themradio with ranges
start"This month" and "later, still planning" need different repliesselect
linkTheir current site or a brief, so you reply with something specificurl
messageThe project in their wordstextarea, at least 30 characters

Leave out phone numbers and company size. Each extra required field costs enquiries, and you'll learn both on the first call.

Preview

Runs Ikiform's validator in your browser. Nothing is sent.

Budget

Filled in by the page, hidden from visitors.

Paste this definition on the form's Fields tab. Ikiform then accepts only the listed budget and project values, and refuses a message under 30 characters with a field error. seen is in the definition because a form with fields refuses any input it doesn't list; on the page it's a hidden input.
File uploads aren't supported yet. A post with a file gets 422 files_not_supported.

The form

Plain HTML, so it works in a hand-written portfolio, a Jekyll or Hugo theme, or an Astro template. The script at the end prefills the project type from the link that brought the visitor here, and records which page that was:

contact.html
<form id="enquiry" action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <label for="name">Your name</label>
  <input id="name" name="name" autocomplete="name" maxlength="120" required />

  <label for="email">Email</label>
  <input id="email" name="email" type="email" autocomplete="email" required />

  <label for="project">What do you need?</label>
  <select id="project" name="project" required>
    <option value="website">A website</option>
    <option value="branding">Branding</option>
    <option value="product">Product design</option>
    <option value="other">Something else</option>
  </select>

  <fieldset>
    <legend>Budget</legend>
    <label><input type="radio" name="budget" value="under_5k" required /> Under $5k</label>
    <label><input type="radio" name="budget" value="5k_15k" /> $5k to $15k</label>
    <label><input type="radio" name="budget" value="over_15k" /> Over $15k</label>
    <label><input type="radio" name="budget" value="unsure" /> Not sure yet</label>
  </fieldset>

  <label for="start">When would you like to start?</label>
  <select id="start" name="start">
    <option value="now">This month</option>
    <option value="soon">In 1 to 3 months</option>
    <option value="later">Later, still planning</option>
  </select>

  <label for="link">Your current site or a brief</label>
  <input id="link" name="link" type="url" placeholder="https://" />

  <label for="message">Tell me about the project</label>
  <textarea id="message" name="message" rows="6" minlength="30" required></textarea>

  <input type="hidden" name="seen" value="" />
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button type="submit">Send enquiry</button>
</form>

<script>
  const params = new URLSearchParams(location.search);
  const enquiry = document.querySelector("#enquiry");
  const project = params.get("project");
  if (project && enquiry.project.querySelector(`option[value="${CSS.escape(project)}"]`)) {
    enquiry.project.value = project;
  }
  enquiry.seen.value = params.get("from") ?? document.referrer;
</script>

On each case study, link to the form with the project type and the page name:

case-studies/northwind.html
<a href="/contact.html?project=branding&from=case-study-northwind">Start a branding project</a>
  • The script only selects a project value that exists in the list, so a hand-edited link can't add new values.
  • seen holds the case study name, or the referrer when there is none. After a few months, the CSV tells you which work brings in which enquiries.
  • Keep email as the field name. Notification emails set it as the reply-to, so you answer from your own inbox.
  • For a framework portfolio, the pillar guide has the same form pattern in Next.js, Astro, SvelteKit, Nuxt, React and Vue. Static site generators have their own setup pages, such as Jekyll and Hugo.

Answer the right enquiries first

Where a portfolio enquiry goes after the visitor presses Sendenquiry formprefilled from a casestudyIkiformspam checks, stores ityour inboxReply goes to the clientphone alertwebhook to Telegram orSlackdashboardCSV of every enquirypostWhere a portfolio enquiry goes after the visitor presses Sendenquiryformprefilled froma case studyIkiformspam checks,stores ityour inboxReply goes to theclientphone alertwebhook to Telegramor SlackdashboardCSV of everyenquirypost
  1. A visitor arrives from a case study, and the form opens with the project type already chosen.
  2. The form posts to Ikiform, which runs the spam checks and stores the enquiry.
  3. You get a notification email, and Reply writes straight back to the client.
  4. A webhook sends the same enquiry to Telegram or Slack, where an automation can alert you for large budgets only.
  5. Every enquiry stays in the dashboard, so you can export a year of them as CSV and see which work brought them in.
Email alone is enough to start. Add the phone alert when you start losing work to slow replies.

Ikiform sends every enquiry the same way; filtering by budget happens in the tool that receives the webhook. In Zapier or n8n, add a filter on budget before the message step. To reach your phone directly, follow Telegram or Slack.

Ikiform doesn't email the person who fills in the form. Notification emails go to you and your team only, so there is no autoresponder, confirmation email or double opt-in. Send those from your email tool, triggered by a webhook. For a portfolio, a short thank-you page that says when you'll reply does most of what an autoresponder would.

A thank-you page that books the call

Set Redirect after submit to a thank-you page of your own. On a portfolio it can do more than say thanks: link your calendar booking page for people who want to talk now, and say when you reply to everyone else. Add two or three case studies they haven't seen; they're still deciding.

Keep the page out of search results with a noindex meta tag, and don't count visits to it as a conversion in analytics without also checking the dashboard, because spam gets the same redirect as a real enquiry.

Portfolio spam

Portfolios get a particular kind of spam: SEO offers, guest post pitches and "redesign your site" templates. Ikiform's scoring adds +2 for known spam phrases, +3 for a link in the name field and +2 for three or more links; at 5 a message is stored as spam and sends no email.

A client rarely sends two enquiries in ten minutes, so set the rate limit to Strict, 2 every 10 minutes per visitor. Add your domain under Allowed domains, and check the Spam filter now and then in the first weeks: a real enquiry with three links to past work can score high.

Check it works

  1. Under the form's Allowed domains, add your site's domain, plus localhost while you develop. Leave it empty to accept posts from anywhere.
  2. Open contact.html?project=branding&from=test and check Branding is selected.
  3. Send an enquiry. In Submissions, seen should read test.
  4. Remove minlength from the textarea in your browser's devtools and send a three-word message. Ikiform refuses it with 422 validation_failed, naming message.
  5. Check the inbox of each notification recipient. Each gets an email with the fields, and Reply goes to the submitter when the form has an email field.
  6. Nothing arrived? Switch the dashboard's status filter to Spam. Spam gets the same success answer as a real submission.

Gotchas

ProblemWhat to do
422 validation_failed for budgetA radio value in the HTML doesn't match the definition's options. They must match exactly, including underscores.
The project isn't prefilledThe project value in the link isn't one of the option values. Check spelling and case.
A real client's enquiry is in spamThree or more links add to the score, and with a phrase from the spam list or a disposable address on top it crosses the threshold. Mark it as not spam; its email and webhooks go out then.
422 files_not_supportedThe form has an <input type="file"> with a file chosen. File uploads aren't supported yet; remove the input or ask for a link instead.

Questions

Stuck on a step, or want a guide for your situation? Email hello@ikiform.com.