Skip to content

Using SignalDB with Svelte 5

This guide explains how to integrate SignalDB into a Svelte 5 project. You’ll learn how to set up SignalDB collections and use Svelte's runes to manage and display data.

Prerequisites

Before you start, make sure you’re familiar with Svelte basics and have a Svelte 5 project ready to go. If you’re new to Svelte 5, you can take a look at the Svelte 5 Documentation to get started.

A basic understanding of signal-based reactivity will also be helpful. You can read more about it on the Core Concepts page and the Svelte runes blog post to understand how reactivity works.

Installation

First, you need to install SignalDB in your project. Open your terminal and run:

bash
npm install @signaldb/core

As Svelte runes have to be used inline, there isn't an easy way to provide a prebuilt adapter. Therefore you have to add a few lines in your code to specify the adapter during collection initialization.

Setting Up SignalDB

Once you’ve installed SignalDB, the next step is to set up your collections. Here's how you can define a Posts collection with a custom reactivity configuration for Svelte:

js
<script>
  import { createSubscriber } from 'svelte/reactivity';
  import { Collection } from "@signaldb/core";

  const Posts = new Collection({
    reactivity: {
      create() {
        let update;
        const subscribe = createSubscriber(u => update = u);
        return {
          depend() {
            subscribe();
          },
          notify() {
            update();
          },
        };
      },
      isInScope: () => !!$effect.tracking(),
    },
  });

  let items = $state.raw([]);
  $effect(() => {
    const cursor = Posts.find({});
    items = cursor.fetch();

    return () => {
      cursor.cleanup();
    };
  });
</script>

This code sets up a Posts collection and enables reactivity using Svelte’s built-in reactivity features.

Creating a Svelte Component

Now let's create a component that lists posts and allows the user to add new ones. Here's an example:

svelte
<script>
  import { createSubscriber } from 'svelte/reactivity';
  import { Collection } from "@signaldb/core";

  const Posts = new Collection({
    reactivity: {
      create() {
        let update;
        const subscribe = createSubscriber(u => update = u);
        return {
          depend() {
            subscribe();
          },
          notify() {
            update();
          },
        };
      },
      isInScope: () => !!$effect.tracking(),
    },
  });

  let items = $state.raw([]);
  $effect(() => {
    const cursor = Posts.find({});
    items = cursor.fetch();

    return () => {
      cursor.cleanup();
    };
  });
</script>

<button onclick={() => Posts.insert({ title: 'Post', author: 'Author' })}>
  Add Post
</button>

<ul>
  {#each items as post}
    <li>
      <strong>{post.title}</strong> by {post.author}
    </li>
  {/each}
</ul>

Explanation:

  1. Reactivity Configuration: We define the reactivity system inside the Posts collection, with depend() and notify() managing dependencies.
  2. Reactive Items List: The items array is set using Svelte's reactivity system, and the component automatically updates when the Posts collection changes.
  3. UI Interaction: Clicking the "Add Post" button inserts a new post into the Posts collection, which triggers the component to update.

Web Worker Compatibility

When using SignalDB with Svelte 5 in applications that utilize web workers, the standard reactivity setup can fail in production builds. This happens because Svelte's runes like $state aren't defined in web worker contexts.

To solve this issue, you can create a dedicated adapter that works in both standard and web worker environments:

js
import { createSubscriber } from 'svelte/reactivity';
import { createReactivityAdapter } from "@signaldb/core";

// Reusable adapter for Svelte reactivity
export function svelteReactivityAdapter() {
  // Check if we're in a web worker
  const isWebWorker =
    typeof self !== "undefined" &&
    typeof WorkerGlobalScope !== "undefined" &&
    self instanceof WorkerGlobalScope;

  if (isWebWorker) {
    return createReactivityAdapter({
      create() {
        return {
          depend() {
            // No-op in web worker
          },
          notify() {
            // No-op in web worker
          },
        };
      },
      isInScope: () => false,
    });
  } else {
    // Regular context, use Svelte's reactivity primitives
    return createReactivityAdapter({
      create() {
        let update;
        const subscribe = createSubscriber(u => update = u);
        return {
          depend() {
            subscribe();
          },
          notify() {
            update();
          },
        };
      },
      isInScope: () => !!$effect.tracking(),
    });
  }
}

Then use this adapter in your collection setup:

js
import { Collection } from "@signaldb/core";
import { svelteReactivityAdapter } from "./your-adapter-file";

const Posts = new Collection({
  reactivity: svelteReactivityAdapter(),
});

This approach ensures your SignalDB collections work properly both in the main thread and in web workers.

Conclusion

You’ve now learned how to set up SignalDB in a Svelte project and create a reactive component to display and add posts. By leveraging Svelte’s reactivity, you can efficiently manage data and create dynamic user interfaces with SignalDB.

Next Steps

Now that you’ve learned how to use SignalDB in Svelte, you maybe want to explore the possibilities how you can synchronize the data with your backend. Take a look at the Synchronization Overview to get started

Released under the MIT License.