Question

Change the URL Without Reloading in JavaScript

javascripthtmlurlurl-rewritingfriendly-url

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 pushState or replaceState must 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 URL API or location properties 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.

Take Quiz

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:

const url = new URL(window.location.href);

console.log(url.origin);   // e.g. https://www.mysite.com
console.log(url.pathname); // e.g. /page2.php
console.log(url.search);   // e.g. ?category=books
console.log(url.hash);     // e.g. #reviews

const withoutHash = url.origin + url.pathname + url.search;
console.log(withoutHash);

If you need only the current path after the domain, use:

console.log(location.pathname);

Include location.search too if query parameters matter.

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:

  1. new URL(location.href) creates an object with separately accessible URL parts.
  2. origin contains https://www.mysite.com.
  3. pathname contains /catalog.php.
  4. search contains ?sort=price.
  5. The expression for beforeHash intentionally leaves out hash, so the result excludes #featured.
  6. history.pushState changes the address bar to the new same-origin path, query, and hash.
  7. The document does not reload. A new history entry is created, so the browser Back button can return to the previous URL.

Real World Use Cases

  • Single-page applications: Update /products/42 after 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=2 when the user selects another page of results.
  • Tabs and sections: Use #details or #reviews to identify the active section of a product page.
  • Wizards: Update /signup?step=2 as the user proceeds, allowing the current step to be restored later.
  • Modal dialogs: Add a query parameter such as ?login=true so a shared link can reopen the same dialog state.
Take Quiz

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:

  • Validation before navigation: Check that an ID or filter value is valid before placing it in a URL.
  • Query parameter helpers: Use URL and URLSearchParams rather than concatenating strings.
  • Replace temporary state: Use replaceState for live search input so every keystroke does not create a Back-button entry.
  • Server fallback: Configure the server to return the app entry page for client-side routes such as /products/42, so refreshing or directly visiting the URL still works.

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

history.pushState({}, "", "https://other-site.example/page2");

This is not allowed because the History API only permits same-origin URLs. Use ordinary navigation if you truly need to go to another site.

Splitting URLs manually

const beforeHash = location.href.split("#")[0];

This works for the narrow task of removing a hash, but URL is clearer when you need paths, query parameters, or reliable modification:

const url = new URL(location.href);
url.hash = "";
console.log(url.href);

Forgetting Back and Forward buttons

If your app updates the URL with pushState, listen for popstate and render the appropriate view when users navigate through history.

Comparisons

ApproachChanges address barReloads or navigatesCreates history entryBest use
location.href = "/path"YesYesUsuallyNavigate to another document or site
location.assign("/path")YesYesYesExplicit document navigation
location.replace("/path")YesYesNoNavigate without allowing Back to the prior page
history.pushState({}, "", "/path")YesNoYesClient-side route or meaningful view change
history.replaceState({}, "", "/path")YesNoNoCorrect or synchronize the current URL
location.hash = "section"YesUsually no full reloadYesFragment navigation or simple hash-based state

pushState is generally preferable to hash-based routing when the server and application support normal paths such as /products/42. Hashes are still useful for in-page anchors and situations where only fragment state is needed.

Take Quiz

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
window.addEventListener("popstate", (event) => {
  console.log(event.state);
});

Rules:

  • pushState and replaceState require a same-origin URL.
  • Neither method loads the destination page.
  • pushState adds a history entry; replaceState does not.
  • pushState itself does not fire popstate.
  • The hash begins with # and is not included in normal server requests.

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.

Why does refreshing a client-side route sometimes return a 404 page?

The browser requests the visible path from the server on refresh. The server must be configured to serve the application for that path, or provide a matching server route.

Does changing location.hash reload the page?

It normally does not perform a full document reload, but it changes the fragment, can scroll to an element with a matching ID, and creates a history entry.

Take Quiz

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.

Take Quiz

Keep learning

Related questions

View more