Question
Is there a way to modify the URL of the current page without reloading it? I would also like to access the portion of the URL before the # hash.
I only need to change the portion after the domain, so the new URL will remain on the same origin. For example, assigning to window.location.href reloads the page:
window.location.href = "https://www.mysite.com/page2.php";
Short Answer
You will learn why changing window.location.href causes navigation, how the History API changes a same-origin URL without a reload, and how to read URL parts such as the path, query string, and hash.
Concept
Changing window.location.href tells the browser to navigate to another document. Navigation normally means making a request, unloading the current page, and loading the destination page.
For applications that update their interface with JavaScript, browsers provide the History API. Its history.pushState() and history.replaceState() methods update the address bar without requesting or reloading the new URL.
history.pushState({}, "", "/page2.php");
This changes the visible URL to the same site's /page2.php, while the currently loaded JavaScript page remains active.
Important rules:
- The URL passed to
pushStateorreplaceStatemust be same-origin: same protocol, host, and port. - Changing the URL does not load content for that URL. Your code must update the page content if needed.
- The hash is the portion beginning with
#. It is a client-side fragment and is not sent in normal HTTP requests. - Use the
URLAPI orlocationproperties instead of manually slicing URL strings when possible.
Mental Model
Think of browser history as a stack of cards. Each card represents an address the user has visited.
window.location.href = ...means: leave the room and travel to the address on another card.history.pushState(...)means: stay in the same room, write a new address on a new card, and place it on top of the stack.history.replaceState(...)means: stay in the room and edit the address on the current top card.
Because you never leave the room with the History API, the page does not reload.
Syntax and Examples
Use pushState when the change should become a new Back-button entry:
history.pushState(state, title, url);
Use replaceState when the current history entry should be updated instead:
history.replaceState(state, title, url);
The title argument is usually an empty string because browsers generally do not use it for the document title.
// Suppose the current site is https://www.mysite.com/products.php
history.pushState({ category: "books" }, "", "/products.php?category=books");
console.log(location.href);
// https://www.mysite.com/products.php?category=books
You can also use a relative URL:
history.pushState({}, "", "page2.php");
For predictable URL parsing, use URL:
Step by Step Execution
Consider a page currently at:
https://www.mysite.com/catalog.php?sort=price#featured
Run this code:
const currentUrl = new URL(location.href);
const beforeHash = currentUrl.origin + currentUrl.pathname + currentUrl.search;
console.log(beforeHash);
// https://www.mysite.com/catalog.php?sort=price
history.pushState(
{ section: "sale" },
"",
"/catalog.php?sort=price#sale"
);
console.log(location.href);
// https://www.mysite.com/catalog.php?sort=price#sale
Step by step:
new URL(location.href)creates an object with separately accessible URL parts.origincontainshttps://www.mysite.com.pathnamecontains/catalog.php.searchcontains .
Real World Use Cases
- Single-page applications: Update
/products/42after JavaScript displays product 42 without loading a new document. - Search and filters: Reflect selected filters in URLs such as
/search?q=keyboard&inStock=true, so a user can bookmark or share the current view. - Pagination: Change
/articles?page=2when the user selects another page of results. - Tabs and sections: Use
#detailsor#reviewsto identify the active section of a product page. - Wizards: Update
/signup?step=2as the user proceeds, allowing the current step to be restored later. - Modal dialogs: Add a query parameter such as
?login=trueso a shared link can reopen the same dialog state.
Real Codebase Usage
In a real project, URL changes and UI changes should happen together. A common pattern is to place navigation logic in one function:
function showProduct(productId) {
history.pushState({ productId }, "", `/products/${productId}`);
document.querySelector("#app").textContent = `Showing product ${productId}`;
}
Also handle browser Back and Forward navigation. Calling pushState does not trigger popstate; popstate occurs when the user moves through existing history entries.
window.addEventListener("popstate", (event) => {
const productId = event.state?.productId;
if (productId) {
document.querySelector("#app").textContent = `Showing product ${productId}`;
}
});
Common project patterns include:
Common Mistakes
Expecting pushState to fetch or render a page
history.pushState({}, "", "/page2.php");
This changes only the browser URL. It does not request page2.php or update the visible content. Update the DOM yourself, or use your application's router.
Using location.href when a reload is unwanted
// This navigates and normally reloads the document.
location.href = "/page2.php";
Use history.pushState({}, "", "/page2.php") for a same-origin, no-reload URL update.
Omitting the leading slash unintentionally
history.pushState({}, "", "page2.php");
This is relative to the current path. If the page is /folder/current.php, the result may become /folder/page2.php. Use /page2.php when you mean a path from the site root.
Attempting to change to another origin
Comparisons
| Approach | Changes address bar | Reloads or navigates | Creates history entry | Best use |
|---|---|---|---|---|
location.href = "/path" | Yes | Yes | Usually | Navigate to another document or site |
location.assign("/path") | Yes | Yes | Yes | Explicit document navigation |
location.replace("/path") | Yes | Yes | No | Navigate without allowing Back to the prior page |
history.pushState({}, "", "/path") | Yes | No | Yes |
Cheat Sheet
// Add a new same-origin URL to browser history without reloading
history.pushState({}, "", "/page2.php");
// Update the current history entry without reloading
history.replaceState({}, "", "/page2.php");
// Read URL pieces
location.href; // complete URL
location.origin; // protocol + host + port
location.pathname; // path, such as /page2.php
location.search; // query string, such as ?page=2
location.hash; // fragment, such as #comments
// Get the URL without its hash
const url = new URL(location.href);
const withoutHash = url.origin + url.pathname + url.search;
// Safely change a query parameter
const url = new URL(location.href);
url.searchParams.set("page", "2");
history.pushState({}, "", url);
// Respond to browser Back and Forward
.(, {
.(event.);
});
FAQ
Can JavaScript change the URL without refreshing the page?
Yes. Use history.pushState() or history.replaceState() with a same-origin URL. They update the address bar without reloading the document.
What is the difference between pushState and replaceState?
pushState creates a new browser history entry. replaceState edits the current entry, so pressing Back does not return to the prior version of that URL.
Does history.pushState() load the new page?
No. It does not request the URL or render new content. Your JavaScript must update the interface.
How do I get a URL without the hash in JavaScript?
Use new URL(location.href) and combine origin, pathname, and search. For only removing the hash, location.href.split("#")[0] also works.
Can I use pushState to change the domain?
No. The new URL must have the same protocol, host, and port as the current page. Cross-origin navigation requires location.href, a link, or another navigation mechanism.
Mini Project
Description
Build a small client-side product filter that keeps the selected category in the URL. It demonstrates how an interface can change state, update the address bar without reloading, and restore state when the user uses Back or Forward.
Goal
Create category buttons that update the category query parameter without reloading the page.
Requirements
Use history.pushState() when a category button is selected.
Read the category query parameter on initial page load.
Render the currently selected category in the page.
Handle the popstate event so Back and Forward restore the displayed category.
Keep learning
Related questions
Abort Ajax Requests with jQuery jqXHR.abort()
Learn how to cancel an in-progress jQuery Ajax request with jqXHR.abort(), handle abort status safely, and avoid stale UI updates.
Access the Correct this Inside a JavaScript Callback
Learn why JavaScript this changes in callbacks and how to preserve an object context using bind, arrow functions, and event handler patterns.
Add Key-Value Pairs to JavaScript Objects
Learn how to add key-value pairs to JavaScript objects with dot and bracket notation, dynamic keys, examples, and common mistakes.