Socket
Book a DemoInstallSign in
Socket

@bitte-kaufen/expo-umami

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@bitte-kaufen/expo-umami

Umami analytics integration for Expo apps with batching and offline support

latest
Source
npmnpm
Version
0.2.6
Version published
Maintainers
1
Created
Source

@bitte-kaufen/expo-umami

CI npm version License: MIT

Easy Umami analytics integration for Expo apps with automatic batching, offline support, and app state management.

Features

  • Easy Setup: Configure via app.json or programmatically
  • Automatic Batching: Groups events and sends them in batches to reduce network calls
  • Smart Flushing: Automatically flushes queue when app backgrounds or closes
  • Offline Support: Optional AsyncStorage persistence for offline events
  • TypeScript: Full type safety out of the box
  • Expo Integration: Built specifically for Expo applications

Installation

npx expo install @bitte-kaufen/expo-umami @react-native-async-storage/async-storage expo-application expo-constants expo-device expo-localization

Configuration

Add the plugin to your app.json:

{
  "expo": {
    "plugins": [
      [
        "@bitte-kaufen/expo-umami",
        {
          "websiteId": "your-website-id",
          "hostUrl": "https://analytics.example.com",
          "batchSize": 10,
          "batchInterval": 30000,
          "persistEvents": true,
          "debug": false
        }
      ]
    ]
  }
}

Then initialize in your app without parameters:

import { initUmami } from '@bitte-kaufen/expo-umami';

// In your App.tsx or _layout.tsx
useEffect(() => {
  initUmami(); // Reads config from app.json
}, []);

Option 2: Programmatic Configuration

import { initUmami } from '@bitte-kaufen/expo-umami';

useEffect(() => {
  initUmami({
    websiteId: 'ed825179-00f1-4828-a8a3-df52b68b58b5',
    hostUrl: 'https://analytics.bitte.kaufen',
    batchSize: 10,        // Send after 10 events (default: 10)
    batchInterval: 30000, // Or every 30 seconds (default: 30000)
    persistEvents: true,  // Save to AsyncStorage for offline (default: false)
    debug: true,          // Enable debug logging (default: false)
  });
}, []);

Usage

💡 Best Practice: Use Web-Style URLs

When tracking screens, use web-style URL paths (e.g., /home, /product/details) instead of component names (e.g., HomeScreen, ProductDetailsScreen). This:

  • Makes analytics data consistent with web applications
  • Provides cleaner, more readable URLs in Umami dashboard
  • Allows for hierarchical organization (/settings/profile, /settings/notifications)
  • Example: Use /overview instead of OverviewScreen

Track Screen Views

import { trackEvent, trackScreenView } from '@bitte-kaufen/expo-umami';

function HomeScreen() {
  useEffect(() => {
    trackScreenView('/home', { title: 'Home Screen' });
  }, []);

  return <View>...</View>;
}

Track Events with Data

import { trackEvent } from '@bitte-kaufen/expo-umami';

function ProductScreen() {
  const handlePurchase = () => {
    trackEvent('/product/purchase', {
      title: 'Product Purchase',
      data: {
        productId: '123',
        price: 29.99,
        currency: 'USD',
      },
    });
  };

  return <Button onPress={handlePurchase}>Buy Now</Button>;
}

Track Custom Events (Clicks, Impressions, etc.)

import { trackClick, trackImpression, trackCustomEvent } from '@bitte-kaufen/expo-umami';

function ProductCard({ product }) {
  useEffect(() => {
    // Track when product card is viewed
    trackImpression('/product/card', {
      title: 'Product Card',
      data: { productId: product.id, price: product.price },
    });
  }, [product]);

  const handleAddToCart = () => {
    // Track button click
    trackClick('/product/add-to-cart', {
      title: 'Add to Cart Button',
      data: { productId: product.id },
    });
  };

  const handleShare = () => {
    // Track custom event with any name
    trackCustomEvent('/product/card', 'share', {
      title: 'Product Card',
      data: { productId: product.id, method: 'social' },
    });
  };

  return (
    <View>
      <Button onPress={handleAddToCart}>Add to Cart</Button>
      <Button onPress={handleShare}>Share</Button>
    </View>
  );
}

Advanced: Custom Event Names

You can also pass a custom event name and title to trackEvent:

trackEvent('/home/hero', {
  title: 'Home Hero Banner',
  eventName: 'banner_interaction',
  data: { position: 'top', action: 'swipe' },
});

Manual Flush

import { flush } from '@bitte-kaufen/expo-umami';

// Force send all queued events immediately
await flush();

Check Status

import { isInitialized, getQueueSize } from '@bitte-kaufen/expo-umami';

console.log('Initialized:', isInitialized());
console.log('Queued events:', getQueueSize());

Configuration Options

OptionTypeDefaultDescription
websiteIdstringrequiredYour Umami website ID
hostUrlstringrequiredYour Umami instance URL
batchSizenumber10Number of events before auto-flush
batchIntervalnumber30000Milliseconds between auto-flushes
persistEventsbooleanfalseSave events to AsyncStorage for offline
debugbooleanfalseEnable console logging

How It Works

  • Event Queueing: Events are added to an in-memory queue
  • Batch Sending: Events are sent when:
    • Queue reaches batchSize events, or
    • batchInterval milliseconds have passed, or
    • App goes to background/inactive state
  • Batch API: Uses Umami's /api/batch endpoint for efficient bulk sending
  • Offline Support: When enabled, events persist to AsyncStorage and survive app restarts
  • URL Normalization: Automatically ensures URLs start with / for consistency

Event Formatting

URLs are used as-is with minimal normalization:

trackEvent('/home/product-list');
// Sends: url = '/home/product-list', title = '/home/product-list'

trackEvent('/settings', { title: 'Settings Screen' });
// Sends: url = '/settings', title = 'Settings Screen'

trackEvent('home');
// Sends: url = '/home', title = 'home' (automatically adds leading /)

API Reference

initUmami(config?: UmamiConfig): Promise<void>

Initialize the Umami client. Can be called without parameters if using config plugin.

trackEvent(url: string, options?: TrackEventOptions): Promise<void>

Track a screen view or event. Options include:

  • title?: string - Custom title for the event (defaults to url)
  • eventName?: string - Custom event name (makes it a custom event instead of pageview)
  • data?: Record<string, any> - Custom event data

trackScreenView(url: string, options?: TrackEventOptions): Promise<void>

Alias for trackEvent. Use whichever reads better in your code.

trackClick(elementName: string, options?: TrackEventOptions): Promise<void>

Track a click event. Automatically sets event name to 'click'.

trackImpression(elementName: string, options?: TrackEventOptions): Promise<void>

Track an impression event. Automatically sets event name to 'impression'.

trackCustomEvent(url: string, eventName: string, options?: TrackEventOptions): Promise<void>

Track a custom event with a specific event name.

flush(): Promise<void>

Manually flush all queued events immediately.

isInitialized(): boolean

Check if the client has been initialized.

getQueueSize(): number

Get the current number of queued events.

Example App

// app/_layout.tsx
import { useEffect } from 'react';
import { initUmami } from '@bitte-kaufen/expo-umami';
import { Stack } from 'expo-router';

export default function RootLayout() {
  useEffect(() => {
    initUmami(); // Uses app.json config
  }, []);

  return <Stack />;
}

// app/index.tsx
import { useEffect } from 'react';
import { View, Text } from 'react-native';
import { trackScreenView } from '@bitte-kaufen/expo-umami';

export default function HomeScreen() {
  useEffect(() => {
    trackScreenView('/home', { title: 'Home Screen' });
  }, []);

  return (
    <View>
      <Text>Welcome!</Text>
    </View>
  );
}

Migration from Umami Tracker

If you're currently using umami.track() directly:

Before:

umami.track({
  hostname: Application.applicationId ?? "unknown.app",
  language: i18n.currentLocale(),
  screen: `${Layout.window.width}x${Layout.window.height}`,
  title: screenName,
  url: `/my-page`,
  data: { foo: 'bar' },
});

After:

trackEvent('/my-page', {
  title: 'My Page',
  data: { foo: 'bar' },
});

All the boilerplate (hostname, language, screen size) is handled automatically.

License

MIT

Contributing

Contributions are welcome! Please open an issue or PR on GitHub.

Keywords

expo

FAQs

Package last updated on 13 Dec 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts