npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@getecho-ai/react-native-sdk

v1.1.3

Published

Echo AI Chat SDK for React Native - AI-powered e-commerce assistant

Readme

Echo React Native SDK

AI-powered chat and e-commerce SDK for React Native apps. Add an intelligent shopping assistant to your app in minutes.

Installation

npm install @getecho-ai/react-native-sdk react-native-webview @react-native-async-storage/async-storage

Expo users:

npx expo install @getecho-ai/react-native-sdk react-native-webview @react-native-async-storage/async-storage

Quick Start (< 5 minutes)

The fastest way to get started - uses the built-in useSimpleCart hook for automatic cart management:

import { EchoProvider, EchoChat, useSimpleCart } from '@getecho-ai/react-native-sdk';

function App() {
  const { cart, callbacks } = useSimpleCart();

  return (
    <EchoProvider config={{ apiKey: 'your-api-key', callbacks }}>
      <YourApp cart={cart} />
      <EchoChat />
    </EchoProvider>
  );
}

That's it! The AI assistant can now add products to cart and show cart contents.

Production Setup (Custom Cart)

For production apps with existing cart logic:

import { EchoProvider, EchoChat } from '@getecho-ai/react-native-sdk';

function App() {
  return (
    <EchoProvider
      config={{
        apiKey: 'your-api-key',
        callbacks: {
          // Required: Handle add to cart
          onAddToCart: async (product, quantity = 1) => {
            await yourCartService.add(product.id, quantity);
            return {
              success: true,
              cartItemCount: yourCartService.getItemCount()
            };
          },

          // Required: Return current cart
          onGetCart: async () => {
            const cart = await yourCartService.getCart();
            return {
              success: true,
              cart: {
                items: cart.items.map(item => ({
                  productId: item.id,
                  quantity: item.qty,
                  product: item.product,
                })),
                itemCount: cart.totalItems,
                total: cart.totalPrice,
                currency: 'TRY',
              }
            };
          },

          // Optional: Navigate to product detail
          onNavigateToProduct: (productId) => {
            navigation.navigate('ProductDetail', { id: productId });
          },

          // Optional: Navigate to checkout
          onNavigateToCheckout: () => {
            navigation.navigate('Checkout');
          },

          // Optional: Track order status
          onTrackOrderState: async ({ orderId }) => {
            const res = await fetch(`/api/orders/${orderId}`);
            const order = await res.json();
            return { success: res.ok, status: order.status, details: order };
          },

          // Optional: Get order history
          onGetOrders: async () => {
            const res = await fetch('/api/orders');
            const data = await res.json();
            return { success: res.ok, orders: data.orders };
          },
        },
      }}
    >
      <YourApp />
      <EchoChat />
    </EchoProvider>
  );
}

Local Development

When developing locally with the Echo backend:

<EchoProvider
  config={{
    apiKey: 'your-api-key',
    apiUrl: 'http://localhost:3000', // Auto-converts to 10.0.2.2 on Android emulator
    callbacks,
  }}
>

The SDK automatically handles Android emulator's localhost quirk (converts localhost to 10.0.2.2).

For real devices, use your machine's local IP:

apiUrl: 'http://192.168.1.100:3000'

Components

<EchoProvider>

Wrap your app with this provider. Required props:

| Prop | Type | Description | |------|------|-------------| | config.apiKey | string | Your Echo API key | | config.callbacks | EchoCallbacks | Cart and navigation callbacks |

Optional props:

| Prop | Type | Description | |------|------|-------------| | config.apiUrl | string | Override API URL (default: production) | | config.userId | string | Pre-set user ID | | config.userEmail | string | User email for identification | | config.userIdentifier | string | Custom user identifier | | config.theme | EchoTheme | UI customization | | config.uiSettings | UISettings | Control UI elements visibility | | config.onEvent | (events: EchoEvent[]) => void | Callback fired when event batches flush |

<EchoChat>

The main chat component. Supports both modal (overlay) and inline (embedded) modes.

// Modal mode (default) - floating button + full-screen overlay
<EchoChat />
<EchoChat floating={false} />
<EchoChat position="bottom-left" />

// Inline mode - embedded directly in your layout
<EchoChat mode="inline" />
<EchoChat
  mode="inline"
  inlineProps={{
    showHeader: true,
    headerTitle: "Support Chat",
    onClose: () => closeChat()
  }}
/>

Props:

| Prop | Type | Default | Description | |------|------|---------|-------------| | mode | "modal" \| "inline" | "modal" | Display mode | | floating | boolean | true | Show floating button (modal only) | | position | "bottom-right" \| "bottom-left" | "bottom-right" | Button position | | inlineProps | EchoChatInlineProps | - | Props for inline mode |

<EchoChatInline>

Inline chat component for embedding in screens or tabs. Does not use a modal wrapper.

import { useEffect } from 'react';
import { EchoChatInline, useEcho } from '@getecho-ai/react-native-sdk';

function ChatScreen() {
  const { openChat, closeChat } = useEcho();

  useEffect(() => {
    openChat();
    return closeChat;
  }, [openChat, closeChat]);

  return (
    <View style={{ flex: 1 }}>
      <EchoChatInline
        showHeader
        headerTitle="Support Chat"
        onClose={closeChat}
        style={{ backgroundColor: '#f5f5f5' }}
      />
    </View>
  );
}

Props:

| Prop | Type | Default | Description | |------|------|---------|-------------| | showHeader | boolean | false | Show header bar | | headerTitle | string | "AI Asistan" | Header title text | | onClose | () => void | - | Close button callback (shown when showHeader=true) | | style | ViewStyle | - | Container style |

<EchoChatModal>

Full-screen modal chat component. Reads visibility from useEcho() context. Use openChat() / closeChat() to control it.

<EchoChatModal />

<EchoChatButton>

Standalone chat button for custom layouts:

// Basic usage
<EchoChatButton
  onPress={() => setModalVisible(true)}
  position="bottom-left"
/>

// Custom icon (emoji)
<EchoChatButton
  onPress={() => setModalVisible(true)}
  icon="🤖"
  iconSize={28}
  color="#FF5722"
/>

// Custom icon (Image)
<EchoChatButton
  onPress={() => setModalVisible(true)}
  icon={<Image source={require('./chat-icon.png')} style={{ width: 32, height: 32 }} />}
  color="#6366f1"
/>

// Custom icon (Vector Icon)
import Icon from 'react-native-vector-icons/MaterialIcons';

<EchoChatButton
  onPress={() => setModalVisible(true)}
  icon={<Icon name="chat" size={28} color="#fff" />}
  color="#1e88e5"
/>

Props:

  • onPress: () => void - Button press handler
  • position?: "bottom-right" | "bottom-left" - Button position (default: "bottom-right")
  • color?: string - Background color (default: "#007AFF")
  • icon?: React.ReactNode | string - Custom icon (emoji string or React component) (default: "💬")
  • iconSize?: number - Icon size for text/emoji icons (default: 24)
  • unreadCount?: number - Show unread message badge

Hooks

useEcho()

Access Echo context anywhere in your app:

const {
  // State
  config,               // EchoConfig
  userId,               // string
  chatId,               // string
  isReady,              // boolean
  isChatOpen,           // boolean

  // Chat Control
  sendMessage,          // (message: string) => void
  searchProducts,       // (query: string) => void
  openChat,             // () => void
  closeChat,            // () => void

  // User Identity
  identify,             // (params: IdentifyParams) => Promise<IdentifyResult>
  logout,               // () => Promise<void>

  // Recommendations
  getRecommendations,   // (request: RecommendationRequest) => Promise<RecommendationResult>

  // Event Tracking
  trackPageView,        // (data: { url: string; title?: string }) => void
  viewedProduct,        // (data: { productId: string }) => void
  showRecommendation,   // (productId: string) => void
  clickRecommendation,  // (productId: string) => void
  trackAddToCart,       // (data: AddToCartEventData) => void
  trackPurchase,        // (data: PurchaseEventData) => void
  trackEvent,           // (eventType: string, eventData?: Record<string, unknown>) => void
} = useEcho();

useSimpleCart(options?)

Built-in cart management for quick prototyping:

const {
  cart,           // Current cart state
  setCart,        // Direct cart setter
  clearCart,      // Clear all items
  removeItem,     // Remove by productId
  updateQuantity, // Update item quantity
  callbacks,      // Ready-to-use EchoCallbacks
} = useSimpleCart({
  initialCart: { items: [], itemCount: 0 },
  onCartChange: (cart) => console.log('Cart updated:', cart),
  onNavigateToProduct: (id) => navigation.navigate('Product', { id }),
  onNavigateToCheckout: () => navigation.navigate('Checkout'),
  onAuthRequired: () => navigation.navigate('Login'),
  onTrackOrderState: async ({ orderId }) => {
    const res = await fetch(`/api/orders/${orderId}`);
    const order = await res.json();
    return { success: res.ok, status: order.status, details: order };
  },
  onGetOrders: async () => {
    const res = await fetch('/api/orders');
    const data = await res.json();
    return { success: res.ok, orders: data.orders };
  },
});

Messaging

sendMessage(message)

Send a message to the chat programmatically. Opens the chat if closed:

const { sendMessage } = useEcho();
sendMessage('I need help finding a gift');

searchProducts(query)

Trigger a product search in the chat. Opens the chat if closed:

const { searchProducts } = useEcho();
searchProducts('red summer dress');

Events & Analytics

The SDK automatically tracks user interactions and provides methods for custom event tracking. Events are GA4-compatible and batched for performance.

Automatic Events

| Event | Trigger | |-------|---------| | widget_open | Chat opened | | widget_close | Chat closed | | new_chat | New chat session started | | product_view | User taps a product in chat | | product_click | User taps to visit a product URL |

Manual Tracking

const {
  trackPageView,
  viewedProduct,
  showRecommendation,
  clickRecommendation,
  trackAddToCart,
  trackPurchase,
  trackEvent,
} = useEcho();

// Track screen views
trackPageView({ url: 'app://products/123', title: 'Product Detail' });

// Track product views
viewedProduct({ productId: 'SKU_123' });

// Track recommendations
showRecommendation('SKU_456');
clickRecommendation('SKU_456');

// GA4-compatible add to cart
trackAddToCart({
  currency: 'USD',
  value: 114.00,
  items: [{ item_id: 'SKU_123', price: 114.00, item_name: 'Product Name', quantity: 1 }]
});

// GA4-compatible purchase
trackPurchase({
  transaction_id: 'T_12345',
  value: 129.99,
  currency: 'USD',
  items: [{ item_id: 'SKU_123', price: 114.00 }]
});

// Custom events
trackEvent('share_product', { productId: 'SKU_123', method: 'whatsapp' });

Listening to Events

Forward events to your analytics provider:

<EchoProvider
  config={{
    apiKey: 'your-api-key',
    callbacks,
    onEvent: (events) => {
      events.forEach(event => {
        analytics.logEvent(event.eventType, event.eventData);
      });
    }
  }}
>

Events are batched: flushed after 5s of inactivity, when 10 events accumulate, or when the app backgrounds.

Recommendations

Fetch AI-powered product recommendations:

const { getRecommendations } = useEcho();

const result = await getRecommendations({
  productIds: ['SKU_123'],
  strategy: 'similar',  // 'similar' | 'complementary' | 'bought_together' | 'cart'
  limit: 8,
});

result.recommendations.forEach(product => {
  console.log(product.title, product.primaryImage, product.priceAmount);
});

UI Settings

Control visibility of UI elements:

<EchoProvider
  config={{
    apiKey: 'your-key',
    callbacks,
    uiSettings: {
      showSidebar: false,       // Hide conversation history sidebar
      showExpandButton: false,  // Hide expand/collapse button
      showCartButton: true,     // Show cart button in header
      showHistoryButton: false, // Hide history/new chat button
      showCloseButton: true,    // Show close button
    },
  }}
>

User Authentication

Echo SDK supports three user states:

  1. Anonymous - Default state. A random UUID is generated and persisted automatically.
  2. Identified - User is linked to an email or identifier. Chat history migrates from anonymous to identified user.
  3. Logged out - User is reset to a new anonymous state with a fresh UUID.

Identifying Users

Option 1: Via config (automatic)

Pass userEmail or userIdentifier in the provider config. The SDK auto-identifies on mount:

<EchoProvider
  config={{
    apiKey: 'your-key',
    callbacks,
    userEmail: user.email,
    userIdentifier: user.id,
  }}
>

Option 2: Via hook (imperative)

Call identify() after login for full control:

const { identify } = useEcho();

const result = await identify({
  email: '[email protected]',
  userIdentifier: 'user-123',
  firstName: 'John',
  lastName: 'Doe',
  phone: '+905551234567',
  traits: { plan: 'premium' },
});
// result: { success: true, userId: '...', userIdChanged: true }

Logging Out

Call logout() to reset to anonymous state. This generates a new anonymous UUID and clears chat history:

const { logout } = useEcho();

await logout();

Utilities

resolveApiUrl(url?)

Platform-aware URL resolution for development:

import { resolveApiUrl } from '@getecho-ai/react-native-sdk';

const url = resolveApiUrl('http://localhost:3000');
// Android emulator: 'http://10.0.2.2:3000'
// iOS simulator: 'http://localhost:3000'
// Production URL: unchanged

getLocalhostUrl(port?)

Get the correct localhost URL for current platform:

import { getLocalhostUrl } from '@getecho-ai/react-native-sdk';

const url = getLocalhostUrl(3000);
// Android: 'http://10.0.2.2:3000'
// iOS: 'http://localhost:3000'

Troubleshooting

"Network request failed" on Android emulator

The SDK should auto-convert localhost URLs. If issues persist:

// Explicitly use 10.0.2.2
apiUrl: 'http://10.0.2.2:3000'

WebView not loading

Ensure peer dependencies are installed:

npm ls react-native-webview @react-native-async-storage/async-storage

For Expo, run npx expo install to get compatible versions.

Cart not updating

  1. Verify onAddToCart returns { success: true, cartItemCount: N }
  2. Check onGetCart returns valid cart structure
  3. Add console.log in callbacks to debug

TypeScript errors

Import types explicitly:

import type { Product, Cart, EchoCallbacks } from '@getecho-ai/react-native-sdk';

API Reference

Types

type Product = {
  id: string;
  title: string;
  description?: string;
  priceAmount?: number;
  currency?: string;
  primaryImage?: string;
  images?: string[];
  url?: string;
  category?: string;
  brand?: string;
  inStock?: boolean;
};

type Cart = {
  items: CartItem[];
  total?: number;
  currency?: string;
  itemCount: number;
};

type CartItem = {
  productId: string;
  quantity: number;
  product?: Product;
};

type AddToCartResult = {
  success: boolean;
  cartItemCount?: number;
  error?: string;
};

type GetCartResult = {
  success: boolean;
  cart?: Cart;
  error?: string;
};

type TrackOrderStateResult = {
  success: boolean;
  status?: string;
  details?: any;
  error?: string;
};

type GetOrdersResult = {
  success: boolean;
  orders?: any[];
  error?: string;
};

type EchoCallbacks = {
  onAddToCart: (product: Product, quantity?: number) => Promise<AddToCartResult>;
  onGetCart: () => Promise<GetCartResult>;
  onNavigateToProduct?: (productId: string) => void;
  onNavigateToUrl?: (url: string) => void;
  onNavigateToCheckout?: () => void;
  onAuthRequired?: () => void;
  onTrackOrderState?: (data: { orderId: string | number }) => Promise<TrackOrderStateResult>;
  onGetOrders?: () => Promise<GetOrdersResult>;
};

type UISettings = {
  showSidebar?: boolean;
  showExpandButton?: boolean;
  showCartButton?: boolean;
  showHistoryButton?: boolean;
  showCloseButton?: boolean;
};

type GA4Item = {
  item_id: string;
  price: number;
  item_name?: string;
  quantity?: number;
  item_brand?: string;
  item_category?: string;
  item_variant?: string;
};

type AddToCartEventData = {
  items: GA4Item[];
  currency?: string;
  value?: number;
};

type PurchaseEventData = {
  transaction_id: string;
  items: GA4Item[];
  value?: number;
  currency?: string;
  tax?: number;
  shipping?: number;
  coupon?: string;
  affiliation?: string;
};

type EchoEvent = {
  chatId: string | null;
  eventType: string;
  eventData: Record<string, unknown>;
  timestamp: string;
  userEmail?: string;
  userIdentifier?: string;
};

type IdentifyParams = {
  email?: string;
  userIdentifier?: string;
  firstName?: string;
  lastName?: string;
  phone?: string;
  traits?: Record<string, unknown>;
};

type IdentifyResult = {
  success: boolean;
  userId: string;
  email?: string;
  userIdentifier?: string;
  userIdChanged: boolean;
};

type RecommendationRequest = {
  productIds: string[];
  strategy: 'similar' | 'complementary' | 'bought_together' | 'cart';
  limit?: number;
  threshold?: number;
  minCount?: number;
  weights?: {
    semantic?: number;
    color?: number;
    price?: number;
    category?: number;
  };
};

type RecommendationResult = {
  success: boolean;
  strategy: string;
  recommendations: RecommendedProduct[];
  count: number;
  message?: string;
  error?: string;
};

type RecommendedProduct = {
  id: string;
  productId: string;
  title: string;
  brand?: string;
  url?: string;
  images: string[];
  primaryImage?: string | null;
  categoryBreadcrumb?: string[];
  priceAmount?: string;
  similarity?: number;
  embeddingSimilarity?: number;
  colorSimilarity?: number;
  summary?: string | null;
};

Example App

See the /example directory for a complete Expo demo app.

License

MIT