How to use SvelteKit with Netlify Forms

swyx - Apr 25 '21 - - Dev Community

This is a direct answer to this SvelteKit issue. I see a lot of JS developers struggle with using Netlify Forms, so I want to help.

9 Min Video Walkthru

This is the video version of the instructions below.

Context

Netlify Forms were designed primarily for traditional static site generators like Hugo and Jekyll, using HTML crawling to initialize forms. The idea is like this:

  • Your static site is a folder of .html files
  • Netlify crawls all of them looking for <form netlify> tags (or <form data-netlify="true">, because some projects like Create React App or Gatsby helpfully delete unrecognized attributes for you, what fun)
  • For each detected tag, it sets up a dedicated "form" which is pretty much an append-only schemaless document store of all submissions.
  • These are received via a special endpoint set up at the domain root (/) looking for POST requests with "Content-Type": "application/x-www-form-urlencoded" (or with multipart for file uploads)
  • Crucially: If no forms are detected, this feature doesn't work.

Many JS framework users struggle to use them because:

  • the form needs to exist in HTML at build time but JS frameworks render them at runtime.
  • SvelteKit introduces another wrinkle in that you have to decide whether to prerender the form.
  • JS framework users also often want to use AJAX to offer a better UX. So again you have to do things slightly differently than with pure HTML forms.

Optional Features You May Want

What I cover here is just the simplest solution to help people solve the immediate pain point. You can also:

  • leave your "helper form" hidden at build time and render the real form clientside
  • upload files with Netlify Forms (underrated)
  • do progressive enhancement of behavior from HTML to JS.
  • For spam prevention, there is support for honeypot and recaptcha. See demo apps for setup instructions.
  • You can also trigger Netlify functions on successful submissions to do arbitrary logic (like notify you on Slack or create an account)

I'll come back and add that to this blogpost if I ever get the time. Pls yell at me to indicate interest or i wont do it.

SvelteKit Setup Instructions

Project Init

Go through the standard SvelteKit setup:

npm init svelte@next myapp       
cd myapp
npm i
Enter fullscreen mode Exit fullscreen mode

You can set up GitHub/Netlify either thru CLI or UI - I found some bugs with Netlify CLI when I tried this but it might be fixed by the time you read this:

# optional: set up git/github/netlify deploy via CLI
git init && git add . && git commit -m "hi"
gh repo create # have the github cli installed
ntl init # netlify cli
Enter fullscreen mode Exit fullscreen mode

or you can just use the UIs for each service

Add SvelteKit Netlify Adapter

npm i -D @sveltejs/adapter-netlify@next       
Enter fullscreen mode Exit fullscreen mode

then add it to svelte.config.cjs:

const adapter = require('@sveltejs/adapter-netlify');
module.exports = {
    kit: {
        adapter: adapter(), // currently the adapter does not take any options
        target: '#svelte',
        prerender: {
            crawl: true,
            enabled: true,
            force: true,
            pages: ['*'],
        },
    }
};
Enter fullscreen mode Exit fullscreen mode

then add a netlify.toml file at project root:

[build]
  command = "npm run build"
  publish = "build/"
  functions = "functions/"
Enter fullscreen mode Exit fullscreen mode

You can see these instructions in my netlify adapter docs.

Add the Prerendered Form

Head to /src/routes/index.svelte and add the form accordingly.

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


<h1>Welcome to SvelteKit</h1>
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>

<form name="test" method="post" netlify netlify-honeypot="bot-field">
    <input type="hidden" name="form-name" value="test" />
    <input type="text" name="bot-field" />
    <p>
        <label>Your Name: <input type="text" name="name" /></label>
    </p>
    <p>
        <label>Your Email: <input type="email" name="email" /></label>
    </p>
    <p>
        <label>Message: <textarea name="message" /></label>
    </p>
    <p>
        <button type="submit">Send</button>
    </p>
</form>
Enter fullscreen mode Exit fullscreen mode

Deploy

Now you should be able to push to GitHub and have it build and render the form successfully.

Examples:

Screenshots

image

image

image

image

Graveyard: AJAX Example

Warning: this is untested code - I ran out of time but dumping it here anyway in case it helps someone

If you want an AJAX experience for your form here is some untested code I was working on that may help you get going:

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

<script>
  let isSubmitting = false;

  const handleSubmit = (e) => {
    let myForm = document.getElementById("test");
    let formData = new FormData(myForm);
    isSubmitting = true;
    return fetch("/", {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: new URLSearchParams(formData).toString(),
    })
      .then(() => {
        console.log("Form successfully submitted");
        isSubmitting = false;
        myForm.reset();
      })
      .catch((error) => {
        alert(error);
        isSubmitting = false;
      });
  };
</script>

<h1>Welcome to SvelteKit</h1>
<p>
  Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation
</p>

<form
  id="test"
  name="test"
  on:submit|preventDefault={handleSubmit}
  netlify
  netlify-honeypot="bot-field"
>
  <input type="hidden" name="form-name" value="test" />
  <input type="text" name="bot-field" style="opacity: 0" />
  <p>
    <label>Your Name: <input type="text" name="name" /></label>
  </p>
  <p>
    <label>Your Email: <input type="email" name="email" /></label>
  </p>
  <p>
    <label>Message: <textarea name="message" /></label>
  </p>
  <p>
    {#if isSubmitting}
      <div>Submitting</div>
    {:else}
      <button type="submit">Send</button>
    {/if}
  </p>
</form>
Enter fullscreen mode Exit fullscreen mode
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .