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.
- Kavelle: a dark site for an interior design studio (zip)
- Lattice: a one-page portfolio for a developer or a small studio (zip)
- Burrow: a bright site for a vet clinic (zip)
- Goldleaf: an architecture studio on a line grid (zip)
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.
<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:
{{!< 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.