<!--Companion article: https://css-tricks.com/headless-form-submission-with-the-wordpress-rest-api/-->
<p class="bg-secondary text-center text-primary p-1">
    There's no reply. Submissions are deleted automatically and immediately.
</p>

<div class="container">

    <div class="columns col-gapless">

        <div class="col-sm-12 col-md-8 col-4 col-mx-auto">

            <h4>Contact Form 7</h4>
            <div class="divider"></div>

            <form action="https://cssformsrestapi.tastewp.com/wp-json/contact-form-7/v1/contact-forms/5/feedback" method="post" autocomplete="off">

                <!--The "required" attributes are not used on purpose.-->

                <div class="form-group">
                    <label class="form-label" for="somebodys-name">Somebody's name</label>
                    <input class="form-input" id="somebodys-name" type="text" name="somebodys-name">
                </div>

                <div class="form-group">
                    <label class="form-label" for="any-email">Any valid email address</label>
                    <!--The type="email" is not used on purpose.-->
                    <input class="form-input" id="any-email" type="text" name="any-email">
                </div>

                <div class="form-group">
                    <!--Space Age began at October 4, 1957-->
                    <label class="form-label" for="before-space-age">A date before the Space Age</label>
                    <!--The "max" attribute is not used on purpose.-->
                    <input class="form-input col-8" id="before-space-age" type="date" placeholder="yyyy-mm-dd" name="before-space-age">
                </div>

                <div class="form-group">
                    <label class="form-label" for="optional-message">Optional message to the world</label>
                    <textarea class="form-input" id="optional-message" name="optional-message"></textarea>
                </div>

                <div class="form-group">
                    <label class="form-switch">
                        <input name="fake-terms" type="checkbox" value="1">
                        <i class="form-icon"></i>
                        Fake and obligatory terms and conditions checkbox
                    </label>
                </div>

                <div class="form-group">
                    <button type="submit" class="btn btn-primary">
                        Submit
                    </button>
                </div>

            </form>

            <div class="mt-12 mt-6">

                <h4>Gravity Forms</h4>
                <div class="divider"></div>

                <form action="https://cssformsrestapi.tastewp.com/wp-json/gf/v2/forms/1/submissions" method="post" autocomplete="off">

                    <!--The "required" attributes are not used on purpose.-->

                    <div class="form-group">
                        <label class="form-label" for="input_1">Somebody's name</label>
                        <input class="form-input" id="input_1" type="text" name="input_1">
                    </div>

                    <div class="form-group">
                        <label class="form-label" for="input_2">Any valid email address</label>
                        <!--The type="email" is not used on purpose.-->
                        <input class="form-input" id="input_2" type="text" name="input_2">
                    </div>

                    <div class="form-group">
                        <!--Space Age began at October 4, 1957-->
                        <label class="form-label" for="input_3">A date before the Space Age</label>
                        <!--The "max" attribute is not used on purpose.-->
                        <input class="form-input col-8" id="input_3" type="date" placeholder="yyyy-mm-dd" name="input_3">
                    </div>

                    <div class="form-group">
                        <label class="form-label" for="input_4">Optional message to the world</label>
                        <textarea class="form-input" id="input_4" name="input_4"></textarea>
                    </div>

                    <div class="form-group">
                        <label class="form-switch">
                            <input name="input_5_1" type="checkbox" value="1">
                            <i class="form-icon"></i>
                            Fake and obligatory terms and conditions checkbox
                        </label>
                    </div>

                    <div class="form-group">
                        <button type="submit" class="btn btn-primary">
                            Submit
                        </button>
                    </div>

                </form>

            </div>

        </div>

    </div>

</div>
.mt-12 {
    margin-top: calc(0.2rem * 12);
}

.mt-6 {
    margin-bottom: calc(0.2rem * 6);
}

[x-cloak] {
    display: none;
}
// https://css-tricks.com/snippets/javascript/strip-html-tags-in-javascript/
const stripHtml = (string) => string.replace(/(<([^>]+)>)/gi, "");

const normalizeResponse = (url, response) => {
    if (
        url.match(/wp-json\/contact-form-7\/v1\/contact-forms\/\d+\/feedback/)
    ) {
        return normalizeContactForm7Response(response);
    }

    if (url.match(/wp-json\/gf\/v2\/forms\/\d+\/submissions/)) {
        return normalizeGravityFormsResponse(response);
    }

    return {
        isSuccess: false,
        message: "Are you submitting to the right URL?",
        validationError: {}
    };
};

const normalizeGravityFormsResponse = (response) => {
    const isSuccess = response.is_valid;
    const message = isSuccess
        ? stripHtml(response.confirmation_message)
        : "There was a problem with your submission.";
    const validationError = isSuccess
        ? {}
        : Object.fromEntries(
              Object.entries(
                  response.validation_messages
              ).map(([key, value]) => [`input_${key}`, value])
          );

    return {
        isSuccess,
        message,
        validationError
    };
};

const normalizeContactForm7Response = (response) => {
    const isSuccess = response.status === "mail_sent";
    const message = response.message;
    const validationError = isSuccess
        ? {}
        : Object.fromEntries(
              response.invalid_fields.map((error) => {
                  const key = /cf7[-a-z]*.(.*)/.exec(error.into)[1];

                  return [key, error.message];
              })
          );

    return {
        isSuccess,
        message,
        validationError
    };
};

const formSubmissionHandler = (event) => {
    event.preventDefault();

    const formElement = event.target,
        { action, method } = formElement,
        body = new FormData(formElement);

    fetch(action, {
        method,
        body
    })
        .then((response) => response.json())
        .then((response) => normalizeResponse(action, response))
        .then((response) => {
            alert(response.message);

            if (response.isSuccess) {
                formElement.reset();
            }
        })
        .catch((error) => {
            alert("Check the console for the error details.");
            console.log(error);
        });
};

const formElements = document.querySelectorAll("form");

formElements.forEach((formElement) =>
    formElement.addEventListener("submit", formSubmissionHandler)
);
View Compiled

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/spectre.css/0.5.9/spectre.min.css

External JavaScript

This Pen doesn't use any external JavaScript resources.