@tonytangdev/pin-point
v0.3.0
Published
Visual feedback overlay for React — drop pins and leave comments on any page
Maintainers
Readme
pin-point
Visual feedback overlay for React. Drop pins on any page, leave comments.
Install
npm install pin-pointUsage
import { FeedbackOverlay } from 'pin-point';
import 'pin-point/styles.css';
function App() {
return (
<FeedbackOverlay
onCommentCreate={async (comment, authHeaders) => {
// Send to your backend with auth headers
}}
onCommentsFetch={async (authHeaders) => {
// Fetch from your backend
return [];
}}
>
<YourApp />
</FeedbackOverlay>
);
}The toolbar is always visible when FeedbackOverlay is mounted. Anonymous users can view existing comments but cannot create new ones. To leave feedback, a user needs either:
- A feedback link — a URL containing
?pin-token=<id>, generated by an admin and shared with reviewers. - The admin key — paste the admin secret via the toolbar's key icon. It's stored in
localStorage.
Admins generate shareable feedback links from the toolbar's Share button.
With pin-point-server
If you don't want to build your own backend, use pin-point-server:
npx pin-point-serverconst API = 'http://localhost:3000';
<FeedbackOverlay
onCommentCreate={async (comment, authHeaders) => {
await fetch(`${API}/comments`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', ...authHeaders },
body: JSON.stringify(comment),
});
}}
onCommentsFetch={async (authHeaders) => {
const res = await fetch(
`${API}/comments?url=${location.pathname}`,
{ headers: authHeaders },
);
return res.json();
}}
onAdminValidate={async (secret) => {
const res = await fetch(`${API}/admin/tokens`, {
headers: { 'X-Pin-Admin': secret },
});
return res.ok;
}}
onShareLinkCreate={async (label, expiresInHours, authHeaders) => {
const res = await fetch(`${API}/admin/tokens`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', ...authHeaders },
body: JSON.stringify({ label, expiresInHours }),
});
const token = await res.json();
return { tokenId: token.id };
}}
>
<YourApp />
</FeedbackOverlay>Props
All callbacks receive an authHeaders object containing the relevant auth header (X-Pin-Token or X-Pin-Admin) based on the current user's auth state. Spread it into your fetch headers.
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| onCommentCreate | (comment: PinComment, authHeaders: Record<string, string>) => Promise<void> | Yes | Called when user submits a comment |
| onCommentsFetch | (authHeaders: Record<string, string>) => Promise<PinComment[]> | Yes | Called on mount to load existing comments |
| onCommentDelete | (id: string, authHeaders: Record<string, string>) => Promise<void> | No | Called when an admin deletes a comment |
| onCommentUpdate | (id: string, content: string, authHeaders: Record<string, string>) => Promise<PinComment> | No | Called when an admin edits a comment |
| onAdminValidate | (secret: string) => Promise<boolean> | No | Validates an admin key entered via the toolbar |
| onShareLinkCreate | (label: string, expiresInHours: number \| null, authHeaders: Record<string, string>) => Promise<{ tokenId: string }> | No | Mints a feedback-link token (admin-only) |
PinComment
type PinComment = {
id: string;
url: string;
content: string;
anchor: {
selector: string;
xPercent: number;
yPercent: number;
};
viewport: { width: number };
createdAt: string;
};License
MIT
