> ## Documentation Index
> Fetch the complete documentation index at: https://sleekplan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Install Sleekplan with React

> Load the Sleekplan feedback widget in a React app by injecting the install snippet from a useEffect hook, with notes for class components and Next.js.

The Sleekplan widget is a plain script snippet, so it works in any React app. The only thing to get right is *when* it loads: run it once, after your app has mounted, so the script attaches to a page that already exists.

<Info>
  First grab your snippet from [**Settings → Widget**](https://app.sleekplan.com/settings/widget) and copy your `SLEEK_PRODUCT_ID`. See [Install the Sleekplan widget](/help/getting-started/install-widget) for the basics.
</Info>

## Function components (hooks)

Add the widget from a `useEffect` hook in your top-level component (for example `App`). The empty dependency array `[]` makes it run once, right after the first render.

```jsx theme={"system"}
import { useEffect } from "react";

function App() {
  useEffect(() => {
    window.$sleek = [];
    window.SLEEK_PRODUCT_ID = YOUR_PRODUCT_ID;

    const s = document.createElement("script");
    s.src = "https://client.sleekplan.com/sdk/e.js";
    s.async = true;
    document.getElementsByTagName("head")[0].appendChild(s);
  }, []);

  return (
    // your app
  );
}

export default App;
```

Replace `YOUR_PRODUCT_ID` with the numeric ID from your snippet (leave it unquoted, it is a number).

<Tip>
  If your app hot-reloads during development, you may briefly see the launcher appear twice. That is a dev-only side effect of the module reloading, it does not happen in a production build.
</Tip>

## Class components

If you still use class components, put the same code in `componentDidMount()` so it runs after the component is on the page:

```jsx theme={"system"}
class App extends React.Component {
  componentDidMount() {
    window.$sleek = [];
    window.SLEEK_PRODUCT_ID = YOUR_PRODUCT_ID;
    (function () {
      const d = document;
      const s = d.createElement("script");
      s.src = "https://client.sleekplan.com/sdk/e.js";
      s.async = 1;
      d.getElementsByTagName("head")[0].appendChild(s);
    })();
  }

  render() {
    return (
      // your app
    );
  }
}

export default App;
```

## Next.js

In Next.js, use the built-in `Script` component in your root layout so the snippet loads once across route changes. Give it the `afterInteractive` strategy and set your product ID before the SDK loads.

```jsx theme={"system"}
import Script from "next/script";

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Script id="sleek-config" strategy="beforeInteractive">
          {`window.$sleek=[];window.SLEEK_PRODUCT_ID=YOUR_PRODUCT_ID;`}
        </Script>
        <Script
          src="https://client.sleekplan.com/sdk/e.js"
          strategy="afterInteractive"
        />
      </body>
    </html>
  );
}
```

Because the widget is a client-side script, it only runs in the browser, so there is nothing to render on the server.

## Identify your users

Once the widget loads you can attach your signed-in user with the SDK, for example after login:

```js theme={"system"}
$sleek.setUser({
  mail: user.email,
  id: user.id,
  name: user.name,
});
```

For confirmed accounts or private boards, use [Single Sign-On](https://sleekplan.com/docs/authentication/single-sign-on) instead. The full API is in the [Developer Docs](https://sleekplan.com/docs/sdk/user-setup).

## Next steps

<CardGroup cols={2}>
  <Card title="Back to install overview" icon="arrow-left" href="/help/getting-started/install-widget">
    The snippet, user identification, and other platforms.
  </Card>

  <Card title="Content Security Policy" icon="shield-halved" href="/help/getting-started/install-widget/content-security-policy">
    Allowlist the Sleekplan domains if your app enforces a CSP.
  </Card>
</CardGroup>
