The pending ReviewNotice plus onReview / onDismiss actions.
const { notice, onReview, onDismiss } = useReviewPrompt();
// after 15 days and 6 searches returning 42 products:
// notice → { days: 15, searches: 6, products: 42 }
export function useReviewPrompt(): UseReviewPrompt {
const [notice, setNotice] = useState<ReviewNotice | undefined>(undefined);
useEffect(() => {
let cancelled = false;
const check = async () => {
try {
// Only Web Store (Chrome) installs can leave a store review.
if (isFirefoxRuntime() || getInstallSource() !== 'webstore') return;
await ensureInstallDate();
const state = await getReviewPromptState();
if (cancelled || state.reviewed || state.installedAt <= 0) return;
const days = Math.floor((Date.now() - state.installedAt) / DAY_MS);
if (days < MIN_DAYS_INSTALLED || state.searchCount < MIN_SEARCHES) return;
// Dismissal window: silenced after MAX_DISMISSALS; the first dismissal
// snoozes until `snoozedUntil` before the one final showing.
if (state.dismissCount >= MAX_DISMISSALS) return;
if (state.dismissCount === 1) {
if (state.snoozedUntil === undefined || Date.now() < state.snoozedUntil) return;
}
setNotice({ days, searches: state.searchCount, products: state.totalResults });
} catch (error) {
console.error('Failed to evaluate the review prompt:', { error });
}
};
void check();
return () => {
cancelled = true;
};
}, []);
const onReview = useCallback(() => {
setNotice(undefined);
chrome.tabs.create({ url: CHROME_WEBSTORE_REVIEWS_URL, active: true });
void markReviewed();
}, []);
const onDismiss = useCallback(() => {
setNotice(undefined);
void snoozeReviewPrompt();
}, []);
return { notice, onReview, onDismiss };
}
Nudges engaged users to leave a Chrome Web Store review once they've clearly gotten value from the extension.
The prompt appears only for a Web Store install that has been present at least
reviewPrompt.minDaysInstalleddays and run at leastreviewPrompt.minSearchessearches (both fromconfig.json). The first dismissal snoozes it ~30 days for one final showing; a second dismissal — or opening the reviews page — silences it for good. Install date and counters come from getReviewPromptState; a pre-existing user with no recorded date is backfilled with "now" on this first check.