Pick your app

The examples below will be updated with your app ID.

Introduction

Getting started with Svelte

#Automatic Setup With Create Instant App

The fastest way to get started with Instant and SvelteKit is to use create-instant-app to scaffold a new project with Instant already set up.

To get started, run:

npx create-instant-app --sv

#Manual Setup

Create a blank SvelteKit app:

npx sv create my-app

Add the InstantDB Svelte Library:

npm i @instantdb/svelte

Use instant-cli to set up a new Instant project. This will prompt you to log in if you haven't already. It will then create schema and permissions files and update your .env file.

npx instant-cli init

Create a database client in src/lib/db.ts:

src/lib/db.ts
import { init } from '@instantdb/svelte';
import schema from '../instant.schema';
export const db = init({
appId: import.meta.env.VITE_INSTANT_APP_ID!,
schema,
useDateObjects: true,
});

You're now ready to run queries and transactions against your database!

#Creating a To-Do List App

Let's add a "todo" entity to our schema file at src/instant.schema.ts:

src/instant.schema.ts
import { i } from '@instantdb/svelte';
const _schema = i.schema({
entities: {
$files: i.entity({
path: i.string().unique().indexed(),
url: i.string(),
}),
$users: i.entity({
email: i.string().unique().indexed().optional(),
imageURL: i.string().optional(),
type: i.string().optional(),
}),
todos: i.entity({
text: i.string(),
done: i.boolean(),
createdAt: i.date(),
}),
},
links: {
$usersLinkedPrimaryUser: {
forward: {
on: '$users',
has: 'one',
label: 'linkedPrimaryUser',
onDelete: 'cascade',
},
reverse: {
on: '$users',
has: 'many',
label: 'linkedGuestUsers',
},
},
},
rooms: {},
});
//...

Push the schema:

npx instant-cli push

Instant doesn't support SSR with SvelteKit yet (let us know if you want this!) so you need to disable SSR for routes that use Instant.

src/routes/+page.ts
export const ssr = false;

Replace the content of src/routes/+page.svelte with the following:

src/routes/+page.svelte
<script lang="ts">
import { id, type InstaQLEntity } from '@instantdb/svelte';
import { db } from '$lib/db';
import type { AppSchema } from '../instant.schema';
type Todo = InstaQLEntity<AppSchema, 'todos'>;
const query = db.useQuery({ todos: {} });
let text = $state('');
function addTodo(todoText: string) {
db.transact(
db.tx.todos[id()].update({
text: todoText,
done: false,
createdAt: Date.now(),
}),
);
}
function deleteTodo(todo: Todo) {
db.transact(db.tx.todos[todo.id].delete());
}
function toggleDone(todo: Todo) {
db.transact(db.tx.todos[todo.id].update({ done: !todo.done }));
}
function deleteCompleted(todos: Todo[]) {
const completed = todos.filter((todo) => todo.done);
if (completed.length === 0) return;
const txs = completed.map((todo) => db.tx.todos[todo.id].delete());
db.transact(txs);
}
</script>
{#if query.isLoading}
<div>Loading...</div>
{:else if query.error}
<div>Error: {query.error.message}</div>
{:else} {@const todos = query.data?.todos ?? []}
<div>
<h2>Todos</h2>
<div>
<form onsubmit={(e) => {
e.preventDefault(); if (!text) return; addTodo(text); text = '';
}}>
<input
bind:value={text}
autofocus
placeholder="What needs to be done?"
type="text"
/>
</form>
{#each todos as todo (todo.id)}
<div>
<input type="checkbox" checked={todo.done} onchange={() =>
toggleDone(todo)} />
<span class:line-through={todo.done}>{todo.text}</span>
<button onclick={() => deleteTodo(todo)}>X</button>
</div>
{/each}
<div>
Remaining todos: {todos.filter((t) => !t.done).length}
<button onclick={() =>
deleteCompleted(todos)}> Delete Completed
</button>
</div>
</div>
</div>
{/if}

Go to localhost:5173, and huzzah 🎉 You've got a fully functional todo list running!

#Reactivity

In Svelte 5, Instant's hooks return $state proxies. You read properties directly off the returned object, and Svelte automatically tracks them for re-rendering.

<script lang="ts">
const query = db.useQuery({ todos: {} });
// query.isLoading, query.data, query.error are all reactive
</script>
{#if !query.isLoading}
<p>{query.data?.todos.length} todos</p>
{/if}

For primitive values like connection status and local IDs, hooks return a { current: value } wrapper (since primitives cannot be $state proxies):

<script lang="ts">
const status = db.useConnectionStatus();
// status.current is reactive
</script>
<p>Connection: {status.current}</p>

#Reactive and conditional queries

You can pass a function to useQuery to make queries reactive or conditional. The function will be re-evaluated whenever its dependencies change. Return null to skip the query:

<script lang="ts">
import { db } from '$lib/db';
const auth = db.useAuth();
// Only query when we have a logged-in user
const query = db.useQuery(() =>
auth.user ? { todos: {} } : null
);
</script>
{#if auth.isLoading}
<p>Loading...</p>
{:else if !auth.user}
<p>Please log in.</p>
{:else if query.isLoading}
<p>Loading todos...</p>
{:else}
{@const todos = query.data?.todos ?? []}
<p>{todos.length} todos</p>
{/if}

This also works for reactive parameters. Any $state variable read inside the function will automatically trigger a new query when it changes:

<script lang="ts">
import { db } from '$lib/db';
let filter = $state<'all' | 'active' | 'done'>('all');
const query = db.useQuery(() => {
if (filter === 'all') return { todos: {} };
return { todos: { $: { where: { done: filter === 'done' } } } };
});
</script>
<button onclick={() => filter = 'all'}>All</button>
<button onclick={() => filter = 'active'}>Active</button>
<button onclick={() => filter = 'done'}>Done</button>

#Writing data

Transactions in Svelte work the same way they do in React via db.transact:

<script lang="ts">
import { id } from '@instantdb/svelte';
import { db } from '$lib/db';
function addTodo(text: string) {
db.transact(db.tx.todos[id()].update({ text, done: false, createdAt: Date.now() }));
}
function toggleDone(todo: { id: string; done: boolean }) {
db.transact(db.tx.todos[todo.id].update({ done: !todo.done }));
}
function deleteTodo(todoId: string) {
db.transact(db.tx.todos[todoId].delete());
}
</script>

To learn more, see our writing data docs.

#Auth

The Svelte SDK supports all of Instant's auth methods: magic codes, guest auth, Google OAuth, and more.

#useAuth

Use db.useAuth() to get the current auth state. This gives you full control over loading, error, and user states:

<script lang="ts">
import { db } from '$lib/db';
const auth = db.useAuth();
</script>
{#if auth.isLoading}
<div>Loading...</div>
{:else if auth.error}
<div>Error: {auth.error.message}</div>
{:else if auth.user}
<div>
<p>Hello, {auth.user.isGuest ? 'Guest' : auth.user.email}!</p>
<button onclick={() => db.auth.signOut()}>Sign out</button>
</div>
{:else}
<div>
<p>Please log in.</p>
<button onclick={() => db.auth.signInAsGuest()}>
Try as guest
</button>
</div>
{/if}

#SignedIn / SignedOut

For simpler cases where you just need to gate content on auth state, you can use the SignedIn and SignedOut guard components instead:

<script lang="ts">
import { SignedIn, SignedOut } from '@instantdb/svelte';
import { db } from '$lib/db';
</script>
<SignedIn {db}>
<p>You are logged in!</p>
<button onclick={() => db.auth.signOut()}>Sign out</button>
</SignedIn>
<SignedOut {db}>
<p>Please log in.</p>
</SignedOut>

useAuth is better when you need access to isLoading, error, or user.isGuest. The guard components are simpler when you just need to show or hide content based on login state.

#Components

#Cursors

A multiplayer cursor component that tracks mouse positions via presence. Wrap any area where you want to show live cursors from other users:

<script lang="ts">
import { Cursors } from '@instantdb/svelte';
import { db } from '$lib/db';
const room = db.room('main', 'my-room-id');
</script>
<Cursors {room} userCursorColor="tomato">
<div>Move your mouse around!</div>
</Cursors>

The Cursors component supports custom cursor rendering via a renderCursor snippet, a configurable wrapper element (as), and className/style props for styling. See the Presence, Cursors, and Activity docs for more details.

If you are an LLM trying to learn how to develop with Instant, here are all of the links to the docs:

Getting started w/ ReactGetting started w/ React NativeGetting started w/ Vanilla JSGetting started w/ SolidJSGetting started w/ SvelteGetting started w/ VueGetting started w/ TanStack StartGetting started w/ PythonCreate Instant AppRecommended WorkflowUsing LLMsInitModeling dataWriting dataReading dataInfinite QueriesInstant on the backendPatternsAuthMagic codesGuest AuthGoogle OAuthSign In with AppleGitHub OAuthLinkedIn OAuthClerkFirebase AuthPermissionsRate LimitsManaging usersPresence, Cursors, and ActivityInstant CLIDevtoolPlatform APIExplorer ComponentCustom emailsApp teamsStorageStreamsWebhooksStripe PaymentsAdmin HTTP API(Experimental) Next.js SSROverviewVPSAWSMigrate from Instant CloudMigrating from Supabase