npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sveltebase/instant

v0.4.6

Published

Typed InstantDB helpers for Svelte and SvelteKit.

Downloads

1,823

Readme

@sveltebase/instant

Typed InstantDB helpers for Svelte and SvelteKit.

This package is mainly for giving @instantdb/svelte a clean SvelteKit SSR flow. It helps you:

  • create app-local helpers around your InstantDB client
  • read the signed-in user from the server cookie
  • initialize Instant auth on the client with that server user
  • handle auth syncing and authenticated server queries through one SvelteKit endpoint
  • prefetch query data during SSR
  • hydrate that same data into useQuery so the first render is already filled

If you are using InstantDB inside SvelteKit and want the first server render and later client navigations to work nicely together, this package is the missing glue.

Install

Add the package with Bun:

bun add @sveltebase/instant

You will also need the peer dependencies used by this package:

bun add @instantdb/svelte svelte

If you are using the SvelteKit server handler from this package, install SvelteKit too:

bun add @sveltejs/kit

What it exports

  • createInstantHelpers
  • useQuery
  • prefetchQuery
  • createInstantHandler
  • parseUser

Recommended SvelteKit setup

The recommended setup is:

  1. create your local Instant helpers in $lib/db/index.ts
  2. read the current user in +layout.server.ts
  3. initialize auth in +layout.svelte
  4. create one catch-all endpoint at /api/instant/[...action]/+server.ts
  5. define each page query once in _components/query.ts
  6. use that same query in both +page.ts and +page.svelte

This gives you a simple SSR flow:

  • on the first request, the server loads data through your Instant API route
  • the page renders with real data immediately
  • after hydration, useQuery continues on the client
  • on later navigations, SvelteKit can preload the page on hover, so data starts loading on the client before the navigation completes

1. Create $lib/db/index.ts

Create your Instant client once, then bind the helpers to it.

import { init } from "@instantdb/svelte";
import { createInstantHelpers } from "@sveltebase/instant";
import schema from "./instant.schema";

export const db = init({
  appId: "YOUR_INSTANT_APP_ID",
  schema
});

export const { auth, useQuery, prefetchQuery } = createInstantHelpers(db);

This gives you app-local helpers already connected to your db instance, so the rest of your app can just import from $lib/db.


2. Load the user in +layout.server.ts

Use parseUser to read the Instant user from cookies on the server.

import { parseUser } from "@sveltebase/instant";

export async function load({ cookies }) {
  const user = parseUser(cookies.getAll());

  return { user };
}

This reads the instant_user cookie and makes the user available to your root layout.


3. Initialize auth in +layout.svelte

Initialize the auth helper with the user returned from the server.

<script lang="ts">
  import { auth } from "$lib/db";
  import type { LayoutData } from "./$types";

  let { data }: { data: LayoutData } = $props();

  auth.init(() => data.user);
</script>

<slot />

This is important for SSR. The server already knows who the user is, so the client should start with the same user immediately instead of discovering auth state later.


4. Create /api/instant/[...action]/+server.ts

Create the unified Instant handler and load adminDb from $lib/server/db.

import { createInstantHandler } from "@sveltebase/instant";
import { adminDb } from "$lib/server/db";

export const POST = createInstantHandler(adminDb);

This route handles the server side of the package:

  • auth state syncing
  • authenticated Instant queries during SSR

adminDb should be your server-only Instant admin client. The handler uses it to create a user-scoped database from the current signed-in user's token and run the query as that user.


5. Create a shared query in _components/query.ts

For each page, define the query once and reuse it everywhere.

For example, if your route is src/routes/posts, create:

export const postsQuery = () => ({
  posts: {}
});

A common place for that is:

src/routes/posts/_components/query.ts

This keeps your page load function and your Svelte component in sync because they both use the exact same query definition.


6. Use the same query in +page.ts

In the page load function, prefetch the query for SSR.

import { prefetchQuery } from "$lib/db";
import { postsQuery } from "./_components/query";

export async function load({ fetch }) {
  const posts = await prefetchQuery(postsQuery(), fetch);

  return { posts };
}

On the first request, this runs on the server. prefetchQuery sends the query to the package's Instant route, which executes the query with the current authenticated user and returns JSON for SSR.


7. Use the same query in +page.svelte

In the page component, pass the server-loaded data into useQuery.

<script lang="ts">
  import { useQuery } from "$lib/db";
  import type { PageData } from "./$types";
  import { postsQuery } from "./_components/query";

  let { data }: { data: PageData } = $props();

  const posts = useQuery(postsQuery(), () => data.posts);
</script>

{#if posts.isLoading}
  <p>Loading...</p>
{:else if posts.data}
  <ul>
    {#each posts.data.posts as post}
      <li>{post.title}</li>
    {/each}
  </ul>
{/if}

This is the key SSR pattern:

  • +page.ts gets the initial data on the server
  • +page.svelte receives that data immediately
  • useQuery starts from that initial value instead of rendering an empty state first
  • after that, Instant continues as a live client-side subscription

How SSR works

Here is the full request flow:

First page load

When someone opens the page directly:

  1. SvelteKit runs +layout.server.ts
  2. parseUser(cookies.getAll()) reads the current user from the cookie
  3. SvelteKit runs +page.ts
  4. prefetchQuery(...) sends the query to /api/instant/query
  5. createInstantHandler(adminDb) handles that request on the server
  6. the handler reads the same auth cookie, creates a user-scoped database, and runs the query
  7. the result is returned to +page.ts
  8. the page is rendered on the server with real data
  9. +layout.svelte calls auth.init(data.user)
  10. +page.svelte calls useQuery(..., () => data.posts) so hydration starts with the already-fetched data

That means the first HTML response is already filled with the query result.

Later navigations

After the app is running in the browser, the flow is smoother:

  • SvelteKit can start loading the next page on hover
  • that means +page.ts can begin before the click finishes
  • from there, data loading happens client-side for faster transitions
  • the page still uses the same shared query definition
  • useQuery keeps the page reactive after navigation completes

So the pattern gives you both:

  • SSR data on the first request
  • fast client-side navigations afterward

Full example

$lib/db/index.ts

import { init } from "@instantdb/svelte";
import { createInstantHelpers } from "@sveltebase/instant";
import schema from "./instant.schema";

export const db = init({
  appId: "YOUR_INSTANT_APP_ID",
  schema
});

export const { auth, useQuery, prefetchQuery } = createInstantHelpers(db);

src/routes/+layout.server.ts

import { parseUser } from "@sveltebase/instant";

export async function load({ cookies }) {
  const user = parseUser(cookies.getAll());

  return { user };
}

src/routes/+layout.svelte

<script lang="ts">
  import { auth } from "$lib/db";

  let { data, children } = $props();

  auth.init(() =>data.user);
</script>

{@render children()}

src/routes/api/instant/[...action]/+server.ts

import { createInstantHandler } from "@sveltebase/instant";
import { adminDb } from "$lib/server/db";

export const POST = createInstantHandler(adminDb);

src/routes/posts/_components/query.ts

export const postsQuery = () => ({
  posts: {}
});

src/routes/posts/+page.ts

import { prefetchQuery } from "$lib/db";
import { postsQuery } from "./_components/query";

export async function load({ fetch }) {
  const posts = await prefetchQuery(postsQuery(), fetch);

  return { posts };
}

src/routes/posts/+page.svelte

<script lang="ts">
  import { useQuery } from "$lib/db";
  import type { PageData } from "./$types";
  import { postsQuery } from "./_components/query";

  let { data }: { data: PageData } = $props();

  const posts = useQuery(postsQuery, () => data.posts);
</script>

{#if posts.isLoading}
  <p>Loading...</p>
{:else if posts.data}
  <ul>
    {#each posts.data.posts as post}
      <li>{post.title}</li>
    {/each}
  </ul>
{/if}

useQuery

Use useQuery inside Svelte components when you want a reactive Instant subscription.

<script lang="ts">
  import { useQuery } from "$lib/db";

  const posts = useQuery(() => ({
    posts: {}
  }));
</script>

You can also disable a query by returning null:

<script lang="ts">
  import { useQuery } from "$lib/db";

  let enabled = false;

  const posts = useQuery(() => {
    if (!enabled) return null;

    return {
      posts: {}
    };
  });
</script>

You may optionally provide initial data, which is what makes the SSR flow above work cleanly:

const posts = useQuery(
  () => ({ posts: {} }),
  () => initialPosts
);

prefetchQuery

prefetchQuery is the server-friendly loader.

Use it in SvelteKit load functions when you want the first render to already contain the query result.

import { prefetchQuery } from "$lib/db";

const posts = await prefetchQuery(
  { posts: {} },
  fetch
);

In SSR, it requests data through the package's Instant route. In the browser, it falls back to a client-side query flow.


Auth syncing

The auth helper keeps Instant auth and your SvelteKit server in sync.

Read the current user

const user = auth.user;
const initialized = auth.initialized;

Clean up

If you ever create the auth helper in a lifecycle-sensitive place, you can destroy it when no longer needed:

auth.destroy();

Notes

  • parseUser reads the current user from the instant_user cookie array
  • createInstantHandler handles both auth syncing and authenticated query execution
  • authenticated server queries require a signed-in user with a refresh_token
  • prefetchQuery returns null if the server request is not successful
  • useQuery throws if the subscription returns an error

Peer dependencies

This package declares these peer dependencies:

  • @instantdb/svelte
  • svelte
  • @sveltejs/kit (optional, only needed for the server handler)

License

ISC