While contributing to Accuguide — an open source platform that helps people discover accessible places and services — I came across an annoying bug where the browser was asking for location permission on every single page load, not just when it was actually needed.
In this article I'll walk you through what caused it, how I tracked it down, and how I fixed it.
The Problem
When you visit any page on Accuguide, the browser would immediately pop up:
"accuguide.org wants to know your location"
This happened even on pages like /info/about or /legal/privacy where location data is completely irrelevant. It was bad UX and also hurt the site's Lighthouse/PageSpeed score under the "Avoids requesting permissions on page load" audit.
The expected behavior was simple: only ask for location permission on the /search page, and only when the user has actually typed a search query.
Finding the Bug
The location logic lived in a React context called LocationProvider in app/contexts/location-context.tsx.
Inside it was this useEffect:
// ❌ The problem
useEffect(() => {
requestLocationPermission()
}, [])
This runs on every page because LocationProvider was wrapped around the entire app in the root layout. The empty dependency array [] means it fires once on mount — but since it's in the root layout, "mount" happens on every page.
Understanding the Fix
The fix had two parts:
Part 1: Remove the auto-trigger from LocationProvider
Remove the useEffect that was automatically requesting location on mount:
// ✅ Remove this entirely from LocationProvider
useEffect(() => {
requestLocationPermission()
}, [])
Now LocationProvider no longer automatically asks for location. It just provides the context — location must be requested explicitly.
Part 2: Request location only on the search page with a query
In app/search/page.tsx, add logic to request location only when:
- The user is on the search page
- There is an active search query
const searchParams = useSearchParams()
const query = searchParams.get('q')
const { requestLocationPermission, location } = useLocation()
useEffect(() => {
if (query && !location) {
requestLocationPermission()
}
}, [query])
Breaking this down:
-
query— the search term from the URL (?q=coffee shops) -
!location— only request if we don't already have location -
[query]— only re-run when the query changes
Why This Matters
Before the fix:
- ❌ Location permission prompt appears on every page
- ❌ Lighthouse flags "Avoids requesting permissions on page load"
- ❌ Users get confused and deny permission unnecessarily
- ❌ Browser may block future permission requests after repeated denials
After the fix:
- ✅ Location permission only requested on
/searchpage - ✅ Only triggered when there's an active search query
- ✅ Lighthouse audit passes
- ✅ Users understand WHY location is needed (they're searching!)
The Key Lesson
Context providers run on every page. If you put side effects like requestLocationPermission() inside a provider that wraps your whole app, it will run everywhere.
Always ask: "Where does this actually need to happen?" Then put the logic there — not in a global provider.
// ❌ Don't do this in a global provider
useEffect(() => {
doSomethingThatShouldOnlyHappenInOnePlace()
}, [])
// ✅ Do this in the specific component/page that needs it
useEffect(() => {
if (conditionIsRight) {
doSomethingThatShouldOnlyHappenInOnePlace()
}
}, [dependency])
Real World Impact
This is a common mistake in React apps. I've seen it with:
- Analytics tracking firing on pages it shouldn't
- Cookie consent popups appearing multiple times
- Auth checks running unnecessarily on public pages
The pattern is always the same: a side effect placed too high in the component tree.
Summary
| Before | After | |
|---|---|---|
| Location prompt | Every page | Search page only |
| Trigger condition | On mount | When query exists |
| Lighthouse audit | ❌ Fails | ✅ Passes |
| User experience | Confusing | Intuitive |
If you found this helpful, check out the Accuguide repo and my GitHub profile.
Have questions or spotted something I missed? Drop a comment below!
Top comments (0)