Comparison

WordPress contact page template

A contact page template is a theme file that WordPress uses for your Contact page: the layout around the text, with the form already in place. This page gives you that file for block themes and for classic themes, and free themes that already have one.

Short answer: In a block theme, add templates/page-contact.html with the Formgong form block; WordPress uses it for the page with the slug contact. In a classic theme the same job is done by page-contact.php. Install the free Formgong plugin and paste your access key under Settings → Formgong.

Use a free theme with the contact form built in

If you are picking a theme anyway, start with one that has a working contact section. These website templates come as WordPress block themes. The contact form is the Formgong plugin's block, so it keeps working when you switch the design later.

Upload the zip under Appearance → Themes → Add New Theme → Upload Theme and activate it. Install the free Formgong plugin, paste your key from formgong.com/new under Settings → Formgong, and send a test message.

Block theme: templates/page-contact.html

Block themes such as Twenty Twenty-Five keep their templates as HTML files in templates/. WordPress looks for page-{slug}.html before page.html, so a file called page-contact.html is used for the page whose slug is contact. Put it in a child theme if the theme gets updates from wordpress.org, or the next update removes it.

The template keeps the theme's header and footer, prints the page title and text, and adds the form under them. formgong/form is the block from the Formgong plugin. It uses the key and the form template you saved under Settings → Formgong.

templates/page-contact.html
<!-- wp:template-part {"slug":"header","tagName":"header"} /-->
<!-- wp:group {"tagName":"main","layout":{"type":"constrained"}} -->
<main class="wp-block-group">
<!-- wp:post-title {"level":1} /-->
<!-- wp:post-content {"layout":{"type":"constrained"}} /-->
<!-- wp:formgong/form /-->
</main>
<!-- /wp:group -->
<!-- wp:template-part {"slug":"footer","tagName":"footer"} /-->

Let editors pick the template for any page

A slug-based template only applies to one page. To offer it in the editor's Template panel for every page, register it in theme.json. The name is the file name without .html.

theme.json (excerpt)
{
  "customTemplates": [
    { "name": "page-contact", "title": "Contact page", "postTypes": ["page"] }
  ]
}

The same template without a plugin

If you would rather not install a plugin, put the form straight into the template in a Custom HTML block. Theme files are not filtered the way post content is, so the script stays. Replace fk_your_access_key with your key.

The script waits for Formgong's answer. It shows "sent" only when the answer says success: true; a refused key shows Formgong's error and a lost connection says the message was not sent.

templates/page-contact.html (no plugin)
<!-- wp:template-part {"slug":"header","tagName":"header"} /-->
<!-- wp:group {"tagName":"main","layout":{"type":"constrained"}} -->
<main class="wp-block-group">
<!-- wp:post-title {"level":1} /-->
<!-- wp:post-content {"layout":{"type":"constrained"}} /-->
<!-- wp:html -->
<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>
<!-- /wp:html -->
</main>
<!-- /wp:group -->
<!-- wp:template-part {"slug":"footer","tagName":"footer"} /-->

Classic theme: page-contact.php

Older themes such as Twenty Twenty-One are built from PHP files. There, page-contact.php in the theme or child theme folder does the same job. The Template Name line also makes it selectable in the page settings for any page. The form comes from the plugin's [formgong] shortcode.

page-contact.php
<?php
/**
 * Template Name: Contact page
 */
get_header();
while ( have_posts() ) :
	the_post();
	?>
	<main class="site-main">
		<h1><?php the_title(); ?></h1>
		<?php the_content(); ?>
		<?php echo do_shortcode( '[formgong]' ); ?>
	</main>
	<?php
endwhile;
get_footer();

What is different on WordPress

page-contact only matches a page whose slug is exactly contact. A page called "Contact us" gets the slug contact-us, so it needs page-contact-us or the theme.json entry above.

Once someone edits the template in Appearance → Editor, WordPress keeps that edited copy in the database and stops reading the file. Reset the template in the editor to go back to the file.

The form posts to Formgong, so wp_mail, SMTP plugins and the host's mail settings play no part in delivery. If Contact Form 7 or WPForms is still on the page, remove its shortcode, or visitors see two forms.

How we tested this code

On 19 October 2026 we ran WordPress 7.1.3 in Docker with the Formgong plugin 1.2.0 from wordpress.org. We added page-contact.html and the theme.json entry to a copy of Twenty Twenty-Five, the no-plugin version to another copy, and page-contact.php to a copy of Twenty Twenty-One. We also installed the four template themes listed above. Each form was sent from Chromium three times against a local copy of Formgong: on the page with the slug contact, and on a second page that picked the template by name. With the right key, Formgong stored exactly one message, not marked as spam, and the visitor saw a success page or text. With a key Formgong does not know, the visitor saw "That access key does not exist." and nothing was stored. With the connection cut, nothing claimed the message was 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 the theme's footer or a Custom HTML block 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 WordPress sites have caching and security plugins, page builders and child themes 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 WordPress version and theme. We read every message and fix the page.

Questions

Where do I put page-contact.html?

In the templates folder of your block theme, next to page.html. If the theme comes from wordpress.org, make a child theme first, or the next theme update deletes your file.

Do I need the plugin?

No. The plugin gives you the block and the shortcode and keeps the key in one setting. The no-plugin template on this page posts the same form to Formgong with a few lines of JavaScript.

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.

Will this replace Contact Form 7?

It can. The form posts to Formgong, so messages no longer depend on WordPress sending email. Remove the old shortcode from the page so there is only one form.

Why does my template file have no effect?

Either the page slug is not contact, or the template was edited in the Site Editor and WordPress now uses that saved copy. Check the slug in the page settings, or reset the template in Appearance → Editor.

Sources

Checked on 19.10.2026 against these public pages: WordPress: Template hierarchy, WordPress: Custom templates in theme.json, WordPress: Page templates (classic themes).

← Formgong