NPS survey form

Ask how likely a visitor is to recommend you, from 0 to 10, then an optional comment. One submission, two steps when JavaScript runs.

Using Tilda? Paste into a T123 block (paid plan) or use the no-code webhook.

What you are copying

This starter asks how likely a visitor is to recommend you, on a scale from 0 to 10, then offers an optional comment.

It is a survey of your visitors. It is not a rating of Formgong, and this site does not publish a score for the product.

With JavaScript, the comment sits on a second step. The script shows that step only after a score is chosen.

Without JavaScript, a small noscript style shows both steps at once. The visitor can still submit one form.

There is a single POST, at the end. The first step does not send a partial submission.

Fields Telegram will show

The score is a radio group named score. Telegram prints score and then the number, from 0 to 10.

The comment field is named comment. Email is optional, named email, so you can reply when someone leaves an address.

Do not add a second group named score. Radios with the same name are one answer.

The honeypot botcheck is included, empty and off screen. A filled honeypot is spam and is not delivered.

The extra snippet posts the same answers as JSON with fetch, after the steps. It still needs the access key in the form.

Turnstile stays commented until you opt in and paste a site key. YOUR_TURNSTILE_SITE_KEY is a placeholder, not a working key.

What this does not claim

The template does not compute a promoter index, a chart or a benchmark. You read the numbers in the inbox or in Telegram.

It does not score leads, and it does not send the answers to a model. The number is just a field.

Free keeps submissions for 30 days and allows 300 a month. Telegram is immediate. Email on the free plan arrives for every submission.

Storage is in the EU. Formgong is the operator. Write to support@formgong.com about the product.

If you need a long industry form instead of this two-step question, use the industry templates on the hub.

Preview

How likely are you to recommend us?

Not likely · Very likely

Form code

HTML
<form id="fg-nps" action="https://formgong.com/submit" method="POST" style="position:relative">
  <input type="hidden" name="access_key" value="fk_your_access_key">
  <input type="hidden" name="_lang" value="en">
  <fieldset data-step="1">
    <legend>How likely are you to recommend us?</legend>
    <p><span>Not likely</span> <span>Very likely</span></p>
      <label><input type="radio" name="score" value="0" required> 0</label>
      <label><input type="radio" name="score" value="1" required> 1</label>
      <label><input type="radio" name="score" value="2" required> 2</label>
      <label><input type="radio" name="score" value="3" required> 3</label>
      <label><input type="radio" name="score" value="4" required> 4</label>
      <label><input type="radio" name="score" value="5" required> 5</label>
      <label><input type="radio" name="score" value="6" required> 6</label>
      <label><input type="radio" name="score" value="7" required> 7</label>
      <label><input type="radio" name="score" value="8" required> 8</label>
      <label><input type="radio" name="score" value="9" required> 9</label>
      <label><input type="radio" name="score" value="10" required> 10</label>
    <button type="button" data-next>Next</button>
  </fieldset>
  <fieldset data-step="2" hidden>
    <label>What should we know? <textarea name="comment"></textarea></label>
    <label>Email <input type="email" name="email" autocomplete="email"></label>
    <button type="button" data-back>Back</button>
    <button type="submit">Send</button>
  </fieldset>
  <div aria-hidden="true" style="position:absolute;inset-inline-start:0;top:0;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)">
  <label>Leave this field empty <input type="text" name="botcheck" tabindex="-1" autocomplete="off"></label>
</div>
  <!--
Optional. Turn Turnstile on for this form, then remove these comment marks and paste your site key.
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
<div class="cf-turnstile" data-sitekey="YOUR_TURNSTILE_SITE_KEY" data-language="en"></div>
-->
</form>
<noscript><style>[data-step]{display:block}</style></noscript>
<script>
(function () {
  var form = document.getElementById("fg-nps");
  if (!form) return;
  var steps = form.querySelectorAll("[data-step]");
  function show(index) {
    steps.forEach(function (step, i) { step.hidden = i !== index; });
  }
  var next = form.querySelector("[data-next]");
  var back = form.querySelector("[data-back]");
  if (next) next.addEventListener("click", function () {
    var picked = form.querySelector('input[name="score"]:checked');
    if (!picked) return;
    show(1);
  });
  if (back) back.addEventListener("click", function () { show(0); });
})();
</script>
Same answers as one JSON fetch
Same answers as one JSON fetch
<form id="fg-nps" action="https://formgong.com/submit" method="POST" style="position:relative">
  <input type="hidden" name="access_key" value="fk_your_access_key">
  <input type="hidden" name="_lang" value="en">
  <fieldset data-step="1">
    <legend>How likely are you to recommend us?</legend>
    <p><span>Not likely</span> <span>Very likely</span></p>
      <label><input type="radio" name="score" value="0" required> 0</label>
      <label><input type="radio" name="score" value="1" required> 1</label>
      <label><input type="radio" name="score" value="2" required> 2</label>
      <label><input type="radio" name="score" value="3" required> 3</label>
      <label><input type="radio" name="score" value="4" required> 4</label>
      <label><input type="radio" name="score" value="5" required> 5</label>
      <label><input type="radio" name="score" value="6" required> 6</label>
      <label><input type="radio" name="score" value="7" required> 7</label>
      <label><input type="radio" name="score" value="8" required> 8</label>
      <label><input type="radio" name="score" value="9" required> 9</label>
      <label><input type="radio" name="score" value="10" required> 10</label>
    <button type="button" data-next>Next</button>
  </fieldset>
  <fieldset data-step="2" hidden>
    <label>What should we know? <textarea name="comment"></textarea></label>
    <label>Email <input type="email" name="email" autocomplete="email"></label>
    <button type="button" data-back>Back</button>
    <button type="submit">Send</button>
  </fieldset>
  <div aria-hidden="true" style="position:absolute;inset-inline-start:0;top:0;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)">
  <label>Leave this field empty <input type="text" name="botcheck" tabindex="-1" autocomplete="off"></label>
</div>
  <!--
Optional. Turn Turnstile on for this form, then remove these comment marks and paste your site key.
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
<div class="cf-turnstile" data-sitekey="YOUR_TURNSTILE_SITE_KEY" data-language="en"></div>
-->
</form>
<noscript><style>[data-step]{display:block}</style></noscript>
<script>
(function () {
  var form = document.getElementById("fg-nps");
  if (!form) return;
  var steps = form.querySelectorAll("[data-step]");
  function show(index) {
    steps.forEach(function (step, i) { step.hidden = i !== index; });
  }
  var next = form.querySelector("[data-next]");
  var back = form.querySelector("[data-back]");
  if (next) next.addEventListener("click", function () {
    var picked = form.querySelector('input[name="score"]:checked');
    if (!picked) return;
    show(1);
  });
  if (back) back.addEventListener("click", function () { show(0); });
})();
</script>
<script>
(function () {
  var form = document.getElementById("fg-nps");
  if (!form) return;
  var status = document.createElement("p");
  status.setAttribute("role", "status");
  form.appendChild(status);
  form.addEventListener("submit", function (event) {
    event.preventDefault();
    var data = Object.fromEntries(new FormData(form));
    fetch("https://formgong.com/submit", {
      method: "POST",
      headers: { "Content-Type": "application/json", Accept: "application/json" },
      body: JSON.stringify(data)
    }).then(function (res) { return res.json(); }).then(function (result) {
      status.textContent = result && result.success ? (result.message || "Sent. Thank you!") : ((result && result.message) || "Could not send. Please try again.");
      if (result && result.success) form.reset();
    }).catch(function () {
      status.textContent = "Could not send. Please try again.";
    });
  });
})();
</script>

Questions and answers

Is 0 the low end?

Yes. The radios run from 0 to 10. The labels say which end is low and which end is high. The stored value is the number.

Can I require the comment?

You can add the required attribute on the textarea. This starter leaves it optional so a score alone can be sent.

Does the first step save anything?

No. Formgong receives one submission when the form is submitted. Moving to the comment does not post.

Why is email optional?

Some people will share a score and no address. If you need a reply path, say so in the label and mark the field required.