getting started
remediate is a feedback widget for react. one component on the client, one helper on the server. screenshots, screen recordings, voice notes, element pins. straight to your endpoint as a structured payload.
no accounts, no saas, no storage.
install
npm install remediateor set up with an agent
install the skill once, then run /remediate in any react project.
npx skills add fvckprth/remediatethen run /remediate. it'll detect your framework, pick a backend, install the package, scaffold the server route, and wire the component into your layout.
works for any agent that reads agent skills — claude code, codex, cursor, opencode, and the rest. the installer auto-detects which agents you have and drops the skill in the right place. add -g for a global install across all your projects.
after setup, the skill also handles the full lifecycle — just ask your agent:
- add integration — "add slack" or "also send to discord" adds or replaces a backend, with fan-out support for multiple destinations in one route
- wire auth — "pass user info" detects clerk, nextauth, or supabase and wires
metadata+headersprops - test endpoint — "test the feedback route" sends a synthetic submission via curl to verify the route works
- upgrade — "upgrade remediate" checks npm for the latest version and handles breaking changes
- scaffold dashboard — "build an admin page" generates a page to view submitted feedback, matched to your backend
- capture gating — "only annotations and text notes" configures
captureTypes, with conditional support for role-based or environment-based gating - remove — "remove remediate" cleanly uninstalls the widget, route, and package while preserving captured data
add the server route
the widget POSTs FormData. parse it with parseFeedback.
// app/api/feedback/route.ts (next.js app router)
import { parseFeedback } from "remediate/server";
export async function POST(req: Request) {
const { submission, files } = await parseFeedback(req);
// submission.items is the actual content — not submission.body
const body = submission.items
.map((item) => {
if (item.type === "textNote") return item.text;
if (item.type === "annotation") return item.note;
return item.additionalText;
})
.filter(Boolean)
.join("\n");
// files is a Map<string, ParsedFile>, not an array
for (const [, file] of files) {
// file.blob — raw Blob for your storage layer
// file.type — mime type, e.g. "image/png"
// file.category — "screenshot" | "recording" | "voice"
console.log(file.filename, file.category, file.blob.size);
}
return Response.json({ ok: true, id: submission.id });
}submission is the structured json. the content lives in submission.items — a mixed array of text notes, annotations, photos, videos, and voice notes. files is a Map<string, ParsedFile> keyed by field name (e.g. screenshot-itm_abc123). see payload for the full shape.
works anywhere you have a web Request: app router, remix, hono, bun, deno, cloudflare workers.
add the component
import { Remediate } from "remediate";
export default function App() {
return (
<>
<YourApp />
<Remediate endpoint="/api/feedback" />
</>
);
}next.js app router:
Remediateis a client component. if you add it to a server component (likelayout.tsx), wrap it in a file with"use client"at the top, or place it in a client component that already has one.
a floating button appears in the corner. click it, capture, submit. styles inject themselves.
passing user context
use metadata to attach user identity, session info, or anything else to every submission. it arrives verbatim as submission.metadata on the server.
<Remediate
endpoint="/api/feedback"
metadata={{ userId: user.id, email: user.email }}
/>// on the server
const { submission } = await parseFeedback(req);
console.log(submission.metadata.userId); // whatever you passedtry it without a backend
skip the route. log to the console.
<Remediate onSubmit={(payload) => console.log(payload)} />open devtools, capture something, watch the payload. when you're ready, add endpoint. both can coexist. the POST runs first, then onSubmit on success.
props
all props are optional.
what to do next
- recipes: slack, github issues, vercel blob, postgres
- payload: what's in the json
- faq: common questions
requirements
- react 18+
- https for video and voice. localhost is fine in dev.
- video recording is desktop-only. mobile safari has no screen-capture api.
- cross-origin images and iframes render blank in screenshots.
