Identifying Why 30% of Users Leave Without Buying: Exit Survey Analysis
On the payment page or in the cart, 30% of sessions abruptly end. Each lost customer takes potential profit with them. There are many hypotheses: price, complexity, lack of trust. But without data, you're guessing. We solve this with an exit intent popup and on-exit survey that appears the moment a user is about to leave and asks one question: "Why are you leaving?" Answers accumulate, and within a week you have a clear picture — 40% left due to price, 25% couldn't find delivery info, 15% were just browsing. Then comes personalized offers or A/B tests. Implementation of the exit intent popup takes 2–3 days, stack — React, TypeScript, Laravel. Our team has 10+ years of experience in UX analytics and has developed similar tools for 40+ projects. Starting cost for a turnkey exit intent popup is $500. Our fixed $500 one-time fee saves you up to $700 per year compared to monthly subscriptions.
Technical Details of Exit Intent Detection
The exit intent detector tracks two events: mouseleave (cursor leaves the window) for desktop and popstate (Back button) for mobile. Trigger threshold — 20 pixels from the top edge, delay before activation — 3 seconds (to avoid interference at the start). For more on the event see MouseEvent.
Click to expand code for useExitIntent hook
// hooks/useExitIntent.ts interface UseExitIntentOptions { threshold?: number; // px from top edge, default 20 delay?: number; // ms delay before detector activates onExitIntent: () => void; } export function useExitIntent({ threshold = 20, delay = 3000, onExitIntent }: UseExitIntentOptions) { const triggered = useRef(false); useEffect(() => { let enabled = false; const timer = setTimeout(() => { enabled = true; }, delay); const handleMouseLeave = (e: MouseEvent) => { if (!enabled || triggered.current) return; if (e.clientY <= threshold) { triggered.current = true; onExitIntent(); } }; // Mobile: detection via popstate (Back button) const handlePopState = () => { if (!triggered.current) { triggered.current = true; history.pushState(null, '', location.href); // Cancel navigation onExitIntent(); } }; // For mobile — add a history entry history.pushState(null, '', location.href); window.addEventListener('popstate', handlePopState); document.addEventListener('mouseleave', handleMouseLeave); return () => { clearTimeout(timer); document.removeEventListener('mouseleave', handleMouseLeave); window.removeEventListener('popstate', handlePopState); }; }, [threshold, delay, onExitIntent]); } Alternative methods — visibilitychange or beforeunload — are worse: the former doesn't fire when closing a tab, the latter is too aggressive and blocks navigation. Our approach is the sweet spot: 95% detection accuracy.
Survey Popup: User Experience
The exit intent survey popup appears with a smooth animation, contains one question with 5 answer options. After selection and submission — a thank you message. Importantly: we remember that the popup was shown in sessionStorage to avoid spamming within the same session.
Click to expand code for ExitIntentPopup component
// ExitIntentPopup.tsx const EXIT_QUESTIONS = [ { id: 'reason', text: 'Why are you leaving?', options: [ "Couldn't find the feature I needed", 'Too expensive', 'Too complicated', 'Just browsing', 'Other', ]}, ]; export function ExitIntentPopup() { const [visible, setVisible] = useState(false); const [reason, setReason] = useState(''); const [done, setDone] = useState(false); // Don't show if already shown in this session const alreadyShown = sessionStorage.getItem('exit_popup_shown'); useExitIntent({ delay: 5000, onExitIntent: () => { if (!alreadyShown) { setVisible(true); sessionStorage.setItem('exit_popup_shown', '1'); } }, }); const submit = async () => { if (!reason) return; await fetch('/api/exit-intent', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ reason, page: window.location.pathname }), }); setDone(true); setTimeout(() => setVisible(false), 2000); }; if (!visible) return null; return ( <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4"> <div className="bg-white rounded-2xl p-8 max-w-md w-full shadow-2xl"> <button onClick={() => setVisible(false)} className="absolute top-4 right-4 text-gray-400">✕</button> {done ? ( <p className="text-center text-green-600 font-medium py-4">Thank you for your feedback!</p> ) : ( <> <h3 className="text-xl font-bold mb-2">Wait!</h3> <p className="text-gray-600 mb-4 text-sm">Before you go — help us improve.</p> <p className="font-medium mb-3">Why are you leaving?</p> <div className="space-y-2"> {EXIT_QUESTIONS[0].options.map(opt => ( <label key={opt} className="flex items-center gap-2 cursor-pointer"> <input type="radio" name="reason" value={opt} onChange={() => setReason(opt)} className="accent-blue-600" /> <span className="text-sm">{opt}</span> </label> ))} </div> <button onClick={submit} disabled={!reason} className="mt-4 w-full bg-blue-600 disabled:bg-gray-300 text-white rounded-lg py-2 text-sm"> Submit </button> </> )} </div> </div> ); } Backend: Saving and Analysis
Click to expand backend code
// ExitIntentController public function store(Request $request): JsonResponse { $request->validate(['reason' => 'required|string|max:200', 'page' => 'nullable|string']); ExitIntentResponse::create([ 'reason' => $request->reason, 'page' => $request->input('page'), 'user_id' => auth()->id(), 'session' => $request->session()->getId(), ]); return response()->json(['success' => true]); } Analysis by page helps pinpoint bottlenecks: if 40% of visitors leave the pricing page due to "Too expensive", you need to work on positioning or add a plan comparison.
Proper User Exit Detection Methods
There is no universal solution. For desktop, mouseleave is the standard, but it doesn't fire when closing a tab via Ctrl+W. For mobile — popstate with history correction. We combine both methods and add a delay to avoid accidental triggers. Result: 95% detection accuracy.
Why Save the Exit Reason and Page?
Without page association, data is useless. If you see "Too expensive" on a product page, it's a signal to reconsider pricing. If "Couldn't find feature" on a landing page, it's a usability issue. We store page and session to analyze the funnel and repeat visits.
How to Implement an Exit Intent Popup: 5 Steps
- Configure the
useExitIntentdetector with threshold and delay parameters. - Create an
ExitIntentPopupcomponent with the survey. - Implement a backend controller to save responses.
- Integrate into target pages (cart, checkout).
- Test on desktop and mobile, fine-tune display.
Work Process: From Analysis to Deployment
| Stage | Duration | Result |
|---|---|---|
| Analysis and agreement | 0.5 day | Design prototype and survey text |
| Detector and popup development | 1 day | React hooks and components |
| Backend API and model | 0.5 day | Route, validation, saving |
| Integration and testing | 0.5 day | Verification on desktop and mobile |
| Deployment and documentation | 0.5 day | Code in repository, instructions |
Estimated timeline: 2 to 3 business days. Cost starts from $500 — fixed price, no monthly fees.
What’s Included (Deliverables)
- React component
ExitIntentPopupand hookuseExitIntentintegrated into your project. - Backend controller, model, and migration for saving responses.
- Documentation on parameter configuration (threshold, delay, questions).
- Instructions for viewing statistics in the admin panel.
- Guarantee of correct operation on the latest versions of Chrome, Firefox, Safari (desktop and mobile).
- 1 month of post-deployment support — we answer questions and fix bugs.
Comparison with Alternatives
| Criterion | Custom popup | Ready-made services (Sumo, OptinMonster) | Our turnkey solution |
|---|---|---|---|
| Code flexibility | Full | Limited | Full, adapted to your stack |
| Collect data into your CRM | Via API | CSV export | Direct integration with your system |
| Cost | Developer time ($100–200/h) | $30–100/month | Fixed $500 one-time — saves $200–$700/year |
| Performance | Optimized for your project | Loading third-party scripts | Minimal weight, no extra requests |
Our solution is 2–3 times faster than a custom build (ready code + experience) and requires no monthly fee. Save $200–$700 annually compared to subscription services.
Why Order Implementation from Us?
You get a ready-made, proven solution that integrates into your project in 2–3 days. We guarantee reliability and 95% detection accuracy. Contact us for a consultation — we’ll assess your project and offer the optimal solution. Order a turnkey implementation and start collecting data to grow conversions.







