One script. Any prototype.
Install Pinmark on your prototype
The setup is always the same. Choose your platform to see exactly where the snippet belongs.
- 01Create a project. Open the dashboard and copy your snippet.
<script async src="https://pinmark-gamma.vercel.app/w.js" data-pinmark="pk_live_YOUR_KEY"></script> - 02Allow the domain. Add your prototype's domain (for example
myproto.vercel.app) to Allowed domains. Includelocalhostfor local testing. - 03Paste and publish. Add the snippet to the
<head>. The comment bubble appears bottom-right; press C or drag to comment on an area.
Framework + hosting
Next.js / Vercel
Using an AI code assistant? Give it the project-aware prompt below:
Install Pinmark in this project so it loads on every page.
1. Detect how this app manages its document <head>.
2. For Next.js App Router, add the script in app/layout.tsx. For Pages Router, add it in pages/_document.tsx. For another framework, use its shared document or root layout.
3. Add the script exactly once and keep async and data-pinmark unchanged.
4. Do not change unrelated code.
Use this script:
<script async src="https://pinmark-gamma.vercel.app/w.js" data-pinmark="pk_live_YOUR_KEY"></script>
Afterward, tell me which file you changed.App Router: in app/layout.tsx, add the script inside <head>:
<head>
<script async src="https://pinmark-gamma.vercel.app/w.js" data-pinmark="pk_live_YOUR_KEY"></script>
</head>Pages Router: add it inside <Head> in pages/_document.tsx.
Allowed domain: your-app.vercel.app plus any custom domain. Preview deployments get a unique subdomain per deploy; add those individually if you review previews.
AI app builder
Lovable
Fastest path is asking the AI:
Add this script tag to the <head> of index.html and change nothing else:
<script async src="https://pinmark-gamma.vercel.app/w.js" data-pinmark="pk_live_YOUR_KEY"></script>Or in Dev Mode, edit index.html yourself and paste before </head>. Then publish.
Allowed domain: your-app.lovable.app, or *.lovable.app to cover renames and previews.
Site builder
Webflow
paid site plan required- Site settings → Custom Code
- Paste the snippet into Head Code
- Save, then publish the site
Custom code only runs on sites with a paid plan; free *.webflow.io staging sites won't inject it. For a single page, use Page Settings → Custom Code instead. Allowed domain: your-site.webflow.io or your custom domain.
Site builder
Framer
paid site plan required- Site Settings → General → Custom Code
- Paste into Start of <head> tag
- Publish
Allowed domain: your-site.framer.app, *.framer.website, or your custom domain.
Cloud IDE
Replit
- Edit your app's
index.html(project root, orclient/in full-stack templates) - Paste before
</head> - Redeploy from the Deployments tab
Allowed domain: your-app.replit.app. Replit's AI agent can also add the tag for you.
AI app builders
Bolt / v0
Same prompt pattern as Lovable:
Add this script tag to the document <head> and change nothing else:
<script async src="https://pinmark-gamma.vercel.app/w.js" data-pinmark="pk_live_YOUR_KEY"></script>For v0's Next.js output it belongs in app/layout.tsx. Allowed domain: whatever your deployment's address bar shows.
Hosting
Netlify
Paste into your site's HTML like any static site, or inject without touching code: Site configuration → Build & deploy → Post processing → Snippet injection → "Insert before </head>".
Allowed domain: your-site.netlify.app or your custom domain.
Universal install
Plain HTML
For a custom-coded site, this prompt finds the shared document and installs Pinmark once:
Install Pinmark on every page of this site.
Add the script below exactly once to the shared document <head>, immediately before </head>. If the site uses templates or layouts, choose the root layout that renders every page. Keep async and data-pinmark unchanged, and do not change unrelated code.
<script async src="https://pinmark-gamma.vercel.app/w.js" data-pinmark="pk_live_YOUR_KEY"></script>
Afterward, tell me which file you changed.Paste anywhere in the page, before </head> by convention. It loads async and never blocks or breaks the host page.
<script async src="https://pinmark-gamma.vercel.app/w.js" data-pinmark="pk_live_YOUR_KEY"></script>Works on GitHub Pages, Cloudflare Pages, Render, S3, your own server, any framework. The only requirements: the page's domain is on your allowlist and can reach https://pinmark-gamma.vercel.app.
Site builders
Wix / Squarespace
paid plans requiredWix: Settings → Custom Code → Add Custom Code → apply to All Pages, load in Head.
Squarespace: Settings → Advanced → Code Injection → Header field.
Wix needs Premium; Squarespace needs Business or higher.
Own every layer
Self-hosting
Pinmark is open source under the MIT license.
Running your own instance means bringing your own Supabase project (cloud free tier, or self-hosted Supabase) and deploying apps/web anywhere Node runs. Full steps for environment variables, auth redirect URLs, and a smoke test are in DEPLOY.md.
Quick diagnostics
Troubleshooting
No bubble? Open the browser console and find the [pinmark] line; it names the exact problem:
- domain not in allowed domains: add the page's domain in the dashboard and reload.
- key doesn't match any project: re-copy the snippet; the key may have been regenerated.
- No
[pinmark]line at all: the script isn't on the page. View source and search forw.js; if missing, the platform didn't inject it (check paid-plan requirements above, and that you published after adding it). - Project in review link only mode: the widget is deliberately invisible without the secret link. Open your review link once in that browser, or switch the project to Open.
Pins shift after resizing? Pins dropped on empty background have nothing to attach to and fall back to page-relative position (shown at 60% opacity). Pin on concrete elements, buttons, cards, headings, for positions that survive any viewport.
Stale behavior after changing settings? w.js is cached for five minutes; hard reload (Cmd+Shift+R) picks changes up immediately.