Comparison

Ghost contact form

Ghost sends newsletters and signs up members, yet there is no contact form anywhere in Ghost Admin, so you add one yourself. Below are three ways to do it, from a ready theme to a template file in your own theme.

Short answer: Create a page called Contact, add an HTML card, paste the form below and replace fk_your_access_key with the key of a free Formgong form. Messages arrive by email and in your Formgong inbox. No theme change is needed.

Use a free Ghost theme with the form built in

If you are still choosing a theme, take one that already has a contact form. Our website templates come as Ghost themes. The home page has the template's design and its contact form; posts, tags and authors get the same header, footer and colours.

Upload the zip in Ghost Admin under Settings → Design & branding → Change theme → Upload theme, and activate it. Then open Customize, find Formgong access key under Homepage and paste your key. Ghost(Pro) Starter does not take uploaded themes, so on that plan use the HTML card below instead.

Quickest: an HTML card on a Contact page

This works with any theme, including the default one, and needs no file changes. In Ghost Admin open Pages → New page and call it Contact. Add an HTML card (the + button, or type /html) and paste the code. Replace fk_your_access_key with your key from formgong.com/new and publish. Then add the page to your menu under Settings → Navigation, with the URL /contact/.

The card holds the form, a few lines of CSS and the script that sends it. The script waits for Formgong's answer and shows "sent" only when the answer says success: true. A wrong key shows Formgong's own error, and a lost connection says the message was not sent. Send your test from the published page, not from the editor.

HTML card
<form class="fg-contact" action="https://formgong.com/submit" method="POST" data-formgong>
  <input type="hidden" name="access_key" value="fk_your_access_key">
  <input type="hidden" name="_subject" value="New message from the contact page">
  <label>Name <input type="text" name="name" autocomplete="name" required></label>
  <label>Email <input type="email" name="email" autocomplete="email" required></label>
  <label>Message <textarea name="message" rows="5" required></textarea></label>
  <!-- Honeypot: people never see it, simple bots fill it in. -->
  <div class="fg-hp" aria-hidden="true"><input type="text" name="botcheck" tabindex="-1" autocomplete="off"></div>
  <button type="submit">Send message</button>
  <p data-status role="status"></p>
</form>
<style>
  .fg-contact { position: relative; display: grid; gap: 1rem; max-width: 34rem; }
  .fg-contact label { display: grid; gap: 0.35rem; font-weight: 600; }
  .fg-contact input, .fg-contact textarea { font: inherit; padding: 0.6rem 0.75rem; border: 1px solid #9aa5b1; border-radius: 8px; }
  .fg-contact button { justify-self: start; padding: 0.7rem 1.2rem; border: 0; border-radius: 8px; background: #2f5bea; color: #fff; font: inherit; font-weight: 600; cursor: pointer; }
  .fg-contact button:disabled { opacity: 0.6; }
  .fg-contact .fg-hp { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
</style>
<script>
  document.querySelectorAll("form[data-formgong]").forEach(function (form) {
    form.addEventListener("submit", async function (event) {
      event.preventDefault();
      var button = form.querySelector("button[type=submit]");
      var status = form.querySelector("[data-status]");
      button.disabled = true;
      status.textContent = "Sending…";
      try {
        var response = await fetch(form.action, {
          method: "POST",
          headers: { Accept: "application/json" },
          body: new FormData(form),
        });
        var result = await response.json();
        if (result.success === true) {
          form.reset();
          status.textContent = "Thanks! Your message has been sent.";
        } else {
          status.textContent = result.message || "Not sent. Please try again.";
        }
      } catch (error) {
        status.textContent = "Not sent: check your connection and try again.";
      }
      button.disabled = false;
    });
  });
</script>

In your theme: a custom page template

If you edit your own theme, keep the form in the theme instead of in page content, so an editor cannot delete it by accident. Save the form as partials/contact-form.hbs (the same code as the card above), and add custom-contact.hbs next to page.hbs:

custom-contact.hbs
{{!< default}}
{{#post}}
<main class="gh-main">
  <article class="{{post_class}}">
    <h1>{{title}}</h1>
    <div class="gh-content">{{content}}</div>
    {{> "contact-form"}}
  </article>
</main>
{{/post}}

Ghost offers every custom-*.hbs file in the page settings under Template. Pick this one for your Contact page. If you would rather not pick anything, name the file page-contact.hbs and Ghost uses it for the page whose slug is contact. Zip the theme and upload it again under Settings → Design & branding.

What is different on Ghost

Ghost's own forms, the ones with data-members-form, sign people up to the newsletter. They never send you a message, so they cannot stand in for a contact form. Ghost also has nowhere to keep form messages; the messages stay in Formgong, which shows them in its inbox and forwards them by email, Telegram or to a Google Sheet.

The hidden botcheck field is a honeypot. Simple bots fill it in, and Formgong marks those messages as spam so they do not reach your email. The HTML card and custom-*.hbs templates also work in posts; page-contact.hbs is for pages only.

How we tested this code

On 19 October 2026 we ran Ghost 6.71 in Docker with SQLite, the way ghost install local does. We put the HTML card on a page, added the partial, custom-contact.hbs and page-contact.hbs to a copy of Ghost's default theme Source, and installed the four template themes listed above. Every form was sent from Chromium three times against a local copy of Formgong. With the right key each form showed its success text and exactly one message was stored, not marked as spam. With a key Formgong does not know, the form showed "That access key does not exist." and nothing was stored. With the connection cut, the form said it was not sent.

The rest of the kit

The same Formgong account that receives your form also has a help chat that answers visitors' questions from your own pages, and a reviews widget that collects star ratings and shows them on the site. Each goes in as a short embed code, so you add it to Ghost's Code injection (Settings → Code injection → Site footer) the way you added the form, and you manage the form, the chat and the reviews from one dashboard.

Did it work on your site?

We tested this on clean installs. Real Ghost sites have integrations, edited themes and code injection we did not have. If the form broke on yours, or a step here was wrong or unclear, write to support@formgong.com with your Ghost version and theme. We read every message and fix the page.

Questions

Does Ghost have a built-in contact form?

No. Ghost has signup forms for members and the newsletter, but nothing that sends you a message. You add a form in an HTML card or in your theme and point it at a form backend such as Formgong.

Can I use the HTML card on Ghost(Pro) Starter?

Yes. The HTML card is part of the editor on every plan. Starter does not accept uploaded themes, so the theme template and the Formgong Ghost themes need a plan that allows custom themes.

Is it safe to put the access key in the page?

Yes. The key only lets someone send a message to your form, which the page already does. It cannot read submissions or change settings. You can also add your domain under allowed domains in the form settings, and Formgong will refuse posts from other sites.

Where do the messages go?

To your Formgong inbox and to the email addresses you set on the form. You can also send them to Telegram, Slack, Discord, a Google Sheet, Notion or a webhook.

Can I use page-contact.hbs instead of a custom template?

Yes. Ghost uses page-contact.hbs for the page whose slug is contact, with nothing to pick in the page settings. A custom-contact.hbs template has to be picked, but it works for any page.

Sources

Checked on 19.10.2026 against these public pages: Ghost: Theme structure (custom templates), Ghost: Pricing and plan features.

← Formgong