---
title: "Installation to React or NextJS Applications"
url: "https://help.productfruits.com/en/article/developers-integration-react-nextjs"
language: "en"
last_updated: "2026-10-21T21:06:28.206Z"
---

# Installation to React or NextJS Applications

For React and NextJS applications, it's best to 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.](https://github.com/product-fruits/react-product-fruits)  Updates are in the [v2 branch.](https://github.com/product-fruits/react-product-fruits/tree/v2)

## 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](https://help.productfruits.com/en/article/developers-integration-react-nextjs#don't-destroy-product-fruits-on-unmount-(for-%3C2.2.0-version)).

**NextJS**

Because of NextJS architecture, the safest way is to use the `dontDestroy` prop. The `<ProductFruits />` component then won't kill Product Fruits when the component is unmounted because of multiple re-renders. [Read more](https://help.productfruits.com/en/article/developers-integration-react-nextjs#don't-destroy-product-fruits-on-unmount-(for-%3C2.2.0-version)).

### 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](https://my.productfruits.com/portal/workspace/[default]/settings/installation) 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. 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 with `window.productFruits.services.destroy` and then again initalized with the new language code.
-   `user` - a [user information object](https://help.productfruits.com/en/article/identifying-users) 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](https://help.productfruits.com/en/article/installation-via-npm-package#configuration-object) 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](https://help.productfruits.com/en/article/developers-integration-react-nextjs#don't-destroy-product-fruits-on-unmount).
-   `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*](https://en.wikipedia.org/wiki/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](https://help.productfruits.com/en/article/dynamic-css-classes-or-attributes).

## 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>
    );
  }
}
```

## Troubleshooting: WebSocket is still in CONNECTING state" Errors

This error occurs when something attempts to send data before a WebSocket connection has finished opening. It does not identify the Product Fruits operation involved or confirm that React component teardown caused the problem.

### What to Check

1\. Confirm the installed `react-product-fruits` version.

2\. Verify that `<ProductFruits />` is mounted once at a stable, high level of the application tree.

3\. For Next.js, confirm that the component is dynamically imported with `{ ssr: false }.`

4\. Check the lifecycle configuration:

-   Version 2.2.0 or later: omit `lifeCycle` or use `lifeCycle="neverUnmount"`
-   `lifeCycle="unmount"` destroys Product Fruits whenever the component unmounts.
-   Version 2.2.0 or later: don't pass `dontDestroy`at all. If the prop is present, even as `false`, the component logs an error and doesn't initialize Product Fruits. Remove it when you upgrade.
-   Versions earlier than 2.2.0: Product Fruits is destroyed on unmount by default. Set `dontDestroy={true}` to keep it running.

5\. Check whether authentication, routing, or conditional rendering repeatedly remounts the component.

6\. Ensure that cleanup callbacks do not call Product Fruits APIs and that normal API calls use `useProductFruitsApi` or otherwise wait until Product Fruits is ready.  

### Diagnostic Test

Temporarily remove Product Fruits API calls from cleanup callbacks and use the persistent lifecycle behavior. Then reproduce the original sequence.  
  
If Product Fruits must be removed on logout, use the documented method:

```
window.productFruits.services.destroy();
```

Also check the browser's Network and Console panels for a failed Product Fruits WebSocket connection or a blocked Content Security Policy request.

### If the Error Continues

Send Product Fruits Support the complete error and stack trace, installed package version, initialization component, lifecycle configuration, failing WebSocket request, and exact reproduction steps.
