Hash routing breaks stuff
Hash routing is a client-side routing technique that uses the URL fragment (#...) to represent view state without reloading the page. It is extremely common in older SPAs, but the pattern has a lot of hidden costs.
Everything after the # is technically a fragment identifier.
Why people reach for it
- Easy to implement: most browsers already preserve the fragment and don’t reload the page.
- SPA routing libraries offer it as a built-in option.
- It can feel faster because the browser doesn’t request a new document.
- It was introduced as a workaround before the History API existed.
Those are valid reasons for legacy apps, but they don’t make it the right choice for modern web architecture.
Why hash routing is bad
Poor SEO: search engines treat fragments as client-side state, not a separate URL to index.Broken URL semantics: fragments are meant for anchors, not app state.URL parsers can drop them: some tools, analytics platforms, and servers ignore or cleanse fragments entirely.Anchor links stop working: if you store state in#, you can’t also use fragment identifiers for jump targets or skip links.Accessibility risk: custom scroll-to-hash solutions often hijack native behavior and lose link semantics.
A URL is a contract. Once you start using # as a routing mechanism, it becomes harder to reason about what that URL actually means.
What fragments should be used for
Fragments are ideal for document-level navigation:
- skip links and in-page navigation
- table of contents anchors
- deep links to sections inside a page
- preserving scroll position for a fragment target
If the browser can do this for you natively, don’t replace it with a state-management hack.
What to use instead
Use the History API / real URLs
Modern client-side routing should use normal paths and query params instead of fragments. That keeps URLs meaningful, shareable, and indexable.
Keep state out of the URL when it doesn’t belong there
If you are only storing ephemeral UI state, consider local state, context, or a store. The URL should describe the page, not every toggle and filter value.
Preserve native navigation semantics
If you need an anchor destination, use a normal #target link. If you need to represent a page or view, use a path like /users/123 or /settings/profile.
Practical guidance
- Don’t use hash routing for app navigation unless you’re maintaining a legacy application.
- Use fragments for anchor targets and document fragments only.
- Prefer standard URLs and the History API for SPA routing.
- Avoid adding libraries that patch anchor behavior to make hash routing feel like navigation.