Elementor

Add a contact form to an Elementor page with the HTML widget, styled to match your site.

Elementor's HTML widget, part of the free Elementor plugin, lets you drop a hand-written form anywhere in a layout. Paired with SilentForms it gives you a working contact form without Elementor Pro, without a form plugin and without configuring email on your WordPress server.

Get an access key on the access key page first. It only routes submissions to your inbox, so it is safe in the page's HTML.

Elementor's own forms vs the HTML widget#

Elementor's form widgets (the Pro Form widget and Form Lite) submit to your WordPress site, which then emails or stores the entry. The Pro Form widget can also forward a submission to a URL with its Webhook action under Actions After Submit, but that request is sent by your WordPress server after Elementor has processed the form, in Elementor's own format.

SilentForms is built to receive the form post or fetch call directly from the visitor's browser: that is what its per-visitor rate limits, domain restriction, honeypot and CAPTCHA checks are designed around. So the supported route is a plain HTML form in the HTML widget, as below. Your server sends no email at all, so you need no SMTP plugin either.

Add the HTML widget#

  1. Edit the page with Elementor.
  2. Find the HTML widget (the </> icon) in the widget panel and drag it where the form should go.
  3. Paste the form below into the code area. Elementor flags syntax errors as you type.
  4. Click Publish or Update.
<form class="sf-form" action="https://silentforms.com/api/submit" method="POST">
  <input type="hidden" name="accessKey" value="YOUR_ACCESS_KEY_HERE">
  <input type="hidden" name="subject" value="New message from the website">
  <input type="hidden" name="redirect" value="https://example.com/thank-you/">

  <div class="sf-row">
    <div>
      <label for="sf-name">Name</label>
      <input id="sf-name" type="text" name="name" autocomplete="name" required>
    </div>
    <div>
      <label for="sf-email">Email</label>
      <input id="sf-email" type="email" name="email" autocomplete="email" required>
    </div>
  </div>

  <label for="sf-message">Message</label>
  <textarea id="sf-message" name="message" rows="6" required></textarea>

  <input type="checkbox" name="botcheck" style="display: none;" tabindex="-1" autocomplete="off">
  <button type="submit" class="elementor-button">Send message</button>
</form>

The visitor lands on the page in redirect after a successful submission. Build that page in Elementor too, and use its full URL. Leave the field out to show the SilentForms default thank-you page instead. See Redirect after submit.

Elementor restricts the HTML widget to administrators by default, because it can contain any code. Editors and other roles need access granted in Elementor's Role Manager before they can add or change it.

Style it with your Global settings#

The HTML widget renders straight into the page, so the form inherits your theme's and Elementor's typography. To tie the fields and button to your Global Colors, use the CSS variables Elementor generates for them. Add the CSS below in the same HTML widget inside a <style> tag, or in your site's custom CSS:

.sf-form { display: grid; gap: 0.5rem; }
.sf-form .sf-row { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); }
.sf-form .sf-row > div { display: grid; gap: 0.5rem; }
.sf-form label { font-weight: 600; }
.sf-form input,
.sf-form textarea {
  width: 100%;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--e-global-color-text, #7a7a7a);
  border-radius: 6px;
  font: inherit;
}
.sf-form button {
  justify-self: start;
  margin-top: 0.75rem;
  background-color: var(--e-global-color-accent, #61ce70);
}

The var(…, fallback) form keeps the fields styled even if a kit does not define those colours. The elementor-button class gives the button your kit's default button styling, which the background colour above then overrides; delete that rule to keep the kit's colour.

Show the result without leaving the page#

To keep the visitor on the page, add this below the form in the same widget:

<p id="sf-status" role="status"></p>
<script>
  document.querySelector('.sf-form').addEventListener('submit', async (event) => {
    event.preventDefault();
    const form = event.currentTarget;
    const status = document.getElementById('sf-status');
    status.textContent = 'Sending…';
    try {
      const response = await fetch(form.action, {
        method: 'POST',
        headers: { Accept: 'application/json' },
        body: new FormData(form),
      });
      const result = await response.json();
      status.textContent = result.ok ? 'Thanks! Your message has been sent.' : result.message;
      if (result.ok) form.reset();
    } catch {
      status.textContent = 'Something went wrong. Please try again.';
    }
  });
</script>

If you put the same form in a popup or on several pages, give each copy its own id values so the labels still point at the right fields.

Test it#

Scripts in the HTML widget do not always run inside the editor, so open the published page in a private window, submit the form and check your inbox. The email lists every field in order, and Reply goes to the visitor's address. If you see an error, the message says what is wrong; every one is explained in Troubleshooting.

Next steps#