diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..aacfa3f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-2024 Fireship LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index dce5a26..83bc212 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,32 @@ -# SvelteFire +
+ +

SvelteFire

+npm +Discord +License +
Cybernetically Enhanced Firebase Apps
+
+
+ + + A minimal, yet powerful library that puts realtime Firebase data into Svelte stores. -[Full Documentation](https://sveltefire.fireship.io) +- [Quick Start](https://sveltefire.fireship.io/guide/start) +- [Documentation](https://sveltefire.fireship.io) + +## Build Complex Apps Faster + +SvelteFire allows you to access Firebase Auth, Firestore, Storage, RealtimeDB, and Analytics with minimal complexity. It simplfies relational data with a declarative syntax, handles loading states, automatically disposes of realtime data subscriptions, and more! + +Gaze in awe at the example below where we fetch multiple levels of realtime user data with just a few lines of Svelte code: ```svelte - + @@ -26,476 +46,17 @@ A minimal, yet powerful library that puts realtime Firebase data into Svelte sto ... ``` -## Why? - -Svelte makes it possible to dramatically simplify the way developers work with Firebase. Here are some problems the project solves: - -- Access users, realtime Firestore and Realtime Database data as Svelte stores -- Automatic subscription disposal to prevent memory/cost leaks -- Better TypeScript experience for Firebase -- Handle complex relational data between Auth, Firestore, and Realtime Database -- Easily hydrate SvelteKit server data into a realtime Firebase stream -- Simple Google Analytics integration for SvelteKit - -## Quick Start - -1. Install Firebase npm i firebase v9+ and initialize it in a file like lib/firebase.js: - -``` -npm i sveltefire firebase -``` - -```ts -import { initializeApp } from "firebase/app"; -import { getFirestore } from "firebase/firestore"; -import { getDatabase } from "firebase/database"; -import { getAuth } from "firebase/auth"; -import { getAnalytics } from "firebase/analytics"; - -// Initialize Firebase -const app = initializeApp(/* your firebase config */); -export const db = getFirestore(app); -export const rtdb = getDatabase(app); -export const auth = getAuth(app); -export const auth = getAnalytics(app); -``` +Each component in this example above is underpinned by a Svelte store. These custom stores can be used for fine-grained control and to implement your own custom patterns. -2. Get the Current user +Use stores to access Firebase data with Svelte's reactive `$` syntax: ```svelte - -Hello {$user?.uid} -``` - -3. Listen to realtime data. + import { docStore } from 'sveltefire'; + import { firestore } from '$lib/firebase'; // your firestore instance -Use the `$` as much as you want - it will only result in one Firebase read request. When all the subscriptions are removed, it will automatically unsubscribe. - -```svelte - -{$post?.content} {$post?.title} -``` - -Or better yet, use the built in `Doc` and `Collection` components for Firestore, or `Node` and `NodeList` components for Realtime Database. See below. - -4. Add Firebase/Google Analytics - -Easily generate a `page_view` event on every route change with SvelteKit layouts - works for both client and server rendered pages. - -```svelte - - - - - -{#key $page.route.id} - -{/key} -``` - -## Stores - -Stores are the building blocks of SvelteFire. - -### Auth Store - -Listen to the current user. Render UI conditionally based on the auth state: - -```svelte - - -{#if $user} -

Hi {$user.uid}

-{:else} -

Sign in...

-{/if} -``` - -### Firestore Stores - -Subscribe to realtime data. The store will unsubscribe automatically to avoid unnecessary Firestore reads. - -```svelte - - -{$post?.content} - -{#each $posts as post} - -{/each} -``` - -Cast Firebase data to a TS interface: - -```ts -interface Post { - id: string; - title: string; - content: string; -} -const post = docStore(firestore, "posts/test"); -const posts = collectionStore(firestore, "posts"); -``` - -### Realtime Database Stores - -Subscribe to realtime data. The store will unsubscribe automatically to avoid unnecessary Realtime Database reads. - -```svelte - - -{$post?.content} - -{#each $posts as post} - -{/each} -``` - -Cast Firebase data to a TS interface: - -```ts -interface Post { - id: string; - title: string; - content: string; -} -const post = nodeStore(rtdb, "posts/test"); -const posts = nodeListStore(rtdb, "posts"); -``` - -## SSR - -SvelteFire is a client-side library, but allows you to hydrate server data into a realtime stream. - -First, fetch data from a load function like so: - -```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(); - }; -}); -``` - -Second, pass the server data as the `startWith` value to a store. This will bypass the loading state and ensure the data is rendered in the server HTML, then realtime listeners will be attached afterwards. - -```ts -// Data fetched via server -export let data: PageData; - -// Just give the store a startWith value -const post = docStore(firestore, "posts/test", data.post); -``` - -## Realtime Components - -In addition to stores, SvelteFire provides a set of components that can build complex realtime apps without leaving the HTML. - -### FirebaseApp - -The `FirebaseApp` component puts the FirebaseSDK into Svelte context. This avoids the need to pass `auth`, `firestore` and `rtdb` down to every component. It is typically placed in root layout. - -```svelte - - - - - - - - - -``` - -You can use Svelte's context API to access the Firebase SDK in any component. - -```svelte - -``` - -### User - -Get the current user. - -```svelte - - Hello {user.uid} - - - - You need to sign in! - -``` - -### Doc - -Fetch a single document and listen to data in realtime. The `data` slot prop provides access to the fetched data, while `ref` is the Firestore document reference. - -```svelte - - {data.content} - {ref.path} - -``` - -Slot props can be renamed: - -```svelte - - {post.content} - {postRef.path} - -``` - -Firestore components can also handle loading states: - -```svelte - - -
Loading.... This will disappear when data is defined
-
-``` - -Pass a `startWith` value to bypass the loading state. This is useful in SvelteKit when you need to hydrate server data into a realtime stream: - -```svelte - -``` - -### Collection - -Collections provides array of objects containing the document data, as well as the `id` and `ref` for each result. It also provides a `count` slot prop for number of docs in the query. - -```svelte - -

Fetched {count} documents

- {#each data as post} - {post.id} - {post.ref.path} - {post.content} - {/each} -
-``` - -Collections can also take a Firestore Query instead of a path: - -```svelte - - - - -``` - -### Node - -Fetch a single node from the Realtime Database and listen to its data in realtime. The `data` slot prop gives you access to the fetched data, and the `ref` provides the Realtime Database reference. - -```svelte - - {data.content} - {ref.key} - -``` - -Slot props can be renamed: - -```svelte - - {post.content} - {postRef.key} - -``` - -Realtime Database components can also handle loading states: - -```svelte - - -
Loading.... This will disappear when data is defined
-
-``` - -Pass a `startWith` value to bypass the loading state. This is useful in SvelteKit when you need to hydrate server data into a realtime stream: - -```svelte - -``` - -### NodeList - -Fetch lists of nodes from the Realtime Database and listen to their data in realtime. The component provides an array of the data with the `data` slot prop, the reference with `ref`, and the `count` of items in the list with count. - -```svelte - -

Fetched {count} posts

- {#each data as post} - {item.nodeKey} - {post.content} - {/each} -
-``` - -### DownloadURL - -DownloadURL provides a `link` to download a file from Firebase Storage and its `reference`. - -```svelte - - Download {ref?.name} - -``` - -### StorageList - -StorageList provides a list of `items` and `prefixes` corresponding to the list of objects and sub-folders at a given Firebase Storage path. - -```svelte - -
    - {#if list === null} -
  • Loading...
  • - {:else if list.prefixes.length === 0 && list.items.length === 0} -
  • Empty
  • - {:else} - - {#each list.prefixes as prefix} -
  • - {prefix.name} -
  • - {/each} - - {#each list.items as item} -
  • - {item.name} -
  • - {/each} - {/if} -
-
-``` - -### UploadTask - -Upload a file with progress tracking - -```svelte - - {#if snapshot?.state === "running"} - {progress}% uploaded - {/if} - - {#if snapshot?.state === "success"} - - Download - - {/if} - -``` - -### Using Components Together - -These components can be combined to build complex realtime apps. It's especially powerful when fetching data that requires the current user's UID or a related document's path. - -```svelte - - -

UID: {user.uid}

- -

Profile

- - - - - {profile.content} - -

Comments

- - {#each comments as comment} - {comment.content} - {/each} - -
Loading Comments...
-
- -
Loading Profile...
-
- - - - - {profile.content} - -

Comments

- - {#each comments as comment} - {comment.content} - {/each} - -
Loading Comments...
-
- -
Loading Profile...
-
-
- - -

Sign in to see your profile

-
-
-``` - -## Roadmap - -- ~~Add support for Firebase Storage~~ (Added in latest release!) -- ~~Add support for Firebase RTDB~~ (Added in latest release!) -- Add support for Firebase Analytics in SvelteKit -- Find a way to make TS generics with with Doc/Collection components +``` \ No newline at end of file diff --git a/docs/src/components/SideNav.astro b/docs/src/components/SideNav.astro index dc95e61..214bd04 100644 --- a/docs/src/components/SideNav.astro +++ b/docs/src/components/SideNav.astro @@ -3,8 +3,7 @@
  • guide
  • why?
  • get started
  • -
  • common patterns
  • -
  • sveltekit ssr
  • +
  • common patterns
  • app
  • <FirebaseApp>
  • getFirebaseContext
  • diff --git a/docs/src/pages/analytics/page-view-component.md b/docs/src/pages/analytics/page-view-component.md index 71d4665..580b64d 100644 --- a/docs/src/pages/analytics/page-view-component.md +++ b/docs/src/pages/analytics/page-view-component.md @@ -48,5 +48,5 @@ For fine-grained control, you can include `PageView` on a page-by-page basis. Th - + ``` \ No newline at end of file diff --git a/docs/src/pages/app/context.md b/docs/src/pages/app/context.md index 8c145f7..792c1f7 100644 --- a/docs/src/pages/app/context.md +++ b/docs/src/pages/app/context.md @@ -14,6 +14,6 @@ Get the Firebase SDK context from a component. ```svelte ``` \ No newline at end of file diff --git a/docs/src/pages/app/firebase-app.md b/docs/src/pages/app/firebase-app.md index 32aa65c..0266db3 100644 --- a/docs/src/pages/app/firebase-app.md +++ b/docs/src/pages/app/firebase-app.md @@ -13,10 +13,16 @@ Puts the Firebase app into Svelte's context. It should be used as a parent to al - `firestore` - Firebase Auth instance - `auth` - Firestore instance -- `storage` - Firebase Storage instance +- `storage` - Storage instance +- `rtdb` - RealtimeDB instance +- `analytics` - Firebase Analytics instance + + ### Example +Initialize Firebase with the SDKs you need in your app, then pass them to `FirebaseApp` as props. + ```svelte + + + +

    {post.title}

    +

    {post.content}

    +
    +``` + +Note: This will result in 2 reads from Firestore on initial page load, so only use this pattern when true realtime data necessary. + +## Dynamic Firestore Queries + +Imagine you have a collection of posts what a user can filter by category. Create a reactive declaration to re-run the query whenever the category changes. + +```svelte + + + +
      + {#each posts as post (post.id)} +
    • {post.content}
    • + {/each} +
    +
    + + +``` +## Handle File Uploads with Progress Bar + +The example below shows how to upload a file to Firebase Storage and display a progress bar. First, get a file from the user. Second, pass the file to the `UploadTask` component. Third, display the progress bar and download link. + + +```svelte + + + + +{#if file} + + {#if snapshot?.state === "running" || snapshot?.state === "success"} +

    {progress}% uploaded

    + + {/if} + + {#if snapshot?.state === "error"} + Upload failed + {/if} + + {#if snapshot?.state === "success"} + + {ref?.name} + + {/if} +
    +{/if} +``` \ No newline at end of file diff --git a/docs/src/pages/guide/start.md b/docs/src/pages/guide/start.md index 3ede576..73f12f5 100644 --- a/docs/src/pages/guide/start.md +++ b/docs/src/pages/guide/start.md @@ -39,7 +39,7 @@ Initialize Firebase and add the `FirebaseApp` component to the component tree. T
    ``` -## 3. Use Firebase! +### 3. Use Firebase! You can use stores to access the current user and Firestore. diff --git a/docs/src/pages/index.md b/docs/src/pages/index.md index d959af1..b58ce81 100644 --- a/docs/src/pages/index.md +++ b/docs/src/pages/index.md @@ -18,6 +18,7 @@ Firebase realtime APIs are callback-based, but we can dramatically improve the d - Better TypeScript experience for Firebase - Handle complex relational data between Auth and Firestore - Easily hydrate SvelteKit server data into a realtime Firebase stream +- Simple Google Analytics integration for SvelteKit ## Store Example diff --git a/docs/src/pages/storage/download-url.md b/docs/src/pages/storage/download-url.md index 6d84bec..5658051 100644 --- a/docs/src/pages/storage/download-url.md +++ b/docs/src/pages/storage/download-url.md @@ -22,7 +22,7 @@ Returns the download URL for a file in Firebase Storage. - `link` - The download URL - `ref` - Storage reference -- `storage` - The Firestore instance +- `storage` - The Firebase Storage instance ### Example diff --git a/docs/src/pages/storage/storage-list.md b/docs/src/pages/storage/storage-list.md index e71a7eb..174e8c7 100644 --- a/docs/src/pages/storage/storage-list.md +++ b/docs/src/pages/storage/storage-list.md @@ -22,7 +22,7 @@ Returns a list of files stored in Firebase Storage. - `list` - The list of files and prefixes - `ref` - Storage reference -- `storage` - The Firestore instance +- `storage` - The Firebase Storage instance ### Example diff --git a/docs/src/pages/storage/upload-task.md b/docs/src/pages/storage/upload-task.md index 8f511a5..4b27d07 100644 --- a/docs/src/pages/storage/upload-task.md +++ b/docs/src/pages/storage/upload-task.md @@ -25,7 +25,7 @@ Uploads a file to a Firebase storage bucket. - `snapshot` - Firebase UploadTaskSnapshot - `task` - Firebase UploadTask - `progress` - Number as a percentage of the upload progress -- `storage` - The Firestore instance +- `storage` - The Firebase Storage instance ### Example diff --git a/package.json b/package.json index eb4540b..4f6dc60 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sveltefire", - "version": "0.4.3", + "version": "0.4.4", "scripts": { "dev": "vite dev", "build": "vite build && npm run package",