Cached Forms

When using caching mechanisms with Formie, it's worth taking note of some caveats to ensure things work correctly.

Template Caching #

If you are using the {% cache %} Twig functions in your templates, you'll need to be mindful of CSS and JS assets will not work. For example, say you have a Twig template with the following:

<html>
<head></head>
<body>
    {% cache %}

    {{ craft.formie.renderForm('contactForm') }}

    {% endcache %}
</body>
</html>

Here, we're using the {% cache %} tag to wrap out renderForm function. What this will do is cache the HTML generated by the function. Whilst this is beneficial, the renderForm function also registers the CSS and JS that Formie uses, and this won't be cached. You'll find on subsequent page-reloads that the CSS and JS will not render.

To get around this, you'll need to call craft.formie.registerAssets() outside of your cached content. This will tell Formie and Craft to render the CSS and JS for the form.

<html>
<head></head>
<body>
    {% cache %}

    {{ craft.formie.renderForm('contactForm') }}

    {% endcache %}

    {# Register the assets used by Formie, outside of the cached tags #}
    {% do craft.formie.registerAssets('contactForm') %}
</body>
</html>

Static Caching #

It's quite commonplace to implement full-page static caching on sites. For Craft, we highly recommend the Blitz plugin, but you can use any number of methods to statically cache your pages.

However, caching the form for every visitor poses an issue for Formie's CSRF tokens used to verify the integrity of form submissions. Indeed, this problem will be the same for any form on your site. To get around this, you'll need to implement a way to refresh the CSRF input in your forms through JavaScript.

Let's take a look at an example in action.

{% set form = craft.formie.forms.handle('contactForm').one() %}

{{ craft.formie.renderForm(form) }}

<script>
    // Wait until the DOM is ready
    document.addEventListener('DOMContentLoaded', (event) => {
        // Fetch the form we want to deal with
        let $form = document.querySelector('#formie-form-{{ form.id }}');

        // Find the CSRF token hidden input, so we can replace it
        let $csrfInput = $form.querySelector('input[name="CRAFT_CSRF_TOKEN"]');

        // Fetch the new token for the form and replace the CSRF input with our new one
        fetch('/actions/formie/csrf/input')
            .then(result => { return result.text(); })
            .then(result => { $csrfInput.outerHTML = result; });
    });
</script>

Here, we've combined rendering the form as we normally would, with some extra JavaScript. While this entire code will be cached and served exactly the same to each visitor, the JavaScript will be executed when the page is loaded. The above script makes a GET call to our actions/formie/csrf/input controller action, which returns a HTML input like below:

<input type="hidden" name="CRAFT_CSRF_TOKEN" value="O7fTQEDAHZf9l9luqYuLcM...">

We use this to inject and replace the cached CSRF token (which is completely invalid now), after which the form will submit as expected. It's a little bit of extra work to get things working with a static cached page, but it's worth it for significant performance gains!

Get started with Formie

Available for Craft 3. Get it from the plugin store.