diff --git a/README.md b/README.md index a63adc1..dce5a26 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A minimal, yet powerful library that puts realtime Firebase data into Svelte stores. -[Documentation](https://sveltefire.fireship.io) +[Full Documentation](https://sveltefire.fireship.io) ```svelte @@ -22,17 +22,6 @@ A minimal, yet powerful library that puts realtime Firebase data into Svelte sto {#each comments as comment} - {/each} - - - - -

{post.title}

- - - - {#each comments as comment} - {/each} ... ``` @@ -46,6 +35,7 @@ Svelte makes it possible to dramatically simplify the way developers work with F - 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 @@ -60,12 +50,14 @@ 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); ``` 2. Get the Current user @@ -98,6 +90,24 @@ Use the `$` as much as you want - it will only result in one Firebase read reque 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. diff --git a/docs/src/components/SideNav.astro b/docs/src/components/SideNav.astro index e920a72..dc95e61 100644 --- a/docs/src/components/SideNav.astro +++ b/docs/src/components/SideNav.astro @@ -27,7 +27,7 @@
  • <Node>
  • <NodeList>
  • analytics
  • -
  • <PageView>
  • +
  • <PageView>
  • diff --git a/docs/src/pages/analytics/page-view-component.md b/docs/src/pages/analytics/page-view-component.md new file mode 100644 index 0000000..71d4665 --- /dev/null +++ b/docs/src/pages/analytics/page-view-component.md @@ -0,0 +1,52 @@ +--- +title: PageView Component +pubDate: 2023-12-23 +description: SvelteFire PageView Component API reference +layout: ../../layouts/MainLayout.astro +--- + +# PageView + +The `PageView` component logs a Google analytics `page_view` event when it is mounted. + +### Slot Props + +- `eventName` - (default: 'page_view') Set the current user as the userId in Google Analytics +- `setUser` - (default: true) Set the current user as the userId in Google Analytics +- `customParams` - (optional) custom parameters to pass to the `signIn` function + +### Layout Example (recommended) + +The most efficient way to integrate Firebase Analytics is to log events from a layout component. This will ensure that every route change is logged, both on the client and server. Make sure to `key` the `PageView` component so that it is re-mounted on every route change. + +```svelte + + + + + +{#key $page.route.id} + +{/key} +``` + +### Page Example + +For fine-grained control, you can include `PageView` on a page-by-page basis. This is useful when sending custom parameters. + + +```svelte + + + + + +``` \ No newline at end of file diff --git a/src/lib/components/FirebaseApp.svelte b/src/lib/components/FirebaseApp.svelte index 2c138d4..694f517 100644 --- a/src/lib/components/FirebaseApp.svelte +++ b/src/lib/components/FirebaseApp.svelte @@ -4,13 +4,15 @@ import type { Firestore } from "firebase/firestore"; import type { Database } from "firebase/database"; import type { FirebaseStorage } from "firebase/storage"; + import type { Analytics } from "firebase/analytics"; export let firestore: Firestore; export let rtdb: Database; export let auth: Auth; export let storage: FirebaseStorage; + export let analytics: Analytics | null; - setFirebaseContext({ firestore, rtdb, auth, storage }); + setFirebaseContext({ firestore, rtdb, auth, storage, analytics }); diff --git a/src/lib/components/PageView.svelte b/src/lib/components/PageView.svelte new file mode 100644 index 0000000..6e9785a --- /dev/null +++ b/src/lib/components/PageView.svelte @@ -0,0 +1,27 @@ + diff --git a/src/lib/index.js b/src/lib/index.js index 9a9d0ad..b624ad0 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -8,11 +8,12 @@ import SignedOut from './components/SignedOut.svelte'; import DownloadURL from './components/DownloadURL.svelte'; import StorageList from './components/StorageList.svelte'; import UploadTask from './components/UploadTask.svelte'; -import { userStore } from './stores/auth.js'; -import { docStore, collectionStore } from './stores/firestore.js'; -import { nodeStore, nodeListStore } from './stores/rtdb.js'; -import { getFirebaseContext } from './stores/sdk.js'; -import { downloadUrlStore, storageListStore, uploadTaskStore } from './stores/storage.js'; +import PageView from './components/PageView.svelte'; +import { userStore } from './stores/auth'; +import { docStore, collectionStore } from './stores/firestore'; +import { nodeStore, nodeListStore } from './stores/rtdb'; +import { getFirebaseContext } from './stores/sdk'; +import { downloadUrlStore, storageListStore, uploadTaskStore } from './stores/storage'; export { Doc, @@ -24,6 +25,7 @@ export { UploadTask, StorageList, DownloadURL, + PageView, downloadUrlStore, storageListStore, uploadTaskStore, diff --git a/src/lib/stores/auth.ts b/src/lib/stores/auth.ts index 11b7a7b..0043725 100644 --- a/src/lib/stores/auth.ts +++ b/src/lib/stores/auth.ts @@ -1,5 +1,4 @@ import { writable } from "svelte/store"; -import { getFirebaseContext } from "./sdk.js"; import { onAuthStateChanged, type Auth } from "firebase/auth"; /** diff --git a/src/lib/stores/sdk.ts b/src/lib/stores/sdk.ts index 5509624..8a45e17 100644 --- a/src/lib/stores/sdk.ts +++ b/src/lib/stores/sdk.ts @@ -3,12 +3,14 @@ import type { Database } from "firebase/database"; import type { Auth } from "firebase/auth"; import { getContext, setContext } from "svelte"; import type { FirebaseStorage } from "firebase/storage"; +import type { Analytics } from "firebase/analytics"; export interface FirebaseSDKContext { auth?: Auth; firestore?: Firestore; rtdb?: Database; storage?: FirebaseStorage; + analytics?: Analytics | null; } export const contextKey = "firebase"; diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 8067999..8b27f1a 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,8 +1,8 @@ - + diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index ba43911..0546210 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -12,6 +12,7 @@
  • Realtime Database Test
  • SSR Test
  • Storage Test
  • +
  • Analytics Test
    • Auth Context: {!!ctx.auth}
    • diff --git a/src/routes/analytics-test/+layout.svelte b/src/routes/analytics-test/+layout.svelte new file mode 100644 index 0000000..145bf05 --- /dev/null +++ b/src/routes/analytics-test/+layout.svelte @@ -0,0 +1,12 @@ + + + + +

      Analytics Layout

      +{#key $page.route.id} +

      Logged analytics for {$page.route.id}

      + +{/key} diff --git a/src/routes/analytics-test/+page.server.ts b/src/routes/analytics-test/+page.server.ts new file mode 100644 index 0000000..10092b3 --- /dev/null +++ b/src/routes/analytics-test/+page.server.ts @@ -0,0 +1,7 @@ +import type { PageServerLoad } from './$types'; + +export const load: PageServerLoad = (async () => { + return { + title: 'Analytics Test 1 - Server Rendered', + }; +}) satisfies PageServerLoad; \ No newline at end of file diff --git a/src/routes/analytics-test/+page.svelte b/src/routes/analytics-test/+page.svelte new file mode 100644 index 0000000..c28122e --- /dev/null +++ b/src/routes/analytics-test/+page.svelte @@ -0,0 +1,21 @@ + + + + {data.title} + + +

      {data.title}

      + +

      + This is a test page for analytics. The title is always server rendered. +

      + +Analytics CSR Test + + + \ No newline at end of file diff --git a/src/routes/analytics-test/client-side/+page.svelte b/src/routes/analytics-test/client-side/+page.svelte new file mode 100644 index 0000000..51f9072 --- /dev/null +++ b/src/routes/analytics-test/client-side/+page.svelte @@ -0,0 +1,24 @@ + + + + {title} + + + + +

      {title}

      + +

      + This is a test page for analytics. The title is always server rendered. +

      + +Analytics SSR Test + diff --git a/src/routes/firebase.ts b/src/routes/firebase.ts index fcd7bc3..f5a9c47 100644 --- a/src/routes/firebase.ts +++ b/src/routes/firebase.ts @@ -19,6 +19,7 @@ import { ref as storageRef, uploadString, } from "firebase/storage"; +import { getAnalytics } from "firebase/analytics"; const firebaseConfig = { apiKey: "AIzaSyAMHfJp1ec85QBo-mnke89qtiYGen9zTSE", @@ -28,6 +29,7 @@ const firebaseConfig = { storageBucket: "sveltefire-testing.appspot.com", messagingSenderId: "1030648105982", appId: "1:1030648105982:web:2afebc34841fa242ed4eaf", + measurementId: "G-RT6GM89V6K" }; // Initialize Firebase @@ -36,6 +38,7 @@ export const db = getFirestore(app); export const rtdb = getDatabase(app); export const auth = getAuth(app); export const storage = getStorage(app); +export const analytics = typeof window !== "undefined" ? getAnalytics(app) : null; if (dev || import.meta.env.MODE === "ci") { connectAuthEmulator(auth, "http://localhost:9099"); diff --git a/tsconfig.json b/tsconfig.json index f56aae6..3c05e39 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,6 +9,6 @@ "skipLibCheck": true, "sourceMap": true, "strict": true, - "moduleResolution": "NodeNext" + "moduleResolution": "Bundler" } }