diff --git a/ui/src/common/index.js b/ui/src/common/index.js
index f64d4fe0c..356225680 100644
--- a/ui/src/common/index.js
+++ b/ui/src/common/index.js
@@ -41,3 +41,4 @@ export * from './formatRange.js'
export * from './playlistUtils.js'
export * from './PathField.jsx'
export * from './ParticipantsInfo'
+export * from './useSearchRefocus'
diff --git a/ui/src/common/useSearchRefocus.js b/ui/src/common/useSearchRefocus.js
new file mode 100644
index 000000000..27898b969
--- /dev/null
+++ b/ui/src/common/useSearchRefocus.js
@@ -0,0 +1,19 @@
+import { useEffect } from 'react'
+
+export const useSearchRefocus = () => {
+ useEffect(() => {
+ const handleClick = (e) => {
+ const clearButton = e.target.closest('[aria-label*="clear" i]')
+ if (clearButton) {
+ setTimeout(() => {
+ const searchInput = document.querySelector('[class*="RaSearchInput"] input')
+ if (searchInput) {
+ searchInput.focus()
+ }
+ }, 800)
+ }
+ }
+ document.addEventListener('click', handleClick, true)
+ return () => document.removeEventListener('click', handleClick, true)
+ }, [])
+}
diff --git a/ui/src/common/useSearchRefocus.test.js b/ui/src/common/useSearchRefocus.test.js
new file mode 100644
index 000000000..8498633fa
--- /dev/null
+++ b/ui/src/common/useSearchRefocus.test.js
@@ -0,0 +1,65 @@
+import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest'
+import { renderHook } from '@testing-library/react-hooks'
+import { useSearchRefocus } from './useSearchRefocus'
+
+describe('useSearchRefocus', () => {
+ let container
+
+ beforeEach(() => {
+ vi.useFakeTimers()
+ container = document.createElement('div')
+ container.innerHTML = `
+
+
+
+
+ `
+ document.body.appendChild(container)
+ })
+
+ afterEach(() => {
+ vi.useRealTimers()
+ document.body.removeChild(container)
+ })
+
+ it('focuses the input after clicking clear button', () => {
+ renderHook(() => useSearchRefocus())
+
+ const clearButton = container.querySelector('[aria-label="clear search"]')
+ const input = container.querySelector('input')
+ const focusSpy = vi.spyOn(input, 'focus')
+
+ clearButton.click()
+
+ expect(focusSpy).not.toHaveBeenCalled()
+
+ vi.advanceTimersByTime(600)
+
+ expect(focusSpy).toHaveBeenCalledTimes(1)
+ })
+
+ it('does not focus if click is not on a clear button', () => {
+ renderHook(() => useSearchRefocus())
+
+ const input = container.querySelector('input')
+ const focusSpy = vi.spyOn(input, 'focus')
+
+ input.click()
+
+ vi.advanceTimersByTime(600)
+
+ expect(focusSpy).not.toHaveBeenCalled()
+ })
+
+ it('cleans up event listener on unmount', () => {
+ const removeEventListenerSpy = vi.spyOn(document, 'removeEventListener')
+
+ const { unmount } = renderHook(() => useSearchRefocus())
+ unmount()
+
+ expect(removeEventListenerSpy).toHaveBeenCalledWith(
+ 'click',
+ expect.any(Function),
+ )
+ })
+})
diff --git a/ui/src/layout/Layout.jsx b/ui/src/layout/Layout.jsx
index e3f13d25f..44cf9b42c 100644
--- a/ui/src/layout/Layout.jsx
+++ b/ui/src/layout/Layout.jsx
@@ -7,6 +7,7 @@ import Menu from './Menu'
import AppBar from './AppBar'
import Notification from './Notification'
import useCurrentTheme from '../themes/useCurrentTheme'
+import { useSearchRefocus } from '../common'
const useStyles = makeStyles({
root: { paddingBottom: (props) => (props.addPadding ? '80px' : 0) },
@@ -17,6 +18,7 @@ const Layout = (props) => {
const queue = useSelector((state) => state.player?.queue)
const classes = useStyles({ addPadding: queue.length > 0 })
const dispatch = useDispatch()
+ useSearchRefocus()
const keyHandlers = {
TOGGLE_MENU: useCallback(() => dispatch(toggleSidebar()), [dispatch]),