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.
Add the script tag
Paste this before the closing </body> tag, on every page where you want feedback (most
teams add it everywhere):
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. |
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
Next.js
React (Vite/CRA)
Plain HTML
Google Tag Manager
React Native
iOS (Swift)
Use the Script component in your root layout so the widget loads on every route: Add the tag to index.html. The widget mounts itself outside your React tree, so you
don’t render it as a component: Add a Custom HTML tag that fires on the pages you want: 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: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):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: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: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. 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: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:UIKit apps — attach to your window instead: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.
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.
Next.js
React (Vite/CRA)
Plain HTML
React Native
iOS (Swift)
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.
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.