Posted: November 23, 2019

Preventing redirects when using Netlify Forms

Hey everyone, I thought to write a little article about my recent experience adding a stateful form to my Netlify site. While the Netlify docs do have an article about doing this, I thought the example they included for using AJAX was nice, but a little outdated. I’m not using jQuery on my React sites, no thanks! There is another outdated example using class components and stateful forms but if you don’t want to use a stateful form, this is the easiest way to implement your form!

The crucial detail you need to know in order to get your form data DOES come from the first article:

Note that the content of the AJAX request must be URL-encoded. In the above example, the jQuery serialize() function handles the URL encoding. Netlify forms do not support JSON form data at this time.

OK, that helps quite a bit. If you’re familiar with React and HTML forms at all, you’ll know that in order to stop the standard submit for a form from being performed, you’ll need to supply a custom onSubmit function to prevent the default propogation of the onSubmit event.

In my basic form before (I use styled-components to do my styling, but there are minimal styles at play here, and you can see that basically this is the same HTML that Netlify provides out of the box …), I was using an action attribute on the form pointing to another static page I had. I wanted to get rid of the “Thank You” page I had built and instead show some text indicating that the form was being submitted. Here’s what I started with:

// in contact-form.js
import React from "react";
import styled from "styled-components";

const ContactUsSection = styled.div`
  margin-top: 1rem;
  & > form > p > label > input,
  textarea {
    display: block;
    border: 1px solid gray;
    border-radius: 5px;
  }
`;

const Submit = styled.button`
  font-weight: 700;
  text-transform: uppercase;
  cursor: pointer;
  border: 1px solid gray;
  border-radius: 5px;
  padding: 1rem 1rem 0.75rem;
  font-weight: 100;
  background: none;
`;

const TextArea = styled.textarea`
  display: block;
  min-height: 150px;
  min-width: 500px;

  @media (max-width: 768) {
    min-width: 290px;
  }
`;

const ContactForm = () => (
  <ContactFormSection>
    <h2>Looking to get in touch?</h2>
    <form
      name="contact-form"
      method="POST"
      data-netlify="true"
      netlify-honeypot="bot-field"
      action="/thank-you"
    >
      <p style={{ display: "none" }}>
        <label>
          Don’t fill this out if you expect to hear from me!
          <input name="bot-field" />
        </label>
      </p>
      <input
        style={{ display: "none" }}
        name="form-name"
        value="contact-form"
        readOnly={true}
      />
      <p>
        <label htmlFor="name">
          Your Name: <input type="text" id="name" name="name" required={true} />
        </label>
      </p>
      <p>
        <label htmlFor="email">
          Your Email:{" "}
          <input type="email" id="email" name="email" required={true} />
        </label>
      </p>
      <p>
        <label htmlFor="message">
          Message: <TextArea required={true} id="message" name="message"></TextArea>
        </label>
      </p>
      <Submit type="submit" name="SendMessage">
        Send
      </Submit>
    </form>
  </ContactFormSection>
);

export default ContactForm;

Pretty simple, self-explanatory stuff. Now let’s get rid of the action and implement a custom handler:

// in contact-form.js
import React, { useState } from "react";

const onSubmit = async (event, setSubmitText) => {
  event.preventDefault();
    setSubmitText("Submitting ...");  debugger;
};

const ContactUs = () => {
  const [submitText, setSubmitText] = useState(null);
  return (
    <ContactUsSection id="contact">
      <form
        id="#contact-james"
        name="contact-james"
        method="POST"
        data-netlify="true"
        onSubmit={e => onSubmit(e, setSubmitText)}
      >
        <p style={{ display: "none" }}>
          <label>
            Don’t fill this out if you expect to hear from us!
            <input name="bot-field" value="" readOnly />
          </label>
        </p>
        ... the rest of the form ...
    </form>
    {submitText && <ResponseText>{submitText}</ResponseText>}
    </ContactUsSection>
  );
};
  )
}

OK, so filling out the form with my Dev Tools open, I hit my debugger! This is great. Now I just need to strip the data fields out of the form and format them accordingly in order to communicate them to Netlify correctly. Those can be accessed through the event.currentTarget.elements property … for now, I’m going to strip out the netlify-honeypot field from what gets sent over and instead do the validation myself:

const onSubmit = async (event, setSubmitText) => {
  event.preventDefault();
  setSubmitText("Submitting ...");
  const formElements = [...event.currentTarget.elements];
  const isValid =
    formElements.filter((elem) => elem.name === "bot-field")[0].value === "";
  const validFormElements = isValid ? formElements : [];

  if (validFormElements.length < 1) {
    // or some other cheeky error message
    setSubmitText("It looks like you filled out too many fields!");
  } else {
    const filledOutElements = validFormElements
      .filter((elem) => !!elem.value)
      .map(
        (element) =>
          encodeURIComponent(element.name) +
          "=" +
          encodeURIComponent(element.value)
      )
      .join("&");

    await fetch("/", {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: filledOutElements,
    })
      .then(() => {
        setSubmitText("Successfully submitted!");
      })
      .catch((_) => {
        setSubmitText(
          "There was an error with your submission, please email me using the address above."
        );
      });
  }
};

Et voilà!! Using the included setSubmitText function, I can now prevent redirects when routing my form submissions through Netlify, and I can display a custom message to users. Hope this was helpful to those of you hoping to prevent redirects and provide users with on-page feedback when using Netlify forms.

As always, drop me a line (through the form in question!) with any feedback you have!