Feedback widget (@usero/sdk)
A drop-in feedback button for the web. Vanilla JS, React component, or a <script> tag. The widget renders a floating button;
users click it to leave a 1 to 4 rating, an optional comment, and optional screenshots. Everything lands in your dashboard
automatically.
The package is open source (github.com/usero-feedback/usero) and small: the vanilla build never imports React, so Vue, Svelte, Angular, plain HTML, and Electron apps pay zero React tax.
Install
npm install @usero/sdkReact
Mount it once in your root layout (it renders a fixed-position floating button, so do not put it inside individual pages):
import { UseroFeedbackWidget } from '@usero/sdk/react'
export function App() {
return (
<>
{/* your app */}
<UseroFeedbackWidget clientId='YOUR_CLIENT_ID' environment={process.env.NODE_ENV} />
</>
)
}Vanilla JS (Vue, Svelte, Angular, Astro, Electron, anything)
Call it once at app startup:
import { initUseroFeedbackWidget } from '@usero/sdk'
const widget = initUseroFeedbackWidget({
clientId: 'YOUR_CLIENT_ID',
environment: import.meta.env.MODE,
})
// later, if you need to remove it:
widget.destroy()Script tag (CDN, no bundler)
Add these two tags just before </body>:
<script src="https://unpkg.com/@usero/sdk"></script>
<script>
Usero.initUseroFeedbackWidget({ clientId: 'YOUR_CLIENT_ID' })
</script>unpkg and jsDelivr both serve the IIFE bundle automatically.
Warning
Omit the environment option for your default environment. Do not pass a placeholder like "no-env" or
"default". The dashboard treats an absent environment as the default; a placeholder string creates a separate environment and
your feedback will not appear in the default inbox.
Identify your users (optional)
The widget works fully anonymous. If you have a logged-in user, identifying them lights up "who is this person" on session replays and lets you filter feedback by email in the dashboard.
React: pass the user prop (omit or pass null for logged-out visitors). The widget re-identifies automatically when the prop
changes:
<UseroFeedbackWidget
clientId='YOUR_CLIENT_ID'
user={{ id: currentUser.id, email: currentUser.email, displayName: currentUser.name }}
/>Vanilla: pass a getUser callback, called at session start:
initUseroFeedbackWidget({
clientId: 'YOUR_CLIENT_ID',
getUser: () => (currentUser ? { id: currentUser.id, email: currentUser.email } : null),
})Options
| Option | Type | Default | Description |
|---|---|---|---|
clientId |
string |
required | Your Usero client id. See Find your clientId. |
position |
'left' | 'right' |
'right' |
Which side of the viewport the button sits on. |
theme |
Partial<WidgetTheme> |
auto | Override colors. By default the widget follows the OS color scheme (prefers-color-scheme), with dark as the fallback. Explicit values win; partial overrides merge on top of the detected base. |
title |
string |
'Share Feedback' |
Panel header. |
placeholder |
string |
'Tell us what you think... (optional)' |
Comment placeholder. |
showEmailOption |
boolean |
true |
Show the "share my email" checkbox. |
showScreenshotOption |
boolean |
true |
Show the screenshot upload button (up to 3 images, 10MB each). |
environment |
string |
undefined | Tag feedback with an environment. Omit for your default environment, see the warning above. |
metadata |
Record<string, unknown> |
undefined | Arbitrary metadata attached to every submission. |
baseUrl |
string |
'https://usero.io' |
Override the API host (self-hosted Usero). |
plugins |
UseroPlugin[] |
undefined | Opt-in plugins, for example session replay. |
getUser |
() => User | null |
undefined | Vanilla only. Returns the current logged-in user (or null for anonymous). React uses the user prop instead. |
onSubmit |
(data) => void |
undefined | Fires after a successful submission. |
onError |
(err: Error) => void |
undefined | Fires on init or submission error. |
onOpen / onClose |
() => void |
undefined | Fire when the panel opens or closes. |
Content Security Policy
If your app has a strict CSP, allow https://usero.io in connect-src (and https://unpkg.com in script-src if you use the
script tag). The widget makes no other cross-origin requests.
Next
- Session replay plugin: attach a recording of the last 30 seconds to each feedback
- POST /api/feedback: the API the widget submits to, if you would rather build your own UI