useHash()
Last updated: 24/04/2026
Overview
useHash exposes the current URL hash (window.location.hash) as reactive state and provides a setter that writes window.location.hash when it actually changes. It listens for the browser hashchange event so updates still propagate when the hash changes via back/forward navigation, manual edits to the URL, or other code paths-not only through the returned setter-making it a simple building block for hash-routed tabs, deep links, and lightweight client-side navigation without a router.
What it accepts
- No arguments.
What it returns
- Tuple
[hash, setHash]:hash: Current hash string (including#if present inlocation.hash). Typestring.setHash: Updateswindow.location.hashwhen different. Type(newHash: string) => void.
Usage
Real-world example: hash-driven tabs (#profile / #security) with a text field that can jump to an arbitrary hash.
tsx
import { useMemo, useState } from 'react'
import useHash from '@dedalik/use-react/useHash'
function Example() {
const [hash, setHash] = useHash()
const [custom, setCustom] = useState('#settings')
const active = useMemo(() => {
if (hash === '#profile') return 'profile'
if (hash === '#security') return 'security'
if (hash === '#settings') return 'settings'
return 'home'
}, [hash])
return (
<div>
<h3>Hash routing (lightweight)</h3>
<p>
Current hash: <code>{hash || '(empty)'}</code> - active panel: <code>{active}</code>
</p>
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
<button type='button' onClick={() => setHash('')}>
Clear hash
</button>
<button type='button' onClick={() => setHash('#profile')}>
#profile
</button>
<button type='button' onClick={() => setHash('#security')}>
#security
</button>
</div>
<div style={{ marginTop: 12, display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' }}>
<input value={custom} onChange={(event) => setCustom(event.target.value)} style={{ minWidth: 240 }} />
<button type='button' onClick={() => setHash(custom)}>
Go
</button>
</div>
<div style={{ marginTop: 16, padding: 12, border: '1px solid #ddd', borderRadius: 12 }}>
{active === 'profile' ? <p>Profile panel</p> : null}
{active === 'security' ? <p>Security panel</p> : null}
{active === 'settings' ? <p>Settings panel</p> : null}
{active === 'home' ? <p>Home panel (no matching hash)</p> : null}
</div>
</div>
)
}
export default function Demo() {
return <Example />
}API Reference
useHash
Signature: useHash(): [string, (newHash: string) => void]
Parameters
None.
Returns
Tuple:
hash- Currentwindow.location.hashvalue. (string)setHash- Setswindow.location.hashwhen it differs from the current location. ((newHash: string) => void)
Copy-paste hook
TypeScript
tsx
import { useCallback, useEffect, useState } from 'react'
/**
* Reads and writes `window.location.hash`, keeping React state in sync via the `hashchange` event.
*/
export default function useHash(): [string, (newHash: string) => void] {
const [hash, setLocalHash] = useState<string>(() => (typeof window === 'undefined' ? '' : window.location.hash))
const onHashChange = useCallback(() => {
setLocalHash(window.location.hash)
}, [])
useEffect(() => {
if (typeof window === 'undefined') return
window.addEventListener('hashchange', onHashChange)
return () => window.removeEventListener('hashchange', onHashChange)
}, [onHashChange])
const setHash = useCallback((newHash: string) => {
if (typeof window === 'undefined') return
if (newHash !== window.location.hash) {
window.location.hash = newHash
}
}, [])
return [hash, setHash]
}JavaScript
js
import { useCallback, useEffect, useState } from 'react'
export default function useHash() {
const [hash, setLocalHash] = useState(() => (typeof window === 'undefined' ? '' : window.location.hash))
const onHashChange = useCallback(() => {
setLocalHash(window.location.hash)
}, [])
useEffect(() => {
if (typeof window === 'undefined') return
window.addEventListener('hashchange', onHashChange)
return () => window.removeEventListener('hashchange', onHashChange)
}, [onHashChange])
const setHash = useCallback((newHash) => {
if (typeof window === 'undefined') return
if (newHash !== window.location.hash) {
window.location.hash = newHash
}
}, [])
return [hash, setHash]
}