How to build Contact us page with Sveltekit and Supabase
Tags: svelte, sveltekit
Published: 2022-01-15 06:51:53

---
title: How to build Contact us page with Sveltekit and Supabase
published: true
date: 
tags: svelte, sveltekit
canonical_url: https://eternaldev.com/blog/how-to-build-contact-us-page-with-sveltekit-and-supabase
cover_image: https://www.eternaldev.com/static/2689e4f9bf75db762105c6da6ce15fbd/aa1b3/sveltekit_final_contact_poster.webp
---

## Introduction

Building an online presence takes time and effort and you will need to persuade your potential customers to make the intial contact. It is essential to have a good looking and working contact form in your website. We will create a form in SvelteKit and then use Supabase as a backend to store the form data.

## Getting Started with SvelteKit

SvelteKit is a framework for building web applications that has an awesome developer experience and you can create powerful apps. It combines the power of Server Side Rendering (SSR) and navigation benefits of Single Page Applications (SPA)

Don’t worry if you are unfamiliar with these terms above, it means that your project gets the benefit of having a SEO (Search Engine Optimization) to rank higher in search engine. When you navigate from one page to another, the whole site is not reloaded like in traditional applications.

If you want to learn more on these, visit the SvelteKit Website

[https://kit.svelte.dev/docs](https://kit.svelte.dev/docs)

> Note: As of writing of this post, SvelteKit is not Production ready and so please check the latest version of the SvelteKit if you are planning to build a production ready application.

## Creating your first project

Let’s create a new project.

It is easy to get started with a new project. You can just type the following command on the terminal and you will get a scaffold of SvelteKit app ready to use.

```
npm init svelte@next my-app
cd my-app
npm install
npm run dev
```

SvelteKit is an opinionated framework which has file-system based routing. Creating a new page is as easy as creating an new file in the `src\routes` folder. In the scaffold, you can find the `index.svelte` file inside the `src\routes` folder which will be used as the landing page of the application.

## What are we building today ?

We are going to build a couple of pages for a fictional company which will have a contact us form and it will store the data to Supabase.

[![ContactUsPage.png](https://eternaldev.com/static/bd1867cf83804d52a70a3c2bcae9a991/ec09f/contactuspage.png "ContactUsPage.png")](/static/bd1867cf83804d52a70a3c2bcae9a991/ec09f/contactuspage.png)

## Creating the form data in supabase

Create a new project in Supabase. We have named the project **`contact-page-demo`.** You might have to wait for a few minutes for the project to be ready. Then you are good to go.

Create a new table in your project which will hold the data. Each entry made by any visitor in your website will be stored as a new row in the table. We are going to fetch the following details from the users and store them to the database

1. Name
2. Email
3. Message

User will be filling in these three fields. We will store this data in the table and then use the data to get in touch with the user

### Steps in Supabase

1. Create a table `contact` in your supabase project
2. Add the following fields to the table

[![Supabase_database_setup.png](https://eternaldev.com/static/4f66ae45edc5ba09758374663ba544ad/fdaf8/supabase_database_setup.png "Supabase\_database\_setup.png")](/static/4f66ae45edc5ba09758374663ba544ad/fdaf8/supabase_database_setup.png)

## Integration of SvelteKit with Supabase

We need a way to integrate Supabase in SvelteKit so that we can store the data in the table. After updating the data in the table, we can make use of the data in some way to send some personalized response to the user.

Let’s create a `.env` file at the root of the project to store the variable for the Supabase url and key

```
VITE_SVELTE_APP_SUPABASE_URL=xxxxx
VITE_SVELTE_APP_SUPABASE_ANON_KEY=xxxxxx
```

> Adding `VITE` to the start of the variable makes it visible in the client side code. Check the documentation of [vite environment variables](https://vitejs.dev/guide/env-and-mode.html#env-variables)

You can find these variables in the following pages

1. Supabase Anon Key - Settings → API → Project API Keys
2. Supabase Url - Settings → API → Config

Add those details to the `.env` file and then you can create a new file in the `src` folder called `supabaseClient.ts`

```js
import { createClient } from '@supabase/supabase-js'

const supabaseUrl = import.meta.env.VITE_SVELTE_APP_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SVELTE_APP_SUPABASE_ANON_KEY;

export const supabase = createClient(supabaseUrl, supabaseAnonKey)
```

## Creating the layout file in SvelteKit

We will start by designing the structure of the website. SvelteKit provides a nice way to define a layout file which will be used in all the routes. You can do this using the `__layout.svelte` file in the `routes` folder. The layout file should contain a `slot` element which will be replaced by the content in each of the routes.

Awesome feature if you ask me and which will save a lot of time when defining the project. Modern web applications will need to have some navigation bar. This can be defined in the layout file and magically all the routes will now have it.

[![NavBar.png](https://eternaldev.com/static/3d54cd92f620e3c08bc3035da2ebac39/2e694/navbar.png "NavBar.png")](/static/3d54cd92f620e3c08bc3035da2ebac39/2e694/navbar.png)

```js
<div class="content">
    <nav>
        <span class="company-title">Company Name</span>
        <ul>
            <li><a href="/">Home</a></li>
            <li><a href="/about">About</a></li>
            <li><a href="/contact">Contact Us</a></li>
        </ul>
        <div>
            <a href="/login">Login</a>
        </div>
    </nav>
    
    <slot></slot>
</div>

<style>

    .content {
        padding: 20px 10px;
    }

    nav {
        margin: 0;
        display: flex;
        justify-content: space-between;
        align-items: center;
    }
    
    ul {
        list-style-type: none;
        display: flex;
        gap: 60px;
    }

    a {
        text-decoration: none;
        color: black;
    }

    a:hover {
        text-decoration: underline;
    }

    .company-title {
        font-size: large;
        font-weight: bold;
    }
</style>
```

The important thing to note here is the after the `nav` element, we have the `slot` element which will contain the different content of the routes.

## Installing carbon component for svelte

We will make use of the carbon component library created for svelte. This step is optional and we are using it to make our application look good and get some input form elements out of the box. So let’s install the carbon component.

```
npm i -D carbon-components-svelte
```

After installing the npm package, we need to add one of the carbon themes in our layout file to start using the carbon components.

Let’s add the css file to the `src/__layout.svelte` file

```js
<script lang="ts">
    import "carbon-components-svelte/css/g10.css";
</script>
```

[![CarbonDesignSystem.png](https://eternaldev.com/static/a0179f85df176277b5e6fb80bd725084/3e992/carbondesignsystem.png "CarbonDesignSystem.png")](/static/a0179f85df176277b5e6fb80bd725084/3e992/carbondesignsystem.png)

## Creating the contact us page

We have all the things ready to create the contact page with forms which will connect with Supabase to store the data.

[![ContactUsPage.png](https://eternaldev.com/static/bd1867cf83804d52a70a3c2bcae9a991/ec09f/contactuspage.png "ContactUsPage.png")](/static/bd1867cf83804d52a70a3c2bcae9a991/ec09f/contactuspage.png)

### Installing the svelte-forms-lib

We are going to use an npm library for managing the form elements. This might be an overkill for this small form but it will help in reducing the code. We can install the package using the following code

```
npm install svelte-forms-lib
```

For more details on this package, please check the [Svelte forms lib website](https://svelte-forms-lib-sapper-docs.vercel.app/introduction)

This library provides multiple ways to create the form. You can use the `createForm` method to create a new form. It will provide multiple variables which will help in building the form and the page.

You can have a look at the [basic example here](https://svelte-forms-lib-sapper-docs.vercel.app/basic)

### Creating the Form component

```js
<script>

    import { TextInput, TextArea, Button, FormGroup, Form } from "carbon-components-svelte";
		import { createForm } from "svelte-forms-lib";

    const { form, errors, handleChange, handleSubmit, isSubmitting } = createForm({
        initialValues: { name: "", email: "", message: "" },
        onSubmit: async values => {
            console.log(result);
      }
    });

</script>
<div class="page">

    <h2>Contact Us</h2>
    <p>Enter the details to get in touch with us. You can remove your email at any time after this.</p> <br/><br/>
    
    <Form on:submit={handleSubmit}>

        <FormGroup>
            <TextInput labelText="Name" name="name" 
                on:change={handleChange} bind:value={$form.name}/>
        </FormGroup>
    
        <FormGroup>
            <TextInput labelText="Email" name="email" type="email" 
            on:change={handleChange} bind:value={$form.email}/>
        </FormGroup>
    
        
        <FormGroup>
            <TextArea labelText="Message" name="message" type="textarea"
            on:change={handleChange} bind:value={$form.message}/>
        </FormGroup>
  
        <Button type="submit" disabled={$isSubmitting}>Submit</Button>
    </Form>
</div>
```

Let’s breakdown the above code

1. Import the `createForm` from the `svelte-forms-lib`
2. Pass the `initialValues` which are empty string to the `createForm` method
3. Pass an `onSubmit` handler to process the form data on click of the `submit` button

Now we can use the form component we downloaded as part of Carbon components. `Form` and `FormGroup` component can be used to group the elements.

1. `TextInput` component is used to enter text for the user. Set the name property to the name of the field. Bind the value of the text field to the `$form.name`. Setting the `on:change` event to the `handleChange` method of the svelte-forms-lib
2. `TextArea` component is used to display a text area to enter a bigger text. All the other properties are same of the other component
3. `Button` component can be used to show the “Submit” button

### Adding the API to call

Let’s add the API call on the submit of the form. We will make use of the `supabaseClient` file which we created before. We will take the form data and send it to the supabase table to store it there.

```js
<script>
    import { supabase } from "../../supabaseClient";

		let apiResult = null;

    const { form, errors, handleChange, handleSubmit, isSubmitting, handleReset } = createForm({
        initialValues: { name: "", email: "", message: "" },
        onSubmit: async values => {

            try {
                var result = await supabase.from("contact").insert(values);

                if (result.data != null) {
                    apiResult = true;
                } else {
                    apiResult = false;
                }

            } catch (ex) {
                apiResult = false;
            }

            handleReset();
      }
    });
</script>
```

The main thing to note here is `supabase.from("contact").insert(values)`. This will call the supabase API and then we are awaiting the result. The result will be stored in the `result` variable. When the `data` property in `result`is not null, then the API is successful. If there is any error or exception, we are setting the `apiResult` to false.

[![ContactUsDatabase.png](https://eternaldev.com/static/2d7b54699c3a37abb8381425fa950afc/7ebf9/contactusdatabase.png "ContactUsDatabase.png")](/static/2d7b54699c3a37abb8381425fa950afc/7ebf9/contactusdatabase.png)

### Adding success and error states

We will make use of `InlineNotification` component to show the result of the API call. Based on the success or failure, we are showing the message in the UI to notify the user after the event.

```js
<h2>Contact Us</h2>
    <p>Enter the details to get in touch with us. You can remove your email at any time after this.</p> <br/><br/>
    
    {#if apiResult != null}

        {#if apiResult == true}
        <InlineNotification
            lowContrast
            kind="success"
            title="Success:"
            subtitle="Your message has been received"
        />
        {:else}
        <InlineNotification lowContrast kind="error"
            title="Error:"
            subtitle="An internal server error occurred."
        />

        {/if}    
    {/if}
```

### Adding Validation

We can add simple validation for the form to make sure that form is not submitted with empty data. This can be achieved using simple javascript by checking the variables. If you have a more complicated validation, you can use the `Yup` package to show more advanced validation logic and add user-friendly validation messages.

```
npm install yup
```

This step is also optional, you can add more simple logic to check the variables, but this will show how to use the validation for bigger froms.

```js
const validationSchema = yup.object().shape({
      name: yup.string().required('Please enter a name'),
      email: yup.string().email().required('Please enter a valid email'),
      message: yup.string().required('Please enter a message')
});

const { form, errors, handleChange, handleSubmit, isSubmitting, handleReset } = createForm({
        initialValues: { name: "", email: "", message: "" },
        validationSchema: validationSchema,
        onSubmit: async values => {
						.......
				}
});
```

Let’s breakdown the above code

1. We are creating a `validationSchema` variable which will contain the validations
2. Each property can have it’s own validation logic with custom validation message.
3. `name` property is a string value and it’s required. The string entered in the required method will be the custom validation message
4. `email` property is a string value. It should follow the general validation of email and it’s required
5. `message` property is a string value. It is also a required property.
6. Once we have defined the variables for the validations, we can assign it to the `validationSchema` property in `createForm` which will handle the validations.

```js
<Form on:submit={handleSubmit}>

        <FormGroup>
            <TextInput labelText="Name" name="name" 
                on:change={handleChange} bind:value={$form.name}
                invalid={$errors.name.length > 0} invalidText={$errors.name}/>
        </FormGroup>
    
        <FormGroup>
            <TextInput labelText="Email" name="email" type="email" 
            on:change={handleChange} bind:value={$form.email}
            invalid={$errors.email.length > 0} invalidText={$errors.email}/>
        </FormGroup>
    
        
        <FormGroup>
            <TextArea labelText="Message" name="message" type="textarea"
            on:change={handleChange} bind:value={$form.message}
            invalid={$errors.message.length > 0} invalidText={$errors.message}/>
        </FormGroup>
    
    
        <Button type="submit" disabled={$isSubmitting}>Submit</Button>
    </Form>
```

[![ContactUsPageValidation.png](https://eternaldev.com/static/dc8edf8094c540df648bdf0842f936cb/69d6b/contactuspagevalidation.png "ContactUsPageValidation.png")](/static/dc8edf8094c540df648bdf0842f936cb/69d6b/contactuspagevalidation.png)

We just have to update the `TextInput` and `TextArea` components to show the validation messages using the `invalid` and `invalidText` props.

### Finishing up the form

That’s all is needed to store the contact data in Supabase table. You can check the database on who has contacted and respond back to their message. You can even extend this setup to trigger an email once the contact form is filled using a custom backend app

Let’s look at the whole code for the contact page

```js
<script>
    import { supabase } from "../../supabaseClient";

    import { createForm } from "svelte-forms-lib";
    import * as yup from "yup";

    import { TextInput, TextArea, Button, FormGroup, Form, InlineNotification } from "carbon-components-svelte";

    const validationSchema = yup.object().shape({
        name: yup.string().required('Please enter a name'),
        email: yup.string().email().required('Please enter a valid email'),
        message: yup.string().required('Please enter a message')
    });

    let apiResult = null;

    const { form, errors, handleChange, handleSubmit, isSubmitting, handleReset } = createForm({
        initialValues: { name: "", email: "", message: "" },
        validationSchema: validationSchema,
        onSubmit: async values => {

            try {
                var result = await supabase.from("contact").insert(values);

                if (result.data != null) {
                    apiResult = true;
                } else {
                    apiResult = false;
                }

            } catch (ex) {
                apiResult = false;
            }

            handleReset();
      }
    });

</script>
<div class="page">

    <h2>Contact Us</h2>
    <p>Enter the details to get in touch with us. You can remove your email at any time after this.</p> <br/><br/>
    
    {#if apiResult != null}

        {#if apiResult == true}
        <InlineNotification
            lowContrast
            kind="success"
            title="Success:"
            subtitle="Your message has been received"
        />
        {:else}
        <InlineNotification lowContrast kind="error"
            title="Error:"
            subtitle="An internal server error occurred."
        />

        {/if}    
    {/if}
    <Form on:submit={handleSubmit}>

        <FormGroup>
            <TextInput labelText="Name" name="name" 
                on:change={handleChange} bind:value={$form.name}
                invalid={$errors.name.length > 0} invalidText={$errors.name}/>
        </FormGroup>
    
        <FormGroup>
            <TextInput labelText="Email" name="email" type="email" 
                on:change={handleChange} bind:value={$form.email}
                invalid={$errors.email.length > 0} invalidText={$errors.email}/>
        </FormGroup>
    
        
        <FormGroup>
            <TextArea labelText="Message" name="message" type="textarea"
                on:change={handleChange} bind:value={$form.message}
                invalid={$errors.message.length > 0} invalidText={$errors.message}/>
        </FormGroup>
    
    
        <Button type="submit" disabled={$isSubmitting}>Submit</Button>
    </Form>
</div>

<style>
    .page {
        margin: 40px;
    }
</style>
```

## Conclusion

SvelteKit provides a really nice set of tool for developers to build website and focus on the creative side. We have looked at how to create a contact us page in SvelteKit and integrate with Supabase in this article.

Let us know your thoughts on this and anything else on your mind in our discord server

Discord - [https://discord.gg/AUjrcK6eep](https://discord.gg/AUjrcK6eep)