No response on ajax form post from SvelteKit protected route - hidden form recognized in Admin

I have a SSR SvelteKit app that has a form on it to collect basic info + a single image. All the routes on the app are protected via cookie verification + 302 redirects if the cookie doesn’t exist (or isn’t correct).

I setup a dummy form on a public route (https://www.guestcrafter.com/form/) to get Netlify to recognize the form, this works fine and I see the inputs in my build as expected. Here is the dummy form code for reference:

<script context="module">
	export const prerender = true
</script>

<form name="info" data-netlify="true" hidden>
    <input type="hidden" name="form-name" value="info" />
    <input type="text" required tabindex="0" name="Name" />
    <input type="text" required tabindex="0" name="Location" />
    <input type="file" tabindex="0" name="HeadShot" />
    <input type="text" required tabindex="0" name="PrimaryURL" />
    <input type="text" required tabindex="0" name="SecondaryURL" />
    <textarea required tabindex="0" name="Biography" maxlength="1000" />
    <textarea required tabindex="0" name="TechSetup" maxlength="500" />
    <input type="email" required tabindex="0" name="Email" />
    <input type="tel" required tabindex="0" name="Phone" />
    <button type="submit">
        Submit Form
    </button>
</form>

The actual form is SSR but since it is behind a simple custom auth solution Netlify doesn’t see it. So my assumption is submitting the form should basically be the same as a JS rendered form.

Here is the form component with all the markup + the handle function:

<script>
    let visible = true
    let sending = false

    import { slide } from "svelte/transition"

    let formData = {
        Name: '',
        Location: '',
        PrimaryURL: '',
        SecondaryURL: '',
        Biography: '',
        TechSetup: '',
        Email: '',
        Phone: '' 
    }

    let HeadShot

    const handleSubmit = event => {
        // button formatting
        sending = true

        // debug logging
        console.log(formData)
        
        event.preventDefault()

        // build the form data
        let processedForm = new FormData()
        processedForm.append('form-name', `info`)
        processedForm.append('Name', formData.Name)
        processedForm.append('Location', formData.Location)
        processedForm.append('HeadShot', HeadShot[0], HeadShot[0].name)
        processedForm.append('PrimaryURL', formData.PrimaryURL)
        processedForm.append('SecondaryURL', formData.SecondaryURL)
        processedForm.append('Biography', formData.Biography)
        processedForm.append('TechSetup', formData.TechSetup)
        processedForm.append('Email', formData.Email)
        processedForm.append('Phone', formData.Phone)

        // debug logging of FormData
        for (var pair of processedForm.entries()) {
            console.log(pair[0]+ ', ' + pair[1]); 
        }

        // send the form data
        fetch("/", {
            method: "POST",
            headers: { 'Content-Type': 'multipart/form-data' },
            body: new URLSearchParams(processedForm).toString()
        }).then(() => (visible = false)).catch(error => alert(error))
    }
</script>

{#if visible}
    <form on:submit|preventDefault={handleSubmit}
        out:slide={{duration: 300}}
        method="POST" data-netlify="true"
        name="info"
    >

        <label>
            <header>Display Name</header>
            <input type="text" required tabindex="0" name="Name" bind:value={formData.Name} />
            <footer>Example: Mary Gunn or Mary G.</footer>
        </label>
        <label>
            <header>Location</header>
            <input type="text" required tabindex="0" name="Location" bind:value={formData.Location} />
            <footer>Example: Texas or Kansas City (can be general)</footer>
        </label>
        <label>
            <header>Head Shot</header>
            <input type="file" tabindex="0" name="HeadShot" bind:value={HeadShot} />
            <footer>Your favorite profile pic is great</footer>
        </label>
        <label>
            <header>Primary Link URL</header>
            <input type="text" required tabindex="0" name="PrimaryURL" bind:value={formData.PrimaryURL} />
            <footer>Your top link (youtube channel, blog, instagram etc)</footer>
        </label>
        <label>
            <header>Secondary Link URL</header>
            <input type="text" required tabindex="0" name="SecondaryURL" bind:value={formData.SecondaryURL} />
            <footer>Your secondary link (youtube channel, blog, instagram etc)</footer>
        </label>
        <label>
            <header>Short Bio</header>
            <textarea required tabindex="0" name="Biography" maxlength="1000" bind:value={formData.Biography} />
            <footer>Used on our website. 1000 characters max.</footer>
        </label>
        <label>
            <header>Tech Setup</header>
            <textarea required tabindex="0" name="TechSetup" maxlength="500" bind:value={formData.TechSetup} />
            <footer>Breifly describe your tech setup for making videos. Do you use your phone? Web cam? Etc.</footer>
        </label>

        <h2>Private Contact Info</h2>
        <p>We won't share this information with anyone. We like to have this information just-in-case.</p>
        <label>
            <header>Your Email</header>
            <input type="email" required tabindex="0" name="Email" bind:value={formData.Email} />
            <footer>Best email for personal contact.</footer>
        </label>
        <label>
            <header>Phone Number</header>
            <input type="tel" required tabindex="0" name="PhoneNumber" bind:value={formData.Phone} />
            <footer>Best phone to reach you. (We sometimes text to remind about the show.)</footer>
        </label>

        <button type="submit">
            {#if sending}
                Sending...
            {:else}
                Submit Form
            {/if}
        </button>
    </form>
{:else}
    <p in:slide={{delay: 300, duration: 300}}>Thank you for sending in your info. We will reach out if we have any questions.</p>
{/if}

If you go to the form and try submitting it, you’ll see that the debug data logs correctly and as expected.

I have tried many iterations of the form and the function and have never gotten anything to register in the Netlify admin, spam or otherwise. I have also tried the form with the file field excluded and the content type set to

application/x-www-form-urlencoded

I have also tried sending the post body in all the different methods in the docs… You can see I am using the “body: new URLSearchParams(processedForm).toString()” in the above example, but I have also just sent the raw FormData, and also tried the

function encode(data)

example from the docs. All with the same result: The post finishes with out any feedback or error. And nothing shows up in the admin.

I have no idea what else I could try to get this working. I am open to all suggestions.

Thanks.

Welcome to the forums @Jovian

I believe you might find a solution in this post Sveltekit Form Post 404 - #4 by mgodeck

Thanks for the welcome, @coelmay.

The major difference I have, as outlined, my route is SSR but it is also behind basic auth.

I changed everything I have to match mgodeck’s example and got the same result. My submit function looks like this:

const handleSubmit = event => {
        // button formatting
        sending = true

        // debug logging
        console.log(formData)
        
        event.preventDefault()

        // build the form data
        let processedForm = new FormData(formHelper)

        // debug logging of FormData
        for (var pair of processedForm.entries()) {
            console.log(pair[0]+ ', ' + pair[1]); 
        }

        // send the form data
        fetch("/", {
            method: "POST",
            headers: { 'Content-Type': 'multipart/form-data' },
            body: new URLSearchParams(processedForm).toString()
        }).then(() => (visible = false)).catch(error => alert(error))
    }

I’m leaning toward this just not working on a SSR protected route even though my dummy form is recognized, my fields show up on the build log, and all my code looks good.

If you go to the page I linked in my original post and try to submit the form you’ll see the debug logging in the console.

The thing that mgodeck said in his post that makes me think that mine just won’t work is that he had to use pre-render mode, which delivers a 100% static page.

Hi, @Jovian. It is specifically being caused by the path being used (which is /) being handled by a Function. The form POST only works when the path being used is being responded to by the CDN node itself. For URLs that are being proxied to a Function at Netlify, the form submissions isn’t seen by the CDN and so the form submission is not processed.

There is a workaround. I do see the path /form/ is handled by the static file server and not a Function. If you POST to that path (meaning change the line below as shown) it should work.

Just change this line:

        fetch("/", {

to this:

        fetch("/form/", {

Note, it does not have to be that path. Any path that has an actual HTML file for it can be used. Likely
any URL for a “real” file will work including image, javascript, or CSS file urls but I haven’t tested that. I am sure (as sure as I can be) though that /form/ will work.

If that doesn’t resolve the issue, though, please let us know with a reply here and we will keep troubleshooting.

2 Likes

@luke, that got the form submission to register and all all of the text fields are intact. That solution makes sense and I’m kind of disappointed that I didn’t think to try that.

The file (single image) field is returning blank on the admin screen. I can’t find anything that I’ve done wrong looking at the docs.

I tried appending the file this way too and it didn’t work:

processedForm.append('HeadShot', HeadShot[0])

I am new here… Should I start a new thread for the image upload problem?

Thanks.

hi there, yes, please make a new thread on the image upload issue (did you search for one first? that might be faster if someone already solved it) and link the two (by commenting here and pasting the link).

thanks!

Here is the new thread:

I have really tried a lot of fixes and come up with nothing. Any help is appreciated.