Back to articles

Build a Fullstack SvelteKit App with Supabase Authentication

AuthorMajd Muhtaseb06/02/202515 minutes
Build a Fullstack SvelteKit App with Supabase Authentication

Introduction

SvelteKit is a powerful framework for building web applications with Svelte. Supabase provides a batteries-included backend with features like authentication, database, and storage. This article will guide you through setting up a fullstack SvelteKit application with Supabase authentication.

Prerequisites

  • Node.js and npm installed
  • A Supabase account and project created

Setup SvelteKit

First, let's create a new SvelteKit project:

npm create svelte@latest my-sveltekit-app
cd my-sveltekit-app
npm install

Choose the skeleton project option for a minimal setup.

Install Supabase Client

Install the Supabase JavaScript client:

npm install @supabase/supabase-js

Configure Supabase

Create a .env file in your project root and add your Supabase URL and API key:

VITE_SUPABASE_URL="YOUR_SUPABASE_URL"
VITE_SUPABASE_ANON_KEY="YOUR_SUPABASE_ANON_KEY"

Replace YOUR_SUPABASE_URL and YOUR_SUPABASE_ANON_KEY with your actual Supabase credentials. You can find these in your Supabase project settings.

Initialize the Supabase client in src/lib/supabaseClient.js:

// src/lib/supabaseClient.js
import { createClient } from '@supabase/supabase-js';

const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;

export const supabase = createClient(supabaseUrl, supabaseAnonKey);

Implement Authentication

Let's create a simple login form. Create src/routes/login/+page.svelte:

// src/routes/login/+page.svelte
<script>
  import { supabase } from '$lib/supabaseClient';
  import { goto } from '$app/navigation';
  import { auth } from '$lib/stores';

  let email = '';
  let password = '';
  let loading = false;

  async function handleLogin() {
    loading = true;
    const { data, error } = await supabase.auth.signInWithPassword({
      email,
      password,
    });

    if (error) {
      alert(error.message);
    } else {
      $auth = data.user;
      goto('/');
    }

    loading = false;
  }
</script>

<svelte:head>
  <title>Login</title>
</svelte:head>

<h1>Login</h1>

<input type="email" bind:value={email} placeholder="Email" />
<input type="password" bind:value={password} placeholder="Password" />
<button on:click={handleLogin} disabled={loading}>
  {#if loading}
    Logging in...
  {:else}
    Login
  {/if}
</button>

Create a similar src/routes/register/+page.svelte component for user registration, using supabase.auth.signUp.

// src/routes/register/+page.svelte
<script>
    import { supabase } from '$lib/supabaseClient';
    import { goto } from '$app/navigation';

    let email = '';
    let password = '';
    let loading = false;

    async function handleRegister() {
        loading = true;
        const { data, error } = await supabase.auth.signUp({
            email,
            password,
        });

        if (error) {
            alert(error.message);
        } else {
            // Optionally, send a confirmation email
            goto('/login'); // Redirect to login after registration
        }

        loading = false;
    }
</script>

<svelte:head>
    <title>Register</title>
</svelte:head>

<h1>Register</h1>

<input type="email" bind:value={email} placeholder="Email" />
<input type="password" bind:value={password} placeholder="Password" />
<button on:click={handleRegister} disabled={loading}>
    {#if loading}
        Registering...
    {:else}
        Register
    {/if}
</button>

Create an Auth store in src/lib/stores.js:

// src/lib/stores.js
import { writable } from 'svelte/store';

export const auth = writable(null);

Protect Routes

Create a root +layout.js file in the src/routes directory to implement route protection:

// src/routes/+layout.js
import { supabase } from '$lib/supabaseClient';
import { auth } from '$lib/stores';
import { redirect } from '@sveltejs/kit';

export const load = async ({ url }) => {
    supabase.auth.getSession().then(({ data: { session } }) => {
        auth.set(session?.user ?? null)
    })
	const { data: { user } } = await supabase.auth.getUser();
	if (user == null && url.pathname !== '/login' && url.pathname !== '/register') {
		throw redirect(302, '/login');
	}

	return {
		supabase,
		url: url.origin,
	};
};

Create a home page src/routes/+page.svelte:

<script>
    import { supabase } from '$lib/supabaseClient';
    import { auth } from '$lib/stores';
    import { goto } from '$app/navigation';
    async function handleLogout() {
        const { error } = await supabase.auth.signOut()
        if(!error) {
            $auth = null;
            goto('/login');
        }
    }
</script>

{#if $auth}
    <h1>Welcome, {$auth.email}!</h1>
    <button on:click={handleLogout}>Logout</button>
{:else}
    <p>You are not logged in.</p>
    <a href="/login">Login</a> | <a href="/register">Register</a>
{/if}

Conclusion

This article provided a quick start to building a fullstack SvelteKit application with Supabase authentication. You can expand upon this foundation by adding database interactions, storage, and more complex UI elements. Remember to handle errors gracefully and implement proper security measures in a production environment.