Installation to React or NextJS applications

For React and NextJS applications, it's best to use use our React package. This package provides a React component that can be easily used. NextJS integration has a slightly different procedure, see below.

Please note, this page is for package version 2.0.0 and higher. Check the legacy version page if you use version 1.

React component setup

1. Install the React NPM package

npm install react-product-fruits --save

2. Find the right place where to put the component

This is very important!

(for version >=2.2.0)

The <ProductFruits /> component is a simple wrapper of our NPM component. It makes the integration easier, but you must take special care of the component life cycle

When the PF React component is rendered for the first time, Product Fruits is loaded as you would expect. When the component is unmounted, Product Fruits is not removed from your page. If the PF React component is then mounted again, the loading phase is skipped and Product Fruits still works.

If you have a single-page application, you have to call window.productFruits.services.destroy() manually when you want to remove Product Fruits from the application. For example, you should call this during the user logout process.

If you want to change this behavior to the standard React life cycle one, use the lifeCycle prop of the component and set it to unmount. Now PF will be automatically removed when the component is unmounted. Be really careful with this. If the component gets multiple mounts/unmounts in a row, Product Fruits can flicker or render some content multiple times. We don't recommend using this unless fully it is tested by you for all possible scenarios.

(for version <2.2.0)

React

The <ProductFruits /> component must be inserted into the top-most place in your application tree that is possible. For example, if you use React Router, do not use it inside of every route. Put it to the <Routes> component directly (not to <Route />) or completely outside of it.

If you are not sure, you can also use the dontDestroy prop. Read more.

NextJS

Because of NextJS architecture, the safest way is to use the dontDestoy prop. The <ProductFruits /> component then won't kill Product Fruits when the component is unmounted because of multiple re-renders. Read more.

3. Import the package

For React applications, use standard import

import { ProductFruits } from 'react-product-fruits';

For NextJS applications, use dynamic import. This is needed because NextJS uses server-side rendering, but Product Fruits is only a frontend component.

const ProductFruits = dynamic(
    () => import('react-product-fruits').then(mod => mod.ProductFruits),
    { ssr: false }
)

4. Use the component

The component requires a few props. You have to provide the ID of your workspace, current language code, and current user information.

  • workspaceCode - code of your workspace, you will find it in the Installation section in the Product Fruits administration
  • language - 2 letter ISO language code, it has to match values you specified before in Product Fruits administration (see Workspace settings -> Supported Languages). The language code must be lowercase.
  • user - a user information object with at least the username property filled. Note that this should be information about the user currently logged into your application - NOT the username of your Product Fruit's account.
  • config - an optional config object, see here for available options
  • debug - if set to true, you'll recieve debug info in your browser console
  • dontDestroy - (for <2.2.0 version) PF won't be destroyed when the component is unmounted. Read more.
  • lifeCycle - (for >=2.2.0 version) can be set to neverUnmount (default value) or unmount.

For React applications, use the component as usual:

<ProductFruits workspaceCode="YOUR_WORKSPACE_CODE" language="LOWER-CASE ISO-2 CODE OF LOCALIZATION LANGUAGE" user={userInfo} />

For NextJS applications, check if the window global variable exists.

{global.window && <ProductFruits workspaceCode="YOUR_WORKSPACE_CODE" language="LOWER-CASE ISO-2 CODE OF LOCALIZATION LANGUAGE" user={userInfo} />

 

Don't destroy Product Fruits on unmount (for <2.2.0 version)

Please note, we recommend updating to 2.2.0 or higher and using the new lifeCycle prop.

React and NextJS applications can have a complicated component tree and it can be difficult to find the right spot for the Product Fruits component. The dontDestroy prop will disable unloading of Product Fruits scripts when the component is unmounted.

This can be used when multiple mountings/unmountings happen during your rendering cycle. Then, when you need to destroy Product Fruits scripts manually (e.g. when the user logs out or switches to a different application language), call window.productFruits.services.destroy().

Routing in NextJS

Product Fruits can randomly fail to detect URL changes in NextJS. You can see multipage tours randomly stopping. The solution is to turn off the default URL detection and notify Product Fruits about the page change. Please, only enable this option if you experience problems. In most cases, it won't be needed.

First, disable the route change detection by passing the disableLocationChangeDetection option to the component:

<ProductFruits workspaceCode="YOUR_WORKSPACE_CODE" language="LOWER-CASE ISO-2 CODE OF LOCALIZATION LANGUAGE" user={userInfo} config={{ disableLocationChangeDetection: true }} />

Then, in your _app.tsx file, outside of the App component, add this snippet:

import Router from 'next/router'

Router.events.on('routeChangeComplete', () => {
    window.productFruits.pageChanged();
});

// Here comes the App component function
//function App({ Component, pageProps }: AppProps) {

User information object

The user prop takes a user information object. You must pass at least the username property that holds the current user's unique identifier (e.g. email, username, user ID). If you don't want to transfer real usernames to Product Fruits, you can hash them (i.e. MD5, SHA1).

Product Fruits can also show different content based on different user attributes. These attributes can hold any primitive value (number, string, array). It is not required to provide any other identification than the user's unique identifier, but often more is needed - typically for segmentation purposes. To help you get started, there are some built-in attributes that you can provide as props on the ProductFruits component:

  • email - optional, string
  • firstname - optional, string
  • lastname - optional, string
  • signUpAt - optional, (can be any format but we recommend the ISO 8601 JSON DateTime format)
  • role - optional, string

You can also include other custom attributes. These have to be provided through props subobject. See the full working example below.

Working with Product Fruits JS API

If you want to use Product Fruits API in your React application, it's best to use our useProductFruitsApi hook.

useProductFruitsApi(api => {
        if (your_dep == "something") {
            api.tours.tryStartTour(tourId);
        }
}, [your_dep]);

Dynamic CSS generation

If your project uses a library for CSS scoping (like Emotion), it probably generates dynamic CSS classes like css-a9fp3q. This will make issues with tours and hints as they use CSS selectors for targeting.

If there are no other stable attributes, consider this.

Full working example

You can check out a simple example of a React application using Product Fruits. The example uses React Context and React Router, so it simulates typical use cases.

See the GitHub repository (please note, this example was not updated to the >=2.0.0 version yet)

Here is a short showcase of working integration:

import React from 'react';
import { ProductFruits } from 'react-product-fruits';

export class App extends React.Component {
  render () {
    const { appUser } = this.props; // you have to provide user information from your store
    
    const userInfo = {
      username: appUser.username,
      email: appUser.email,
      firstname: appUser.firstname,
      lastname: appUser.lastname,
      signUpAt: appUser.signUpAt,
      role: appUser.role,
      props: { customProperty1: 123 }
    };

    return (
      <div>
        <ProductFruits workspaceCode="YOUR_WORKSPACE_CODE" language="LOWER-CASE ISO-2 CODE OF LOCALIZATION LANGUAGE" user={userInfo} />
      </div>
    );
  }
}