SvelteFire

repository·master·Indexed 23 days ago

https://github.com/codediodeio/sveltefire

A library that bridges Firebase and Svelte, converting Firebase realtime data into reactive Svelte stores. It provides declarative components like <FirebaseApp>, <SignedIn>, <Doc>, and <Collection>, as well as programmatic APIs such as docStore, collectionStore, and userStore. It also includes a PageView component for integrating Google Analytics and a getFirebaseContext function to retrieve initialized Firebase SDK instances.

Tokens
12.2K
Snippets
36
Records
61
Agent score
77%

What's inside sveltefire

  1. What is SvelteFire?

    master

    SvelteFire is a library designed to bridge Firebase realtime APIs with Svelte's reactive stores. It converts callback-based Firebase APIs into Svelte stores, enabling reactive UI updates when data changes.

    Key features include:

    • Realtime access to Firebase Auth users and Firestore data via Svelte stores.
    • Automatic subscription management to prevent duplicate reads and memory leaks.
    • Improved TypeScript support for Firebase.
    • Simplified handling of relational data between Auth and Firestore.
    • Support for hydrating SvelteKit server data into realtime Firebase streams.
    • Integrated Google Analytics for SvelteKit.
  2. Use SvelteFire components for Auth and Firestore

    master

    Authentication Components

    • SignedIn: Renders its content only when a user is authenticated. Provides let:user and let:signOut slot props.
    • SignedOut: Renders its content only when no user is authenticated. Provides let:auth slot prop.

    Firestore Components

    • Doc: Fetches a single document. Use ref="path/to/doc" and access the data via let:data.
    • Collection: Fetches a collection of documents. Use ref="collection_name" and access the array via let:data={items}.
    <script>
      import { SignedIn, SignedOut, Doc, Collection } from 'sveltefire';
      import { signInAnonymously } from "firebase/auth";
    </script>
    
    <SignedIn let:user let:signOut>
        <p>Hello {user.uid}</p>
        <button on:click={signOut}>Sign Out</button>
    </SignedIn>
    
    <SignedOut let:auth>
        <button on:click={() => signInAnonymously(auth)}>Sign In</button>
    </SignedOut>
    
    <Doc ref="posts/id" let:data>
        <h2>{data.title}</h2>
        <p>{data.content}</p>
    </Doc>
    
    <Collection ref="posts" let:data={posts}>
        {#each posts as post}
            <h2>{post.title}</h2>
            <p>{post.content}</p>
        {/each}
    </Collection>
  3. Initialize SvelteFire with FirebaseApp

    master

    To make Firebase services available throughout your application, initialize your Firebase app and wrap your component tree with the FirebaseApp component. In SvelteKit, this is typically done in the root +layout.svelte file. You must pass the auth and firestore instances to the FirebaseApp component.

    <script lang="ts">
        import { FirebaseApp } from 'sveltefire';
        import { initializeApp } from 'firebase/app';
        import { getFirestore } from 'firebase/firestore';
        import { getAuth } from 'firebase/auth';
    
        // Initialize Firebase
        const app = initializeApp(/* your firebase config */);
        const firestore = getFirestore(app);
        const auth = getAuth(app);
    </script>
    
    <FirebaseApp {auth} {firestore}>
        <slot />
    </FirebaseApp>
  4. Track route changes using PageView in a Layout

    master

    To ensure every route change is logged on both the client and server, the recommended approach is to place the PageView component in your root layout component.

    Crucial: You must wrap the PageView component in an Svelte {#key} block using the current route ID (e.g., $page.route.id from SvelteKit) to force the component to re-mount and trigger a new event on every navigation.

    <!-- +layout.svelte  --> 
    <script lang="ts">
      import { page } from "$app/stores";
      import { PageView } from "sveltefire";
    </script>
    
    <slot />
    
    {#key $page.route.id}
      <PageView />
    {/key}
  5. Implement SSR with SvelteKit and SvelteFire

    master

    SvelteFire is primarily a client-side library, but you can achieve Server-Side Rendering (SSR) by hydrating server-fetched data into a realtime stream.

    1. In your SvelteKit load function (e.g., +page.ts), fetch the initial data using standard Firebase SDK methods like getDoc.
    2. In your Svelte component (+page.svelte), pass that loaded data to the startWith prop of a SvelteFire component (like Doc).

    This approach ensures the data is rendered in the initial HTML sent by the server, bypassing the loading state. Realtime listeners are then attached on the client side.

    Note: This pattern results in two Firestore reads on the initial page load. Use this only when realtime updates are necessary for the specific data.

    // +page.ts
    import { doc, getDoc } from 'firebase/firestore';
    
    export const load = (async () => {
      const ref = doc(firestore, 'posts', 'first-post');
      const snapshot = await getDoc(ref);
      return {
        post: snapshot.data(),
      };
    });
    // +page.svelte  
    <script lang="ts">
      export let data: PageData;
    </script>
    
    <!-- Example using component -->
    <Doc startWith={data.post} ref="posts/first-post" let:data={post}>
        <h2>{post.title}</h2>
        <p>{post.content}</p>
    </Doc>
  6. Handle file uploads with progress tracking

    master

    You can manage Firebase Storage uploads using the UploadTask component.

    1. Capture a File object from an <input type="file">.
    2. Pass the file to the data prop and the destination path to the ref prop of UploadTask.
    3. Use the let:progress slot to get the upload percentage.
    4. Use the let:snapshot slot to monitor the upload state (running, success, or error).
    5. Once successful, use the DownloadURL component with the snapshot's reference to provide a download link to the user.
    <script lang="ts">
      let file: File;
      let filePath = "things/test.png";
    
      function chooseFile(event) {
        file = event.target.files[0];
      }
    </script>
    
    <input type="file" on:change={chooseFile} />
    
    {#if file}
      <UploadTask ref={filePath} data={file} let:progress let:snapshot>
        {#if snapshot?.state === "running" || snapshot?.state === "success"}
          <p>{progress}% uploaded</p>
          <progress value={progress} max="100" />
        {/if}
    
        {#if snapshot?.state === "error"}
          Upload failed
        {/if}
    
        {#if snapshot?.state === "success"}
          <DownloadURL ref={snapshot?.ref} let:link let:ref>
            <a href={link} download> {ref?.name} </a>
          </DownloadURL>
        {/if}
      </UploadTask>
    {/if}
  7. Use the DownloadURL component

    master

    The DownloadURL component retrieves and provides the download URL for a specific file stored in Firebase Storage. It manages the loading state automatically and exposes the resulting URL via a slot prop.

    Props

    • ref: A Firebase Storage reference or a path string (e.g., files/hi-mom.txt).

    Slots

    • default: The content to display once the URL has been successfully retrieved.
    • loading: The content to display while the URL is being fetched.

    Slot Props

    When using the default slot, you can access the following properties using the let: directive:

    • link: The generated download URL.
    • ref: The Firebase Storage reference.
    • storage: The Firebase Storage instance.
    <script>
      import  { DownloadURL } from "sveltefire";
    </script>
    
    
    <DownloadURL ref="images/pic.png" let:link let:ref>
        <!-- show img -->
        <img src={link} />
    
        <!-- or download via link -->
        <a href={link} download>{ref?.name}</a>
    </DownloadURL>
  8. Fetch Firestore data for authenticated users

    master

    To fetch data belonging to a specific user, wrap your data-fetching component (like Doc or Collection) inside the SignedIn component. Use the let:user slot to access the current user's UID, and pass that UID into the ref prop of the Firestore component to ensure you are targeting the user's specific documents.

      <SignedIn let:user>
        <Doc ref={`posts/${user.uid}`} let:data={post}>
            <h2>{post.title}</h2>
            <p>{post.content}</p>
        </Doc>
      </SignedIn>
  9. Perform dynamic Firestore queries

    master

    To create queries that react to user input (like filters or search terms), use a Svelte reactive declaration ($:) to rebuild the Firestore query object whenever the dependency changes. Pass this reactive query object to the ref prop of the Collection component.

    <script lang="ts">
        import { query, collection, where } from 'firebase/firestore';
    
        let category = 'tech';
    
        $: q = query(
                collection(firestore, `posts`),
                where('category', '==', category)
            );
    </script>
    
    <Collection ref={q} let:data={posts}>
        <ul>
            {#each posts as post (post.id)}
            <li>{post.content}</li>
            {/each}
        </ul>
    </Collection>
    
    <button on:click={() => category = 'sports'}>Sports</button>
  10. Use SvelteFire components for relational data

    master

    SvelteFire provides components that use slot props to simplify common Firebase patterns, such as fetching relational data (e.g., user -> post -> comments).

    Common components include:

    • <FirebaseApp>: The root component that initializes the Firebase connection.
    • <SignedIn>: Renders content only if a user is authenticated, providing the user object via let:user.
    • <Doc>: Fetches a Firestore document based on a ref prop, providing the document data and its ref via let:data and let:ref.
    • <Collection>: Fetches a collection of documents based on a ref prop, providing the array of documents via let:data.
    <!-- 1. 🔥 Firebase App -->
    <FirebaseApp {auth} {firestore}>
    
        <!-- 2. 👤 Get the current user -->
        <SignedIn let:user>
    
            <p>Howdy, {user.uid}</p>
    
            <!-- 3. 📜 Get a Firestore document owned by a user -->
            <Doc ref={`posts/${user.uid}`} let:data={post} let:ref={postRef}>
                
                <h2>{post.title}</h2>
    
                <!-- 4. 💬 Get all the comments in its subcollection -->
                <Collection ref={postRef.path + '/comments'} let:data={comments}>
                    {#each comments as comment}
                        <!-- Render comment -->
                    {/each}
                </Collection>
            </Doc>
        </SignedIn>
    </FirebaseApp>
  11. Use SvelteFire components for declarative Firebase access

    master

    SvelteFire provides a set of components that wrap Firebase services, allowing you to fetch data and manage authentication state directly within your Svelte templates. These components use Svelte's let: directive to expose data to their children.

    Common components include:

    • <FirebaseApp>: Initializes the Firebase environment.
    • <SignedIn>: Provides access to the currently authenticated user.
    • <Doc>: Fetches a specific Firestore document.
    • <Collection>: Fetches a collection of documents (often used for subcollections).
    <!-- 1. 🔥 Firebase App -->
    <FirebaseApp {auth} {firestore}>
    
      <!-- 2. 👤 Get the current user -->
      <SignedIn let:user>
    
        <p>Howdy, {user.uid}</p>
    
        <!-- 3 (a). 📜 Get a Firestore document owned by a user -->
        <Doc ref={`posts/${user.uid}`} let:data={post} let:ref={postRef}>
    
          <h2>{post.title}</h2>
    
          <!-- 4 (a). 💬 Get all the comments in its subcollection -->
          <Collection ref={postRef.path + '/comments'} let:data={comments}>
            {#each comments as comment}
              <!-- Render comments -->
            {/each}
          </Collection>
    
        </Doc>
    
      </SignedIn>
    
    </FirebaseApp>
  12. Use the FirebaseApp component to provide Firebase context

    master

    The FirebaseApp component is a provider that puts your Firebase app instance into Svelte's context. It must be used as a parent component to all other SvelteFire components to ensure they can access your Firebase services.

    Pass your initialized Firebase service instances (Auth, Firestore, etc.) as props to FirebaseApp.

    <script>
        import { FirebaseApp } from 'sveltefire';
        import { initializeApp } from 'firebase/app';
        import { getFirestore } from 'firebase/firestore';
        import { getAuth } from 'firebase/auth';
    
        // Initialize Firebase
        const app = initializeApp(/* your firebase config */);
        const firestore = getFirestore(app);
        const auth = getAuth(app);
    </script>
    
    <FirebaseApp {auth} {firestore}>
        <slot />
    </FirebaseApp>