docs: Update User management quickstart to Svelte 5 (#37646)

* General updates and switch to Svelte 5

* Update code example to Svelte 5

* Update some examples to use codesamples component

* Add Svelte to codesample component

* Final code updates

* Add more CodeSample components
This commit is contained in:
Chris Chinchilla
2025-08-05 15:40:50 +02:00
committed by GitHub
parent 3b3f24aa39
commit 74a1cd8dfe
17 changed files with 1432 additions and 2997 deletions

View File

@@ -17,11 +17,11 @@ If you get stuck while working through this guide, refer to the [full example on
## Building the app
Let's start building the Svelte app from scratch.
Start building the Svelte app from scratch.
### Initialize a Svelte app
We can use the Vite Svelte TypeScript Template to initialize an app called `supabase-svelte`:
You can use the Vite Svelte TypeScript Template to initialize an app called `supabase-svelte`:
```bash
npm create vite@latest supabase-svelte -- --template svelte-ts
@@ -29,14 +29,14 @@ cd supabase-svelte
npm install
```
Then let's install the only additional dependency: [supabase-js](https://github.com/supabase/supabase-js)
Install the only additional dependency: [supabase-js](https://github.com/supabase/supabase-js)
```bash
npm install @supabase/supabase-js
```
And finally we want to save the environment variables in a `.env`.
All we need are the API URL and the `anon` key that you copied [earlier](#get-the-api-keys).
Finally, save the environment variables in a `.env`.
All you need are the API URL and the `anon` key that you copied [earlier](#get-the-api-keys).
<$CodeTabs>
@@ -47,221 +47,46 @@ VITE_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
</$CodeTabs>
Now that we have the API credentials in place, let's create a helper file to initialize the Supabase client. These variables will be exposed
on the browser, and that's completely fine since we have [Row Level Security](/docs/guides/auth#row-level-security) enabled on our Database.
Now you have the API credentials in place, create a helper file to initialize the Supabase client. These variables will be exposed on the browser, and that's fine since you have [Row Level Security](/docs/guides/auth#row-level-security) enabled on the Database.
<$CodeTabs>
```js name=src/supabaseClient.ts
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)
```
</$CodeTabs>
<$CodeSample
path="user-management/svelte-user-management/src/supabaseClient.ts"
meta="name=src/supabaseClient.ts"
/>
### App styling (optional)
An optional step is to update the CSS file `src/app.css` to make the app look nice.
You can find the full contents of this file [here](https://raw.githubusercontent.com/supabase/supabase/master/examples/user-management/svelte-user-management/src/app.css).
Optionally, update the CSS file `src/app.css` to make the app look nice.
You can find the full contents of this file [on GitHub](https://raw.githubusercontent.com/supabase/supabase/master/examples/user-management/svelte-user-management/src/app.css).
### Set up a login component
Let's set up a Svelte component to manage logins and sign ups. We'll use Magic Links, so users can sign in with their email without using passwords.
Set up a Svelte component to manage logins and sign ups. It uses Magic Links, so users can sign in with their email without using passwords.
<$CodeTabs>
```html name=src/lib/Auth.svelte
<script lang="ts">
import { supabase } from '../supabaseClient'
let loading = false
let email = ''
const handleLogin = async () => {
try {
loading = true
const { error } = await supabase.auth.signInWithOtp({ email })
if (error) throw error
alert('Check your email for login link!')
} catch (error) {
if (error instanceof Error) {
alert(error.message)
}
} finally {
loading = false
}
}
</script>
<div class="row flex-center flex">
<div class="col-6 form-widget" aria-live="polite">
<h1 class="header">Supabase + Svelte</h1>
<p class="description">Sign in via magic link with your email below</p>
<form class="form-widget" on:submit|preventDefault="{handleLogin}">
<div>
<label for="email">Email</label>
<input
id="email"
class="inputField"
type="email"
placeholder="Your email"
bind:value="{email}"
/>
</div>
<div>
<button type="submit" class="button block" aria-live="polite" disabled="{loading}">
<span>{loading ? 'Loading' : 'Send magic link'}</span>
</button>
</div>
</form>
</div>
</div>
```
</$CodeTabs>
<$CodeSample
path="user-management/svelte-user-management/src/lib/Auth.svelte"
meta="name=src/lib/Auth.svelte"
/>
### Account page
After a user is signed in we can allow them to edit their profile details and manage their account.
Let's create a new component for that called `Account.svelte`.
After a user is signed in, allow them to edit their profile details and manage their account.
Create a new component for that called `Account.svelte`.
<$CodeTabs>
```tsx name=src/lib/Account.svelte
<script lang="ts">
import { onMount } from 'svelte'
import type { AuthSession } from '@supabase/supabase-js'
import { supabase } from '../supabaseClient'
export let session: AuthSession
let loading = false
let username: string | null = null
let website: string | null = null
let avatarUrl: string | null = null
onMount(() => {
getProfile()
})
const getProfile = async () => {
try {
loading = true
const { user } = session
const { data, error, status } = await supabase
.from('profiles')
.select('username, website, avatar_url')
.eq('id', user.id)
.single()
if (error && status !== 406) throw error
if (data) {
username = data.username
website = data.website
avatarUrl = data.avatar_url
}
} catch (error) {
if (error instanceof Error) {
alert(error.message)
}
} finally {
loading = false
}
}
const updateProfile = async () => {
try {
loading = true
const { user } = session
const updates = {
id: user.id,
username,
website,
avatar_url: avatarUrl,
updated_at: new Date().toISOString(),
}
const { error } = await supabase.from('profiles').upsert(updates)
if (error) {
throw error
}
} catch (error) {
if (error instanceof Error) {
alert(error.message)
}
} finally {
loading = false
}
}
</script>
<form on:submit|preventDefault="{updateProfile}" class="form-widget">
<div>Email: {session.user.email}</div>
<div>
<label for="username">Name</label>
<input id="username" type="text" bind:value="{username}" />
</div>
<div>
<label for="website">Website</label>
<input id="website" type="text" bind:value="{website}" />
</div>
<div>
<button type="submit" class="button primary block" disabled="{loading}">
{loading ? 'Saving ...' : 'Update profile'}
</button>
</div>
<button type="button" class="button block" on:click={() => supabase.auth.signOut()}> Sign Out
</button>
</form>
```
</$CodeTabs>
<$CodeSample
path="/user-management/svelte-user-management/src/lib/Account.svelte"
lines={[[1, 4], [7, 11], [14, 33], [34,53], [55,73], [75,-1]]}
meta="src/lib/Account.svelte"
/>
### Launch!
Now that we have all the components in place, let's update `App.svelte`:
Now that you have all the components in place, update `App.svelte`:
<$CodeTabs>
```html name=src/App.svelte
<script lang="ts">
import { onMount } from 'svelte'
import { supabase } from './supabaseClient'
import type { AuthSession } from '@supabase/supabase-js'
import Account from './lib/Account.svelte'
import Auth from './lib/Auth.svelte'
let session: AuthSession | null
onMount(() => {
supabase.auth.getSession().then(({ data }) => {
session = data.session
})
supabase.auth.onAuthStateChange((_event, _session) => {
session = _session
})
})
</script>
<div class="container" style="padding: 50px 0 100px 0">
{#if !session}
<Auth />
{:else}
<Account {session} />
{/if}
</div>
```
</$CodeTabs>
<$CodeSample
path="user-management/svelte-user-management/src/App.svelte"
meta="name=src/App.svelte"
/>
Once that's done, run this in a terminal window:
@@ -273,7 +98,7 @@ And then open the browser to [localhost:5173](http://localhost:5173) and you sho
<Admonition type="tip">
Svelte uses Vite and the default port is `5173`, Supabase uses `port 3000`. To change the redirection port for Supabase go to: `Authentication > Settings` and change the `Site Url` to `http://localhost:5173/`
Svelte uses Vite and the default port is `5173`, Supabase uses `port 3000`. To change the redirection port for Supabase go to: **Authentication > URL Configuration** and change the **Site URL** to `http://localhost:5173/`
</Admonition>
@@ -285,118 +110,21 @@ Every Supabase project is configured with [Storage](/docs/guides/storage) for ma
### Create an upload widget
Let's create an avatar for the user so that they can upload a profile photo. We can start by creating a new component:
Create an avatar for the user so that they can upload a profile photo. Start by creating a new component:
<$CodeTabs>
```html name=src/lib/Avatar.svelte
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import { supabase } from '../supabaseClient'
export let size: number
export let url: string | null = null
let avatarUrl: string | null = null
let uploading = false
let files: FileList
const dispatch = createEventDispatcher()
const downloadImage = async (path: string) => {
try {
const { data, error } = await supabase.storage.from('avatars').download(path)
if (error) {
throw error
}
const url = URL.createObjectURL(data)
avatarUrl = url
} catch (error) {
if (error instanceof Error) {
console.log('Error downloading image: ', error.message)
}
}
}
const uploadAvatar = async () => {
try {
uploading = true
if (!files || files.length === 0) {
throw new Error('You must select an image to upload.')
}
const file = files[0]
const fileExt = file.name.split('.').pop()
const filePath = `${Math.random()}.${fileExt}`
const { error } = await supabase.storage.from('avatars').upload(filePath, file)
if (error) {
throw error
}
url = filePath
dispatch('upload')
} catch (error) {
if (error instanceof Error) {
alert(error.message)
}
} finally {
uploading = false
}
}
$: if (url) downloadImage(url)
</script>
<div style="width: {size}px" aria-live="polite">
{#if avatarUrl} <img src={avatarUrl} alt={avatarUrl ? 'Avatar' : 'No image'} class="avatar image"
style="height: {size}px, width: {size}px" /> {:else}
<div class="avatar no-image" style="height: {size}px, width: {size}px" />
{/if}
<div style="width: {size}px">
<label class="button primary block" for="single">
{uploading ? 'Uploading ...' : 'Upload avatar'}
</label>
<span style="display:none">
<input
type="file"
id="single"
accept="image/*"
bind:files
on:change={uploadAvatar}
disabled={uploading}
/>
</span>
</div>
</div>
```
</$CodeTabs>
<$CodeSample
path="user-management/svelte-user-management/src/lib/Avatar.svelte"
meta="name=src/lib/Avatar.svelte"
/>
### Add the new widget
And then we can add the widget to the Account page:
And then you can add the widget to the Account page:
<$CodeTabs>
```html name=src/lib/Account.svelte
<script lang="ts">
// Import the new component
import Avatar from './Avatar.svelte'
</script>
<form on:submit|preventDefault="{updateProfile}" class="form-widget">
<!-- Add to body -->
<Avatar bind:url="{avatarUrl}" size="{150}" on:upload="{updateProfile}" />
<!-- Other form elements -->
</form>
```
</$CodeTabs>
<$CodeSample
path="/user-management/svelte-user-management/src/lib/Account.svelte"
lines={[[1,1], [5,5], [71,73], [74,74], [92,-1]]}
meta="src/lib/Account.svelte"
/>
At this stage you have a fully functional application!

View File

@@ -344,6 +344,8 @@ function matchLang(lang: string) {
return 'swift'
case 'sql':
return 'sql'
case 'svelte':
return 'svelte'
default:
return null
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,25 +1,23 @@
{
"name": "svelte-user-management",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-check --tsconfig ./tsconfig.json"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^1.0.1",
"@tsconfig/svelte": "^3.0.0",
"svelte": "^3.49.0",
"svelte-check": "^2.8.0",
"svelte-preprocess": "^4.10.7",
"tslib": "^2.4.0",
"typescript": "^4.6.4",
"vite": "^4.3.9"
},
"dependencies": {
"@supabase/supabase-js": "^2.0.4"
}
"name": "supabase-svelte",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^6.1.0",
"@tsconfig/svelte": "^5.0.4",
"svelte": "^5.37.3",
"svelte-check": "^4.3.1",
"typescript": "~5.9.2",
"vite": "^7.0.6"
},
"dependencies": {
"@supabase/supabase-js": "^2.53.0"
}
}

View File

@@ -1,11 +1,11 @@
<script lang="ts">
import { onMount } from 'svelte';
import { supabase } from './supabaseClient';
import type { AuthSession } from '@supabase/supabase-js';
import Account from './lib/Account.svelte';
import { onMount } from 'svelte'
import { supabase } from './supabaseClient'
import type { AuthSession } from '@supabase/supabase-js'
import Account from './lib/Account.svelte'
import Auth from './lib/Auth.svelte'
let session: AuthSession
let session = $state<AuthSession | null>(null)
onMount(() => {
supabase.auth.getSession().then(({ data }) => {
@@ -20,8 +20,8 @@
<div class="container" style="padding: 50px 0 100px 0">
{#if !session}
<Auth />
<Auth />
{:else}
<Account {session} />
<Account {session} />
{/if}
</div>

View File

@@ -369,4 +369,4 @@ button.primary,
width: 17px;
animation: spin 1s linear infinite;
filter: invert(1);
}
}

View File

@@ -4,76 +4,80 @@
import { supabase } from "../supabaseClient";
import Avatar from "./Avatar.svelte";
export let session: AuthSession;
interface Props {
session: AuthSession;
}
let loading = false
let username: string | null = null
let website: string | null = null
let avatarUrl: string | null = null
let { session }: Props = $props();
let loading = $state(false);
let username = $state<string | null>(null);
let website = $state<string | null>(null);
let avatarUrl = $state<string | null>(null);
onMount(() => {
getProfile()
})
getProfile();
});
const getProfile = async () => {
try {
loading = true
const { user } = session
loading = true;
const { user } = session;
const { data, error, status } = await supabase
.from('profiles')
.select('username, website, avatar_url')
.eq('id', user.id)
.single()
if (error && status !== 406) throw error
.from("profiles")
.select("username, website, avatar_url")
.eq("id", user.id)
.single();
if (error && status !== 406) throw error;
if (data) {
username = data.username
website = data.website
avatarUrl = data.avatar_url
}
username = data.username;
website = data.website;
avatarUrl = data.avatar_url;
}
} catch (error) {
if (error instanceof Error) {
alert(error.message)
alert(error.message);
}
} finally {
loading = false
loading = false;
}
}
};
const updateProfile = async () => {
try {
loading = true
const { user } = session
loading = true;
const { user } = session;
const updates = {
id: user.id,
username,
website,
avatar_url: avatarUrl,
updated_at: new Date().toISOString(),
}
const updates = {
id: user.id,
username,
website,
avatar_url: avatarUrl,
updated_at: new Date().toISOString(),
};
const { error } = await supabase.from("profiles").upsert(updates);
let { error } = await supabase.from('profiles').upsert(updates)
if (error) {
throw error
throw error;
}
} catch (error) {
if (error instanceof Error) {
alert(error.message)
alert(error.message);
}
} finally {
loading = false
loading = false;
}
}
};
</script>
<form on:submit|preventDefault={updateProfile} class="form-widget">
<Avatar bind:url={avatarUrl} size={150} on:upload={updateProfile} />
<form onsubmit={(e) => { e.preventDefault(); updateProfile(); }} class="form-widget">
<div>Email: {session.user.email}</div>
<div>
<Avatar bind:url={avatarUrl} size={150} onupload={updateProfile} />
<label for="username">Name</label>
<input id="username" type="text" bind:value={username} />
</div>
@@ -83,10 +87,14 @@
</div>
<div>
<button type="submit" class="button primary block" disabled={loading}>
{loading ? 'Saving ...' : 'Update profile'}
{loading ? "Saving ..." : "Update profile"}
</button>
</div>
<button type="button" class="button block" on:click={() => supabase.auth.signOut()}>
<button
type="button"
class="button block"
onclick={() => supabase.auth.signOut()}
>
Sign Out
</button>
</form>

View File

@@ -1,30 +1,30 @@
<script lang="ts">
import { supabase } from "../supabaseClient";
let loading = false
let email = ''
let loading = $state(false);
let email = $state("");
const handleLogin = async () => {
try {
loading = true
const { error } = await supabase.auth.signInWithOtp({ email })
if (error) throw error
alert('Check your email for login link!')
loading = true;
const { error } = await supabase.auth.signInWithOtp({ email });
if (error) throw error;
alert("Check your email for login link!");
} catch (error) {
if (error instanceof Error) {
alert(error.message)
alert(error.message);
}
} finally {
loading = false
loading = false;
}
}
};
</script>
<div class="row flex-center flex">
<div class="col-6 form-widget" aria-live="polite">
<h1 class="header">Supabase + Svelte</h1>
<p class="description">Sign in via magic link with your email below</p>
<form class="form-widget" on:submit|preventDefault={handleLogin}>
<form class="form-widget" onsubmit={(e) => { e.preventDefault(); handleLogin(); }}>
<div>
<label for="email">Email</label>
<input
@@ -36,8 +36,13 @@
/>
</div>
<div>
<button type="submit" class="button block" aria-live="polite" disabled={loading}>
<span>{loading ? 'Loading' : 'Send magic link'}</span>
<button
type="submit"
class="button block"
aria-live="polite"
disabled={loading}
>
<span>{loading ? "Loading" : "Send magic link"}</span>
</button>
</div>
</form>

View File

@@ -1,84 +1,87 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
import { supabase } from "../supabaseClient"
import { supabase } from "../supabaseClient";
export let size: number
export let url: string
let avatarUrl: string = null
let uploading = false
let files: FileList
interface Props {
size: number;
url?: string | null;
onupload?: () => void;
}
const dispatch = createEventDispatcher()
let { size, url = $bindable(null), onupload }: Props = $props();
let avatarUrl = $state<string | null>(null);
let uploading = $state(false);
let files = $state<FileList>();
const downloadImage = async (path: string) => {
try {
const { data, error } = await supabase.storage.from('avatars').download(path)
const { data, error } = await supabase.storage
.from("avatars")
.download(path);
if (error) {
throw error
throw error;
}
const url = URL.createObjectURL(data)
avatarUrl = url
const url = URL.createObjectURL(data);
avatarUrl = url;
} catch (error) {
if (error instanceof Error) {
console.log('Error downloading image: ', error.message)
console.log("Error downloading image: ", error.message);
}
}
}
};
const uploadAvatar = async () => {
try {
uploading = true
uploading = true;
if (!files || files.length === 0) {
throw new Error('You must select an image to upload.')
throw new Error("You must select an image to upload.");
}
const file = files[0]
const fileExt = file.name.split('.').pop()
const filePath = `${Math.random()}.${fileExt}`
const file = files[0];
const fileExt = file.name.split(".").pop();
const filePath = `${Math.random()}.${fileExt}`;
const { error } = await supabase.storage
.from("avatars")
.upload(filePath, file);
let { error } = await supabase.storage
.from('avatars')
.upload(filePath, file)
if (error) {
throw error
throw error;
}
url = filePath
dispatch('upload')
url = filePath;
onupload?.();
} catch (error) {
if (error instanceof Error) {
alert(error.message)
alert(error.message);
}
} finally {
uploading = false
uploading = false;
}
}
};
$: if (url) downloadImage(url)
$effect(() => {
if (url) downloadImage(url);
});
</script>
<div style="width: {size}px" aria-live="polite">
{#if avatarUrl}
{#if avatarUrl}
<img
src={avatarUrl}
alt={avatarUrl ? 'Avatar' : 'No image'}
alt={avatarUrl ? "Avatar" : "No image"}
class="avatar image"
style="height: {size}px; width: {size}px"
style="height: {size}px, width: {size}px"
/>
{:else}
<div
class="avatar no-image"
style="height: {size}px; width: {size}px"
/>
<div class="avatar no-image" style="height: {size}px, width: {size}px"></div>
{/if}
<div style="width: {size}px">
<label class="button primary block" for="single">
{uploading ? 'Uploading ...' : 'Upload avatar'}
{uploading ? "Uploading ..." : "Upload avatar"}
</label>
<span style="display:none">
<input
@@ -86,7 +89,7 @@
id="single"
accept="image/*"
bind:files
on:change={uploadAvatar}
onchange={uploadAvatar}
disabled={uploading}
/>
</span>

View File

@@ -1,8 +1,9 @@
import { mount } from 'svelte'
import './app.css'
import App from './App.svelte'
const app = new App({
target: document.getElementById('app')
const app = mount(App, {
target: document.getElementById('app')!,
})
export default app

View File

@@ -1,40 +0,0 @@
export type Json = string | number | boolean | null | { [key: string]: Json } | Json[]
export interface Database {
public: {
Tables: {
profiles: {
Row: {
id: string
updated_at: string | null
username: string | null
avatar_url: string | null
website: string | null
}
Insert: {
id: string
updated_at?: string | null
username?: string | null
avatar_url?: string | null
website?: string | null
}
Update: {
id?: string
updated_at?: string | null
username?: string | null
avatar_url?: string | null
website?: string | null
}
}
}
Views: {
[_ in never]: never
}
Functions: {
[_ in never]: never
}
Enums: {
[_ in never]: never
}
}
}

View File

@@ -1,7 +1,6 @@
import { createClient } from '@supabase/supabase-js'
import type { Database } from './schema'
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY
export const supabase = createClient<Database>(supabaseUrl, supabaseAnonKey)
export const supabase = createClient(supabaseUrl, supabaseAnonKey)

View File

@@ -1,7 +1,7 @@
import sveltePreprocess from 'svelte-preprocess'
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
export default {
// Consult https://github.com/sveltejs/svelte-preprocess
// Consult https://svelte.dev/docs#compile-time-svelte-preprocess
// for more information about preprocessors
preprocess: sveltePreprocess()
preprocess: vitePreprocess(),
}

View File

@@ -0,0 +1,20 @@
{
"extends": "@tsconfig/svelte/tsconfig.json",
"compilerOptions": {
"target": "ES2022",
"useDefineForClassFields": true,
"module": "ESNext",
"resolveJsonModule": true,
/**
* Typecheck JS in `.svelte` and `.js` files by default.
* Disable checkJs if you'd like to use dynamic types in JS.
* Note that setting allowJs false does not prevent the use
* of JS in `.svelte` files.
*/
"allowJs": true,
"checkJs": true,
"isolatedModules": true,
"moduleDetection": "force"
},
"include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte"]
}

View File

@@ -1,21 +1,7 @@
{
"extends": "@tsconfig/svelte/tsconfig.json",
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"resolveJsonModule": true,
"baseUrl": ".",
/**
* Typecheck JS in `.svelte` and `.js` files by default.
* Disable checkJs if you'd like to use dynamic types in JS.
* Note that setting allowJs false does not prevent the use
* of JS in `.svelte` files.
*/
"allowJs": true,
"checkJs": true,
"isolatedModules": true
},
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"],
"references": [{ "path": "./tsconfig.node.json" }]
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

View File

@@ -1,8 +1,25 @@
{
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2023",
"lib": ["ES2023"],
"module": "ESNext",
"moduleResolution": "Node"
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}

View File

@@ -1,7 +1,7 @@
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
// https://vitejs.dev/config/
// https://vite.dev/config/
export default defineConfig({
plugins: [svelte()]
plugins: [svelte()],
})