npm package: @plainapps/analytics-svelte
Description: Svelte stores and actions for Plain Web Analytics - privacy-focused, cookie-free analytics
Author: Plain Web Analytics
Latest version: 1.0.2

# @plainapps/analytics-svelte

Svelte stores and actions for [Plain Web Analytics](https://plainwebanalytics.com) - privacy-focused, cookie-free web analytics.

## Features

- 📊 **Svelte Stores** - Reactive stores for analytics state
- 🎯 **Action Directives** - Declarative tracking with `use:track`
- 🛤️ **SvelteKit Support** - Built-in SSR considerations
- ⚡ **Lightweight** - Tree-shakeable, minimal bundle impact
- 🔄 **Context API** - Share analytics across component hierarchies
- 📝 **TypeScript** - Full TypeScript support with type definitions

## Installation

```bash
npm install @plainapps/analytics-svelte
# or
yarn add @plainapps/analytics-svelte
# or
pnpm add @plainapps/analytics-svelte
```

## Quick Start

### Option 1: Stores (Recommended)

The simplest approach using Svelte stores:

```svelte
<!-- +layout.svelte -->
<script>
  import { onMount } from 'svelte';
  import { initAnalytics } from '@plainapps/analytics-svelte';

  onMount(() => {
    initAnalytics({ siteId: 'pwa_site_abc123' });
  });
</script>

<slot />
```

Track events anywhere:

```svelte
<script>
  import { trackEvent, trackGoal, isInitialized } from '@plainapps/analytics-svelte';

  function handleSignup() {
    trackEvent('signup_click', { source: 'homepage' });
  }

  function handlePurchase(amount) {
    trackGoal('purchase', { revenue: amount * 100 }); // cents
  }
</script>

<button on:click={handleSignup} disabled={!$isInitialized}>
  Sign Up
</button>
```

### Option 2: Action Directives

Declarative tracking without writing event handlers:

```svelte
<script>
  import { track, trackGoalAction } from '@plainapps/analytics-svelte';
</script>

<!-- Track clicks -->
<button use:track={{ event: 'signup_click', properties: { source: 'homepage' } }}>
  Sign Up
</button>

<!-- Track goals -->
<button use:trackGoalAction={{ goal: 'purchase', revenue: 9900 }}>
  Buy Now - $99
</button>

<!-- Track on different events -->
<input use:track={{ event: 'search_focus', on: 'focus' }} placeholder="Search..." />

<!-- Track only once -->
<div use:track={{ event: 'banner_view', on: 'mouseenter', once: true }}>
  Promotional Banner
</div>
```

### Option 3: Context API

For sharing analytics across component hierarchies:

```svelte
<!-- +layout.svelte -->
<script>
  import { onMount } from 'svelte';
  import { createPlainAnalytics, setAnalyticsContext } from '@plainapps/analytics-svelte';

  const analytics = createPlainAnalytics({ siteId: 'pwa_site_abc123' });
  setAnalyticsContext(analytics);

  onMount(() => {
    analytics.init();
  });
</script>

<slot />
```

```svelte
<!-- ChildComponent.svelte -->
<script>
  import { getAnalyticsContext } from '@plainapps/analytics-svelte';

  const { trackEvent, isInitialized } = getAnalyticsContext();

  let initialized = false;
  isInitialized.subscribe(v => initialized = v);
</script>

<button on:click={() => trackEvent('child_click')} disabled={!initialized}>
  Track from Child
</button>
```

## SvelteKit Integration

### Basic Setup

```svelte
<!-- src/routes/+layout.svelte -->
<script>
  import { onMount } from 'svelte';
  import { browser } from '$app/environment';
  import { initAnalytics } from '@plainapps/analytics-svelte';

  onMount(() => {
    if (browser) {
      initAnalytics({ siteId: 'pwa_site_abc123' });
    }
  });
</script>

<slot />
```

### Route Change Tracking

```svelte
<!-- src/routes/+layout.svelte -->
<script>
  import { onMount, afterNavigate } from 'svelte';
  import { browser } from '$app/environment';
  import { initAnalytics, trackPageview } from '@plainapps/analytics-svelte';

  onMount(() => {
    if (browser) {
      initAnalytics({ siteId: 'pwa_site_abc123', autoPageview: false });
    }
  });

  afterNavigate(() => {
    trackPageview();
  });
</script>

<slot />
```

### With SvelteKit's `page` Store

```svelte
<script>
  import { page } from '$app/stores';
  import { browser } from '$app/environment';
  import { initAnalytics, trackPageview, isInitialized } from '@plainapps/analytics-svelte';
  import { onMount } from 'svelte';

  onMount(() => {
    if (browser) {
      initAnalytics({ siteId: 'pwa_site_abc123', autoPageview: false });
    }
  });

  // Track pageviews reactively
  $: if ($isInitialized && browser) {
    trackPageview($page.url.pathname);
  }
</script>
```

## Vite Setup

No special configuration needed:

```svelte
<!-- App.svelte -->
<script>
  import { onMount } from 'svelte';
  import { initAnalytics } from '@plainapps/analytics-svelte';

  onMount(() => {
    initAnalytics({ siteId: 'pwa_site_abc123' });
  });
</script>
```

## API Reference

### Stores

#### `initAnalytics(options)`

Initialize analytics. Call in `onMount` or client-side code.

```ts
interface PlainAnalyticsOptions {
  siteId: string;           // Required: Your site key
  apiEndpoint?: string;     // Custom API endpoint
  autoPageview?: boolean;   // Auto-track initial pageview (default: true)
  hashMode?: boolean;       // Track hash changes (default: false)
  debug?: boolean;          // Enable debug logging (default: false)
}
```

#### `trackPageview(url?)`

Track a pageview manually.

```ts
import { trackPageview } from '@plainapps/analytics-svelte';

trackPageview();              // Track current URL
trackPageview('/custom-url'); // Track custom URL
```

#### `trackEvent(name, properties?)`

Track a custom event.

```ts
import { trackEvent } from '@plainapps/analytics-svelte';

trackEvent('button_click', { button: 'signup', variant: 'blue' });
```

#### `trackGoal(goalId, options?)`

Track a goal conversion.

```ts
import { trackGoal } from '@plainapps/analytics-svelte';

trackGoal('purchase', {
  revenue: 9900,                     // Revenue in cents
  properties: { product: 'pro-plan' }
});
```

#### `setEnabled(enabled)`

Enable or disable tracking.

```ts
import { setEnabled } from '@plainapps/analytics-svelte';

setEnabled(false); // Disable tracking
setEnabled(true);  // Enable tracking
```

#### Readable Stores

```ts
import { isInitialized, isEnabled, config } from '@plainapps/analytics-svelte';

$isInitialized // boolean - Whether analytics is ready
$isEnabled     // boolean - Whether tracking is enabled
$config        // AnalyticsConfig | null - Current configuration
```

### Actions

#### `use:track`

Track events declaratively.

```svelte
<button use:track={{ event: 'click', properties: { btn: 'cta' } }}>
  Click
</button>

<input use:track={{ event: 'focus', on: 'focus' }} />

<div use:track={{ event: 'hover', on: 'mouseenter', once: true }}>
  Hover me
</div>
```

#### `use:trackGoalAction`

Track goals declaratively.

```svelte
<button use:trackGoalAction={{ goal: 'signup', properties: { plan: 'pro' } }}>
  Sign Up
</button>

<button use:trackGoalAction={{ goal: 'purchase', revenue: 9900 }}>
  Buy Now
</button>
```

#### `use:trackVisible`

Track when an element enters the viewport.

```svelte
<section use:trackVisible={{ event: 'pricing_viewed' }}>
  Pricing content...
</section>

<div use:trackVisible={{ event: 'product_impression', properties: { id: 'abc' } }}>
  Product Card
</div>
```

### Context API

#### `createPlainAnalytics(options)`

Create an analytics instance for context.

```ts
import { createPlainAnalytics, setAnalyticsContext } from '@plainapps/analytics-svelte';

const analytics = createPlainAnalytics({ siteId: 'pwa_site_abc123' });
setAnalyticsContext(analytics);

// Initialize in onMount
analytics.init();
```

#### `getAnalyticsContext()`

Get analytics from context (throws if not found).

```ts
import { getAnalyticsContext } from '@plainapps/analytics-svelte';

const { trackEvent, isInitialized } = getAnalyticsContext();
```

#### `hasAnalyticsContext()`

Check if context exists (doesn't throw).

```ts
import { hasAnalyticsContext } from '@plainapps/analytics-svelte';

if (hasAnalyticsContext()) {
  // Context is available
}
```

## Examples

### Track Form Submission

```svelte
<script>
  import { trackEvent, trackGoal } from '@plainapps/analytics-svelte';

  async function handleSubmit() {
    const success = await submitForm();

    if (success) {
      trackEvent('form_submit', { form: 'contact' });
      trackGoal('lead_capture');
    }
  }
</script>

<form on:submit|preventDefault={handleSubmit}>
  <input type="email" placeholder="Email" />
  <button type="submit">Subscribe</button>
</form>
```

### Conditional Tracking (GDPR)

```svelte
<script>
  import { setEnabled, isEnabled } from '@plainapps/analytics-svelte';
</script>

<label>
  <input
    type="checkbox"
    checked={$isEnabled}
    on:change={(e) => setEnabled(e.target.checked)}
  />
  Allow analytics
</label>
```

### Track E-commerce Purchase

```svelte
<script>
  import { trackGoal } from '@plainapps/analytics-svelte';

  async function handleCheckout(cart) {
    const order = await processPayment(cart);

    trackGoal('purchase', {
      revenue: order.total * 100, // cents
      properties: {
        order_id: order.id,
        items: cart.items.length,
      },
    });
  }
</script>
```

### Track Scroll Depth

```svelte
<script>
  import { onMount } from 'svelte';
  import { trackEvent, isInitialized } from '@plainapps/analytics-svelte';

  const tracked = { 25: false, 50: false, 75: false, 100: false };

  onMount(() => {
    function handleScroll() {
      if (!$isInitialized) return;

      const depth = Math.round(
        (window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100
      );

      for (const threshold of [25, 50, 75, 100]) {
        if (depth >= threshold && !tracked[threshold]) {
          tracked[threshold] = true;
          trackEvent('scroll_depth', { depth: threshold });
        }
      }
    }

    window.addEventListener('scroll', handleScroll, { passive: true });
    return () => window.removeEventListener('scroll', handleScroll);
  });
</script>
```

## TypeScript

Full TypeScript support is included:

```ts
import type {
  PlainAnalyticsOptions,
  PlainAnalyticsContext,
  PlainAnalyticsPlugin,
  GoalOptions,
  TrackActionOptions,
  TrackGoalActionOptions,
  EventProperties,
  Goal,
} from '@plainapps/analytics-svelte';
```

## License

MIT