I was sitting on my couch trying to add a show to Sonarr on my phone. Searched for something, did the thing, then tapped the × to clear the search and add another. The keyboard dismissed. I had to tap the input box again to get it back.
Two taps instead of one. To be clear, this wasn’t life-threatening – not a crash, not wrong data – just the kind of friction that compounds quietly across every session until you stop noticing it, or stop using the app on mobile because it feels like it’s working against you.
I went looking for who had filed a bug before me, because surely someone had. No one had. So I filed it. Reproducible, irritating, worth my time.
Why it was actually hard
The fix seemed obvious: when the user clears the search, call .focus() on the input. Except on mobile Safari (and Chrome on iOS, per my testing), .focus() only raises the software keyboard when it’s called synchronously inside a direct user gesture. Defer it – with a useEffect, a setTimeout, anything async – and the browser silently ignores it. Input gets focus in the DOM sense, but the keyboard stays down.
(A maintainer later asked whether e.preventDefault() on the button would be simpler. That’d work on desktop – blocks the mousedown before the input loses focus. On mobile, focus is already gone during touchstart, which fires earlier in the event sequence. preventDefault has nothing to prevent by then.)
So the fix required calling .focus() synchronously inside the tap handler, which meant the input component needed to expose a focus() method — a React pattern already used elsewhere in the codebase, thankfully.
Being a guest
This is my first potential contribution to a widely-used open source project with real maintainers who have opinions (I assume they have opinions, having built a damn useful and pretty useable app). Didn’t seem right to blunder in.
Before branching: read the contribution guidelines, confirmed the pattern I was using existed elsewhere in their code, verified their gitflow. Opened the issue first and waited for triage before readying the PR.
When I did open the Draft PR, I called out the one glaring thing upfront: the diff looks alarming – 280+ lines changed – but almost all of it is re-indentation from the refactor. Here’s the whitespace-ignoring view. Here’s why the approach is valid. Don’t make the reviewer work to figure out what you actually changed, especially as an unknown Internet goon throwing them a drive-by.
A maintainer asked if a simpler one-liner would do. I explained why it wouldn’t work on mobile, politely and with specifics, and offered to collaborate if they had insights I didn’t.
Where it sits
The PR is Ready for Review. The issue was triaged and labelled the next day. Keyboard will pop up on the first tap – at least on my couch, on my phone.
What I want to emphasise isn’t that I can write React – hell, with Agentic tools that’s the easy part. It’s that I noticed the friction, understood it before touching the code, and approached the fix in a way that respected the people who’d built the thing I was trying to improve. Standing on the shoulders of giants, the least I could do is wash the mud off my shoes.
Two taps to one. It’s a small thing. I filed a bug over it anyway.
