Understanding URL Query Strings vs Hash Fragments
The other day, I stumbled upon something interesting. I was checking out a survey and noticed two URLs that looked like they pointed to the same place:
https://example.com/page?panel_view=true
and
https://example.com/page#/?panel_view=true
At a glance, you might think: "Same link, right?" Well… not exactly.
Let's dig into what's going on here — and why this small detail might actually matter, especially if you're working with modern web apps.
The Curious "#"
In the second URL, you'll notice a hash (#) followed by a slash:
#/?panel_view=true
That little hashtag, also called a hash fragment, plays a special role in web development.
🔍 TL;DR:
- Everything before the #= sent to the server
- Everything after the #= stays in the browser
In other words: the server doesn't care about anything after the #. It never even sees it. But if your app is a single-page application (SPA) — built with something like React, Vue, or Angular — it might care a lot.
So, Why Use the Hash at All?
Back in the day, # was mostly used for scrolling to sections within a page (like #about-us).
Now, in modern SPAs, it's often used for routing. When you see something like #/dashboard or #/login, that's your front-end framework deciding what content to show — without refreshing the page.
Do They Work the Same?
They might! Or they might not.
If the app is a traditional server-rendered page, it likely uses query strings (?) to load different content.
If it's an SPA, the app might rely entirely on the hash (#/) to trigger internal views.
Using the wrong one could lead to weird issues — like skipping required steps, not loading the right view, or even breaking analytics.
What About Caching?
Great question. Here's where things get even more interesting:
🧠 Browsers and CDNs treat query strings and hash fragments very differently when it comes to caching.
- Query strings ( - ?panel_view=true) affect the full URL — including server-side caching and CDN behavior. So: Different query strings = different cached versions. This can be good (for dynamic content), but sometimes bad (if it busts the cache unnecessarily).
- Hash fragments ( - #), on the other hand: Do not affect caching. The browser sees- https://example.com/page#aboutand- https://example.com/page#contactas the same resource — it just scrolls to a different part or changes state after the page loads.
💡 If you want to bust cache or serve different content from the server, use query strings. If you only need to affect front-end behavior, use hash fragments.
Catching These in Next.js
If you're using Next.js, here's how you grab each type:
✅ Query Strings
Next.js handles query strings natively using the useRouter hook:
import { useRouter } from 'next/router';
const Page = () => {
  const router = useRouter();
  const { panel_view } = router.query;
  return <p>panel_view is {panel_view}</p>;
};
📦 Hash Fragments
Hash values aren't part of Next.js routing — they live only in the browser. You'll need to use window.location.hash:
import { useEffect, useState } from 'react';
const Page = () => {
  const [hash, setHash] = useState('');
  useEffect(() => {
    setHash(window.location.hash);
  }, []);
  return <p>Current hash is: {hash}</p>;
};
Just remember: since this uses window, it must run client-side only (e.g., inside useEffect).
A Tiny Detail, A Bigger Lesson
This small difference is a great reminder that URLs are more than just links — they carry instructions. And depending on how your app is built, the format of those instructions can change what users see (or don't see).
Next time you copy-paste a link, give it a second look. That tiny # could be doing a lot more than you think. 😉
Ever been bitten by a query string or hash behavior in a project? Drop a comment — I'd love to hear your story!
 
 
              
 
    
Top comments (0)