Skip to main content
The Constructor.io JavaScript Client can be used in React Native applications, but requires additional configuration since React Native doesn’t provide a full browser DOM environment.
This client is primarily designed for browser environments. For server-side integrations, use @constructor-io/constructorio-node instead.

DOM-Less Environment

React Native applications don’t have access to standard browser APIs like document, window, or native fetch. The Constructor.io client needs these APIs to function properly.

Required Polyfills

To use the client in React Native, you need to provide polyfills for missing browser APIs:

1. Fetch API

The client requires a fetch implementation. React Native includes a native fetch, but you need to ensure it’s available globally:
// No additional setup needed - React Native includes fetch by default

2. URL Polyfill

You need a URL polyfill for URL parsing:
npm install react-native-url-polyfill
import 'react-native-url-polyfill/auto';

3. EventTarget Polyfill

For custom event dispatching:
npm install event-target-shim
import { EventTarget } from 'event-target-shim';

// Make EventTarget available globally
if (!global.EventTarget) {
  global.EventTarget = EventTarget;
}

Installation

1

Install the Client

Install the Constructor.io JavaScript client:
npm install @constructor-io/constructorio-client-javascript
2

Install Polyfills

Install required polyfills:
npm install react-native-url-polyfill event-target-shim
3

Setup Polyfills

Create a setup file to initialize polyfills before using the client:
// polyfills.js
import 'react-native-url-polyfill/auto';
import { EventTarget } from 'event-target-shim';

// Make EventTarget available globally
if (!global.EventTarget) {
  global.EventTarget = EventTarget;
}

// Create a minimal window mock for event dispatching
if (!global.window) {
  global.window = {
    addEventListener: () => {},
    removeEventListener: () => {},
    dispatchEvent: () => {},
  };
}
4

Import Polyfills

Import the polyfills file at the very top of your app entry point:
// App.js or index.js
import './polyfills';
import ConstructorioClient from '@constructor-io/constructorio-client-javascript';

// Rest of your app code

Basic Usage

After setting up polyfills, use the client as you would in a browser environment:
import './polyfills';
import ConstructorioClient from '@constructor-io/constructorio-client-javascript';

const constructorio = new ConstructorioClient({
  apiKey: 'YOUR_API_KEY',
  userId: 'user-123',
});

// Use the client
constructorio.search.getSearchResults('running shoes')
  .then((response) => {
    console.log(response.response.results);
  })
  .catch((error) => {
    console.error('Search error:', error);
  });

Complete Example

Here’s a complete React Native component using Constructor.io:
import React, { useState, useEffect } from 'react';
import {
  View,
  TextInput,
  FlatList,
  Text,
  TouchableOpacity,
  Image,
  StyleSheet,
} from 'react-native';
import './polyfills';
import ConstructorioClient from '@constructor-io/constructorio-client-javascript';

const constructorio = new ConstructorioClient({
  apiKey: 'YOUR_API_KEY',
});

const SearchScreen = () => {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);
  const [loading, setLoading] = useState(false);

  const handleSearch = async (searchQuery) => {
    if (searchQuery.length < 2) {
      setResults([]);
      return;
    }

    setLoading(true);

    try {
      const response = await constructorio.search.getSearchResults(searchQuery, {
        resultsPerPage: 20,
      });

      setResults(response.response.results);
    } catch (error) {
      console.error('Search error:', error);
    } finally {
      setLoading(false);
    }
  };

  const handleProductPress = (product) => {
    // Track the click
    constructorio.tracker.trackSearchResultClick(
      product.data.id,
      query,
      {
        result_id: product.result_id,
      }
    );

    // Navigate to product details
    // navigation.navigate('ProductDetails', { productId: product.data.id });
  };

  return (
    <View style={styles.container}>
      <TextInput
        style={styles.searchInput}
        placeholder="Search products..."
        value={query}
        onChangeText={(text) => {
          setQuery(text);
          handleSearch(text);
        }}
      />

      {loading && <Text style={styles.loading}>Loading...</Text>}

      <FlatList
        data={results}
        keyExtractor={(item) => item.data.id}
        renderItem={({ item }) => (
          <TouchableOpacity
            style={styles.productItem}
            onPress={() => handleProductPress(item)}
          >
            {item.data.image_url && (
              <Image
                source={{ uri: item.data.image_url }}
                style={styles.productImage}
              />
            )}
            <View style={styles.productInfo}>
              <Text style={styles.productTitle}>{item.data.title}</Text>
              {item.data.price && (
                <Text style={styles.productPrice}>${item.data.price}</Text>
              )}
            </View>
          </TouchableOpacity>
        )}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 16,
  },
  searchInput: {
    height: 48,
    borderWidth: 1,
    borderColor: '#ddd',
    borderRadius: 8,
    paddingHorizontal: 16,
    marginBottom: 16,
  },
  loading: {
    textAlign: 'center',
    padding: 16,
  },
  productItem: {
    flexDirection: 'row',
    padding: 12,
    borderBottomWidth: 1,
    borderBottomColor: '#eee',
  },
  productImage: {
    width: 80,
    height: 80,
    borderRadius: 8,
    marginRight: 12,
  },
  productInfo: {
    flex: 1,
    justifyContent: 'center',
  },
  productTitle: {
    fontSize: 16,
    fontWeight: '600',
    marginBottom: 4,
  },
  productPrice: {
    fontSize: 14,
    color: '#666',
  },
});

export default SearchScreen;

Autocomplete Example

Implementing autocomplete with debouncing:
import React, { useState, useEffect, useCallback } from 'react';
import { View, TextInput, FlatList, Text, TouchableOpacity } from 'react-native';
import './polyfills';
import ConstructorioClient from '@constructor-io/constructorio-client-javascript';

const constructorio = new ConstructorioClient({
  apiKey: 'YOUR_API_KEY',
});

const AutocompleteSearch = () => {
  const [query, setQuery] = useState('');
  const [suggestions, setSuggestions] = useState([]);
  const [debounceTimer, setDebounceTimer] = useState(null);

  const fetchSuggestions = async (searchQuery) => {
    if (searchQuery.length < 2) {
      setSuggestions([]);
      return;
    }

    try {
      const response = await constructorio.autocomplete.getAutocompleteResults(
        searchQuery,
        {
          resultsPerSection: {
            'Search Suggestions': 5,
            Products: 5,
          },
        }
      );

      // Flatten suggestions from all sections
      const allSuggestions = [];
      Object.keys(response.sections).forEach((section) => {
        response.sections[section].forEach((item) => {
          allSuggestions.push({
            ...item,
            section,
          });
        });
      });

      setSuggestions(allSuggestions);
    } catch (error) {
      console.error('Autocomplete error:', error);
    }
  };

  const handleQueryChange = (text) => {
    setQuery(text);

    // Clear previous debounce timer
    if (debounceTimer) {
      clearTimeout(debounceTimer);
    }

    // Set new debounce timer
    const timer = setTimeout(() => {
      fetchSuggestions(text);
    }, 300);

    setDebounceTimer(timer);
  };

  const handleSuggestionPress = (suggestion) => {
    // Track the selection
    constructorio.tracker.trackAutocompleteSelect(
      suggestion.value || suggestion.data.id,
      {
        result_id: suggestion.result_id,
      }
    );

    // Perform action based on suggestion type
    if (suggestion.section === 'Search Suggestions') {
      setQuery(suggestion.value);
      fetchSuggestions(suggestion.value);
    } else {
      // Navigate to product
    }
  };

  return (
    <View>
      <TextInput
        placeholder="Search..."
        value={query}
        onChangeText={handleQueryChange}
      />

      <FlatList
        data={suggestions}
        keyExtractor={(item, index) => `${item.section}-${index}`}
        renderItem={({ item }) => (
          <TouchableOpacity onPress={() => handleSuggestionPress(item)}>
            <Text>{item.value || item.data.title}</Text>
            <Text style={{ fontSize: 12, color: '#666' }}>{item.section}</Text>
          </TouchableOpacity>
        )}
      />
    </View>
  );
};

export default AutocompleteSearch;

Limitations

Some features may have limited functionality in React Native:
  • Custom Events: The client dispatches custom events on window, but these may not work as expected in React Native. Consider using callbacks or state management instead.
  • EventSource/SSE: The Agent module uses EventSource for Server-Sent Events, which may require additional polyfills or may not work at all in React Native.
  • Browser Headers: Some HTTP headers normally populated by browsers (User-Agent, Referer) may not be available or may need manual configuration.

Best Practices

1

Initialize Once

Create a single instance of the Constructor.io client and reuse it throughout your app:
// services/constructorio.js
import './polyfills';
import ConstructorioClient from '@constructor-io/constructorio-client-javascript';

export const constructorio = new ConstructorioClient({
  apiKey: 'YOUR_API_KEY',
});
2

Handle Errors

Always implement error handling for network requests:
try {
  const response = await constructorio.search.getSearchResults(query);
  handleResults(response);
} catch (error) {
  console.error('Search failed:', error);
  showErrorMessage('Search failed. Please try again.');
}
3

Implement Debouncing

Use debouncing for autocomplete and search-as-you-type features to reduce API calls.
4

Track User Actions

Always track user interactions using the tracker module to improve recommendation quality.

Additional Resources

Build docs developers (and LLMs) love