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

# Installation

> Embed the Produck widget with a single script tag.

The widget is one hosted script. Drop it into your app and it loads, renders the duck
button, and handles the rest — nothing to bundle, install from npm, or add to a build step.
Building a mobile app? See the **React Native** and **iOS (Swift)** tabs under
[Framework snippets](#framework-snippets).

## Add the script tag

Paste this before the closing `</body>` tag, on every page where you want feedback (most
teams add it everywhere):

```html theme={null}
<script src="https://tryproduck.com/sdk/v1.js" data-project="pk_live_YOUR_KEY" defer></script>
```

The widget initializes itself as soon as it loads — no startup call needed.

| Attribute           | Required    | Description                                                                                                                    |
| ------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `src`               | yes         | The hosted widget: `https://tryproduck.com/sdk/v1.js`. Load it from this URL so you always get updates.                        |
| `data-project`      | yes         | Your project's API key from the dashboard. Starts with `pk_live_`.                                                             |
| `data-launcher`     | optional    | `droplet` (default) or `none` to suppress the built-in button. See [Bring your own UI](/sdk/javascript-api#bring-your-own-ui). |
| `data-confirmation` | optional    | `toast` (default) or `none` to suppress the built-in confirmation.                                                             |
| `data-draggable`    | optional    | `on` to let users drag the launcher anywhere on screen. Off by default.                                                        |
| `defer`             | recommended | Loads the widget without blocking your page render.                                                                            |

## Framework snippets

<Tabs>
  <Tab title="Next.js">
    Use the `Script` component in your root layout so the widget loads on every route:

    ```tsx app/layout.tsx theme={null}
    import Script from "next/script";

    export default function RootLayout({ children }: { children: React.ReactNode }) {
      return (
        <html lang="en">
          <body>
            {children}
            <Script
              src="https://tryproduck.com/sdk/v1.js"
              data-project="pk_live_YOUR_KEY"
              strategy="afterInteractive"
            />
          </body>
        </html>
      );
    }
    ```
  </Tab>

  <Tab title="React (Vite/CRA)">
    Add the tag to `index.html`. The widget mounts itself outside your React tree, so you
    don't render it as a component:

    ```html index.html theme={null}
    <body>
      <div id="root"></div>
      <script type="module" src="/src/main.tsx"></script>
      <script src="https://tryproduck.com/sdk/v1.js" data-project="pk_live_YOUR_KEY" defer></script>
    </body>
    ```
  </Tab>

  <Tab title="Plain HTML">
    ```html theme={null}
    <script src="https://tryproduck.com/sdk/v1.js" data-project="pk_live_YOUR_KEY" defer></script>
    ```
  </Tab>

  <Tab title="Google Tag Manager">
    Add a **Custom HTML** tag that fires on the pages you want:

    ```html theme={null}
    <script src="https://tryproduck.com/sdk/v1.js" data-project="pk_live_YOUR_KEY" defer></script>
    ```
  </Tab>

  <Tab title="React Native">
    Mobile uses the `@produck/react-native` package instead of the script tag: users capture
    the current screen, annotate it, and submit — with optional frame replay and
    component-level targeting.

    **Install.** One command for every React Native app — bare, Expo, dev client, or Expo Go:

    ```sh theme={null}
    npm i @produck/react-native react-native-svg react-native-safe-area-context react-native-view-shot lucide-react-native
    ```

    Bare React Native: `cd ios && pod install`. Expo: restart the dev server (or use
    `npx expo install` for the three native modules if you prefer SDK-pinned versions).

    **Wrap your app** with the provider (inside `SafeAreaProvider`):

    ```tsx App.tsx theme={null}
    import { SafeAreaProvider } from "react-native-safe-area-context";
    import { ProduckProvider } from "@produck/react-native";

    export default function Root() {
      return (
        <SafeAreaProvider>
          <ProduckProvider
            config={{
              projectKey: "pk_live_YOUR_KEY",
              appId: "com.yourco.app",
            }}
          >
            <App />
          </ProduckProvider>
        </SafeAreaProvider>
      );
    }
    ```

    A floating launcher opens the capture flow; `useProduckFeedback().open()` triggers it
    from your own UI. `appId` is your bundle id — it groups feedback the way a domain does
    on web. A rolling 30-second frame replay is attached to every submission by default
    (`replay: false` opts out). Other optional config: `screen`, `user {id, email, name}`,
    `launcher`, `onError`, `analyticsOptOut`, `buildId`.

    **Component targeting (recommended).** Add the Babel plugin so annotation drags snap to
    the underlying component:

    ```js babel.config.js theme={null}
    module.exports = {
      presets: ["babel-preset-expo"],
      plugins: ["@produck/react-native/babel"],
    };
    ```

    Wrap custom components manually with `<ProduckTarget sourceId hostType>` where needed.

    **Exact-source binding (optional, CI).** Upload a build manifest per CI build and Produck
    resolves annotated components to their exact source for that build:

    ```sh theme={null}
    produck-rn manifest --root . --build-id "$BUILD_ID" --app-id com.yourco.app \
      --revision "$GIT_SHA" --output .produck/manifest.json
    PRODUCK_PROJECT_KEY=pk_live_YOUR_KEY produck-rn upload-manifest .produck/manifest.json
    ```

    Pass the same `buildId` in the provider config.

    No native permissions are required — screenshots and replay frames render from your
    app's own view hierarchy.
  </Tab>

  <Tab title="iOS (Swift)">
    Native iOS apps use the **ProduckKit** Swift package — the same capture flow as React
    Native: users screenshot the current screen, annotate it with highlight, marker, draw,
    and eraser tools, and submit. Pure Swift, iOS 16+, no CocoaPods, no other dependencies.

    **Install.** In Xcode, open **File → Add Package Dependencies** and paste the ProduckKit
    repository URL:

    ```
    https://github.com/tryproduck/produck-swift
    ```

    The package resolves to a prebuilt, checksum-verified XCFramework, so it adds nothing
    to your compile times, and every release ships its dSYMs so your crash reports keep
    symbolicating.

    **SwiftUI apps** — one view modifier on your root view:

    ```swift MyApp.swift theme={null}
    import ProduckKit

    @main
    struct MyApp: App {
        var body: some Scene {
            WindowGroup {
                ContentView()
                    .produckFeedback(ProduckConfig(
                        projectKey: "pk_live_YOUR_KEY",
                        appId: "com.yourco.app"
                    ))
            }
        }
    }
    ```

    **UIKit apps** — attach to your window instead:

    ```swift SceneDelegate.swift theme={null}
    import ProduckKit

    let handle = Produck.attach(to: window, config: ProduckConfig(
        projectKey: "pk_live_YOUR_KEY",
        appId: "com.yourco.app"
    ))
    // handle.open() triggers the flow from your own UI;
    // handle.noteScreen("Checkout") labels replay segments.
    ```

    A floating launcher opens the capture flow; it lives in its own overlay window, so it
    stays visible across navigation pushes, tab switches, and any sheets or full-screen
    covers your app presents. `appId` is your bundle id — it groups
    feedback the way a domain does on web. A rolling 30-second frame replay is attached to
    every submission by default (`replay: false` opts out); if you enable replay, disclose
    screen capture in your app's privacy policy. Feedback posts to `https://tryproduck.com/api`
    by default; set `apiBase` only to point at a self-hosted or staging backend. Other optional
    config: `screen`, `user: SdkUser(id:email:name:)`, `launcher: false` for programmatic-only,
    `onError`, `analyticsOptOut`.

    No native permissions are required — screenshots and replay frames render from your
    app's own window, on-device, and upload only when the user taps Send.
  </Tab>
</Tabs>

## Set up with an AI agent

Using Cursor, Claude Code, Copilot, or another coding agent? Copy the prompt for your
framework, replace `pk_live_YOUR_KEY` with your project's API key, and paste it into the
agent. Each prompt carries everything the agent needs — file locations, exact code, and
verification steps.

<Tabs>
  <Tab title="Next.js">
    ```text theme={null}
    Set up the Produck feedback widget in this Next.js app.

    My Produck project key: pk_live_YOUR_KEY

    1. The widget is one hosted script — do NOT install anything from npm.
    2. Find the root layout. App Router: app/layout.tsx. Pages Router: pages/_app.tsx.
    3. Add the script so it loads on every route, using next/script:

       import Script from "next/script";
       // inside <body>, after {children}:
       <Script
         src="https://tryproduck.com/sdk/v1.js"
         data-project="pk_live_YOUR_KEY"
         strategy="afterInteractive"
       />

    4. If the app sets a Content-Security-Policy, extend it with
       script-src https://tryproduck.com and connect-src https://api.tryproduck.com.
    5. Verify: start the dev server, load any page, and confirm (a) a floating duck
       button appears in the bottom-right corner, (b) the browser console has no errors
       from tryproduck.com, (c) pressing Cmd+Shift+Z (Ctrl+Shift+Z on Windows/Linux)
       opens the widget. Report what you observed.
    ```
  </Tab>

  <Tab title="React (Vite/CRA)">
    ```text theme={null}
    Set up the Produck feedback widget in this React app.

    My Produck project key: pk_live_YOUR_KEY

    1. The widget is one hosted script — do NOT install anything from npm and do NOT
       render it as a React component; it mounts itself outside the React tree.
    2. In index.html (Vite: project root; CRA: public/index.html), add before </body>:

       <script src="https://tryproduck.com/sdk/v1.js" data-project="pk_live_YOUR_KEY" defer></script>

    3. If the app sets a Content-Security-Policy, extend it with
       script-src https://tryproduck.com and connect-src https://api.tryproduck.com.
    4. Verify: start the dev server, load the app, and confirm (a) a floating duck
       button appears in the bottom-right corner, (b) no console errors from
       tryproduck.com, (c) Cmd+Shift+Z / Ctrl+Shift+Z opens the widget. Report what
       you observed.
    ```
  </Tab>

  <Tab title="Plain HTML">
    ```text theme={null}
    Set up the Produck feedback widget on this website.

    My Produck project key: pk_live_YOUR_KEY

    1. Add this tag before the closing </body> tag of every page (or the shared
       layout/template if one exists):

       <script src="https://tryproduck.com/sdk/v1.js" data-project="pk_live_YOUR_KEY" defer></script>

    2. Verify: open a page in a browser and confirm (a) a floating duck button appears
       in the bottom-right corner, (b) no console errors from tryproduck.com. Report
       what you observed.
    ```
  </Tab>

  <Tab title="React Native">
    ```text theme={null}
    Set up Produck feedback (@produck/react-native) in this React Native app.

    My Produck project key: pk_live_YOUR_KEY

    1. Install:
       npm i @produck/react-native react-native-svg react-native-safe-area-context react-native-view-shot lucide-react-native
       Bare React Native: cd ios && pod install. Expo: restart the dev server.
    2. Read the app's bundle identifier (app.json "ios.bundleIdentifier" /
       "android.package", or the native project) — that is the appId below.
    3. Wrap the root component with the provider, inside SafeAreaProvider:

       import { SafeAreaProvider } from "react-native-safe-area-context";
       import { ProduckProvider } from "@produck/react-native";

       <SafeAreaProvider>
         <ProduckProvider config={{ projectKey: "pk_live_YOUR_KEY", appId: "<bundle id>" }}>
           <App />
         </ProduckProvider>
       </SafeAreaProvider>

    4. Add the Babel plugin for component targeting — in babel.config.js plugins:
       "@produck/react-native/babel"
    5. Notes: a rolling 30s frame replay is attached by default (replay: false opts
       out); no native permissions are needed.
    6. Verify: typecheck passes, the app launches with a floating feedback button in
       the bottom-right corner, and tapping it opens the screenshot + annotation flow.
       Report what you observed.
    ```
  </Tab>

  <Tab title="iOS (Swift)">
    ```text theme={null}
    Set up ProduckKit (Produck's native iOS feedback SDK) in this iOS app.

    My Produck project key: pk_live_YOUR_KEY

    Do all of this on a new git branch (e.g. add-produck-feedback) and open a
    pull request when done — do not commit to the default branch.

    1. Add the Swift package https://github.com/tryproduck/produck-swift
       (from: "0.1.0"). Use whatever this project already uses for dependencies:
       Xcode project → add it to the project's Package Dependencies; Package.swift →
       add to dependencies + target dependencies ("ProduckKit"); XcodeGen/Tuist →
       the manifest. iOS deployment target must be 16.0+.
    2. Read the app's bundle identifier — that is the appId below.
    3. SwiftUI app (has an App struct): add the modifier to the root view:

       import ProduckKit

       ContentView()
           .produckFeedback(ProduckConfig(
               projectKey: "pk_live_YOUR_KEY",
               appId: "<bundle id>"
           ))

       UIKit-only app (no SwiftUI root): in scene/window setup, keep the handle:

       let produck = Produck.attach(to: window, config: ProduckConfig(
           projectKey: "pk_live_YOUR_KEY",
           appId: "<bundle id>"
       ))

    4. Notes: the package resolves to a prebuilt XCFramework (no SDK source appears
       in the project — that is expected), no other dependencies, no CocoaPods, no
       Info.plist or permission changes. A rolling 30s frame replay is attached by
       default (replay: false opts out) — if kept on, remind me to disclose screen
       capture in the privacy policy.
    5. Verify: the app builds, launches with a floating feedback button in the
       bottom-right corner, and tapping it captures the screen and opens the
       annotation toolbar (highlight, marker, draw).
    6. Open the pull request and reply with the PR link plus what you observed
       during verification.
    ```
  </Tab>
</Tabs>

## Opening the widget

Once the script is on the page, your users get two ways in:

* **The duck button** — floating in the bottom-right corner.
* **Keyboard shortcut** — **⌘ + Shift + Z** (Mac) or **Ctrl + Shift + Z** (Windows/Linux).

Want to open it from your own button instead? See the [JavaScript API](/sdk/javascript-api).

<Note>
  The widget renders inside an isolated shadow root, so its styles never leak into your app
  and your CSS never touches the widget. No class-name collisions to worry about.
</Note>
