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.
Here is the package's GitHub repository. Updates are in the v2 branch.
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 administrationlanguage
- 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. Note that Product Fruits can only be initalized with one language at a time. If language switching is needed, the Product Fruits instance should be destroyed withwindow.productFruits.services.destroy
and then again initalized with the new language code.user
- a user information object with at least theusername
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 optionsdebug
- if set to true, you'll recieve debug info in your browser consoledontDestroy
- (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 toneverUnmount
(default value) orunmount
.
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, stringfirstname
- optional, stringlastname
- optional, stringsignUpAt
- 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, we recommend to use our useProductFruitsApi
hook.
useProductFruitsApi(api => {
if (your_dep == "something") {
api.tours.tryStartTour(tourId);
}
}, [your_dep]);
We also recommend using try/catch error handling or adding an error boundary around the Product Fruits component to avoid errors if the Product Fruits API is unavailable.
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
Here is a simple example of a React application using Product Fruits. The example uses React Context and React Router, so it simulates typical use cases.
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>
);
}
}