diff --git a/app/vmui/packages/vmui/src/pages/CustomPanel/QueryConfigurator/QueryConfigurator.tsx b/app/vmui/packages/vmui/src/pages/CustomPanel/QueryConfigurator/QueryConfigurator.tsx
index 6ca90a9217..5d50f3fce5 100644
--- a/app/vmui/packages/vmui/src/pages/CustomPanel/QueryConfigurator/QueryConfigurator.tsx
+++ b/app/vmui/packages/vmui/src/pages/CustomPanel/QueryConfigurator/QueryConfigurator.tsx
@@ -178,6 +178,10 @@ const QueryConfigurator: FC<QueryConfiguratorProps> = ({
     }
   }, [stateQuery, awaitStateQuery]);
 
+  useEffect(() => {
+    setStateQuery(query || []);
+  }, [query]);
+
   return <div
     className={classNames({
       "vm-query-configurator": true,
diff --git a/app/vmui/packages/vmui/src/pages/CustomPanel/hooks/useSetQueryParams.ts b/app/vmui/packages/vmui/src/pages/CustomPanel/hooks/useSetQueryParams.ts
index 0b3f22feb4..60f7f916f8 100644
--- a/app/vmui/packages/vmui/src/pages/CustomPanel/hooks/useSetQueryParams.ts
+++ b/app/vmui/packages/vmui/src/pages/CustomPanel/hooks/useSetQueryParams.ts
@@ -1,12 +1,18 @@
-import { useEffect } from "react";
-import { useTimeState } from "../../../state/time/TimeStateContext";
-import { useCustomPanelState } from "../../../state/customPanel/CustomPanelStateContext";
-import { useAppState } from "../../../state/common/StateContext";
-import { useQueryState } from "../../../state/query/QueryStateContext";
+import { useEffect, useState } from "react";
+import { useTimeDispatch, useTimeState } from "../../../state/time/TimeStateContext";
+import { useCustomPanelDispatch, useCustomPanelState } from "../../../state/customPanel/CustomPanelStateContext";
+import { useAppDispatch, useAppState } from "../../../state/common/StateContext";
+import { useQueryDispatch, useQueryState } from "../../../state/query/QueryStateContext";
 import { displayTypeTabs } from "../DisplayTypeSwitch";
-import { compactObject } from "../../../utils/object";
-import { useGraphState } from "../../../state/graph/GraphStateContext";
+import { useGraphDispatch, useGraphState } from "../../../state/graph/GraphStateContext";
 import { useSearchParams } from "react-router-dom";
+import { useCallback } from "preact/compat";
+import { getInitialDisplayType } from "../../../state/customPanel/reducer";
+import { getInitialTimeState } from "../../../state/time/reducer";
+import useEventListener from "../../../hooks/useEventListener";
+import { getQueryArray } from "../../../utils/query-string";
+import { arrayEquals } from "../../../utils/array";
+import { isEqualURLSearchParams } from "../../../utils/url";
 
 export const useSetQueryParams = () => {
   const { tenantId } = useAppState();
@@ -14,25 +20,108 @@ export const useSetQueryParams = () => {
   const { query } = useQueryState();
   const { duration, relativeTime, period: { date, step } } = useTimeState();
   const { customStep } = useGraphState();
-  const [, setSearchParams] = useSearchParams();
+  const [searchParams, setSearchParams] = useSearchParams();
+
+  const dispatch = useAppDispatch();
+  const timeDispatch = useTimeDispatch();
+  const graphDispatch = useGraphDispatch();
+  const queryDispatch = useQueryDispatch();
+  const customPanelDispatch = useCustomPanelDispatch();
+
+  const [isPopstate, setIsPopstate] = useState(false);
+
+  const setterSearchParams = useCallback(() => {
+    if (isPopstate) {
+      // After the popstate event, the states synchronizes with the searchParams,
+      // so there's no need to refresh the searchParams again.
+      setIsPopstate(false);
+      return;
+    }
+
+    const newSearchParams = new URLSearchParams(searchParams);
 
-  const setSearchParamsFromState = () => {
-    const params: Record<string, unknown> = {};
     query.forEach((q, i) => {
       const group = `g${i}`;
-      params[`${group}.expr`] = q;
-      params[`${group}.range_input`] = duration;
-      params[`${group}.end_input`] = date;
-      params[`${group}.tab`] = displayTypeTabs.find(t => t.value === displayType)?.prometheusCode || 0;
-      params[`${group}.relative_time`] = relativeTime;
-      params[`${group}.tenantID`] = tenantId;
+      if ((searchParams.get(`${group}.expr`) !== q) && q) {
+        newSearchParams.set(`${group}.expr`, q);
+      }
 
-      if ((step !== customStep) && customStep) params[`${group}.step_input`] = customStep;
+      if (searchParams.get(`${group}.range_input`) !== duration) {
+        newSearchParams.set(`${group}.range_input`, duration);
+      }
+
+      if (searchParams.get(`${group}.end_input`) !== date) {
+        newSearchParams.set(`${group}.end_input`, date);
+      }
+
+      if (searchParams.get(`${group}.relative_time`) !== relativeTime) {
+        newSearchParams.set(`${group}.relative_time`, relativeTime || "none");
+      }
+
+      const stepFromUrl = searchParams.get(`${group}.step_input`) || step;
+      if (stepFromUrl && (stepFromUrl !== customStep)) {
+        newSearchParams.set(`${group}.step_input`, customStep);
+      }
+
+      const displayTypeCode = `${displayTypeTabs.find(t => t.value === displayType)?.prometheusCode || 0}`;
+      if (searchParams.get(`${group}.tab`) !== displayTypeCode) {
+        newSearchParams.set(`${group}.tab`, `${displayTypeCode}`);
+      }
+
+      if (searchParams.get(`${group}.tenantID`) !== tenantId && tenantId) {
+        newSearchParams.set(`${group}.tenantID`, tenantId);
+      }
     });
+    if (isEqualURLSearchParams(newSearchParams, searchParams) || !newSearchParams.size) return;
+    setSearchParams(newSearchParams);
+  }, [tenantId, displayType, query, duration, relativeTime, date, step, customStep]);
 
-    setSearchParams(compactObject(params) as Record<string, string>);
-  };
+  useEffect(() => {
+    const timer = setTimeout(setterSearchParams, 200);
+    return () => clearTimeout(timer);
+  }, [setterSearchParams]);
 
-  useEffect(setSearchParamsFromState, [tenantId, displayType, query, duration, relativeTime, date, step, customStep]);
-  useEffect(setSearchParamsFromState, []);
+  useEffect(() => {
+    // Synchronize the states with searchParams only after the popstate event.
+    if (!isPopstate) return;
+
+    const timeFromUrl = getInitialTimeState();
+    const isDurationDifferent = (timeFromUrl.duration !== duration);
+    const isRelativeTimeDifferent = timeFromUrl.relativeTime !== relativeTime;
+    const isDateDifferent = timeFromUrl.relativeTime === "none" && timeFromUrl.period.date !== date;
+    const someNotEqual = isDurationDifferent || isRelativeTimeDifferent || isDateDifferent;
+    if (someNotEqual) {
+      timeDispatch({ type: "SET_TIME_STATE", payload: timeFromUrl });
+    }
+
+    const displayTypeFromUrl = getInitialDisplayType();
+    if (displayTypeFromUrl !== displayType) {
+      customPanelDispatch({ type: "SET_DISPLAY_TYPE", payload: displayTypeFromUrl });
+    }
+
+    const tenantIdFromUrl = searchParams.get("g0.tenantID") || "";
+    if (tenantIdFromUrl !== tenantId) {
+      dispatch({ type: "SET_TENANT_ID", payload: tenantIdFromUrl });
+    }
+
+    const queryFromUrl = getQueryArray();
+    if (!arrayEquals(queryFromUrl, query)) {
+      queryDispatch({ type: "SET_QUERY", payload: queryFromUrl });
+      timeDispatch({ type: "RUN_QUERY" });
+    }
+
+    // Timer prevents customStep reset on time range change.
+    const timer = setTimeout(() => {
+      const customStepFromUrl = searchParams.get("g0.step_input") || step;
+      if (customStepFromUrl && customStepFromUrl !== customStep) {
+        graphDispatch({ type: "SET_CUSTOM_STEP", payload: customStepFromUrl });
+      }
+    }, 50);
+
+    return () => clearTimeout(timer);
+  }, [searchParams, isPopstate]);
+
+  useEventListener("popstate", () => {
+    setIsPopstate(true);
+  });
 };
diff --git a/app/vmui/packages/vmui/src/pages/CustomPanel/index.tsx b/app/vmui/packages/vmui/src/pages/CustomPanel/index.tsx
index 64536551c1..cf56a8c407 100644
--- a/app/vmui/packages/vmui/src/pages/CustomPanel/index.tsx
+++ b/app/vmui/packages/vmui/src/pages/CustomPanel/index.tsx
@@ -12,7 +12,6 @@ import Alert from "../../components/Main/Alert/Alert";
 import classNames from "classnames";
 import useDeviceDetect from "../../hooks/useDeviceDetect";
 import InstantQueryTip from "./InstantQueryTip/InstantQueryTip";
-import useEventListener from "../../hooks/useEventListener";
 import { useRef } from "react";
 import CustomPanelTraces from "./CustomPanelTraces/CustomPanelTraces";
 import WarningLimitSeries from "./WarningLimitSeries/WarningLimitSeries";
@@ -65,9 +64,6 @@ const CustomPanel: FC = () => {
     setHideError(false);
   };
 
-  const handleChangePopstate = () => window.location.reload();
-  useEventListener("popstate", handleChangePopstate);
-
   useEffect(() => {
     graphDispatch({ type: "SET_IS_HISTOGRAM", payload: isHistogram });
   }, [graphData]);
diff --git a/app/vmui/packages/vmui/src/state/customPanel/reducer.ts b/app/vmui/packages/vmui/src/state/customPanel/reducer.ts
index b80aaeaa30..720a4645f6 100644
--- a/app/vmui/packages/vmui/src/state/customPanel/reducer.ts
+++ b/app/vmui/packages/vmui/src/state/customPanel/reducer.ts
@@ -19,12 +19,16 @@ export type CustomPanelAction =
   | { type: "TOGGLE_QUERY_TRACING" }
   | { type: "TOGGLE_TABLE_COMPACT" }
 
-const queryTab = getQueryStringValue("g0.tab", 0) as string;
-const displayType = displayTypeTabs.find(t => t.prometheusCode === +queryTab || t.value === queryTab);
+export const getInitialDisplayType = () => {
+  const queryTab = getQueryStringValue("g0.tab", 0) as string;
+  const displayType = displayTypeTabs.find(t => t.prometheusCode === +queryTab || t.value === queryTab);
+  return displayType?.value || DisplayType.chart;
+};
+
 const limitsStorage = getFromStorage("SERIES_LIMITS") as string;
 
 export const initialCustomPanelState: CustomPanelState = {
-  displayType: (displayType?.value || DisplayType.chart),
+  displayType: getInitialDisplayType(),
   nocache: false,
   isTracingEnabled: false,
   seriesLimits: limitsStorage ? JSON.parse(limitsStorage) : DEFAULT_MAX_SERIES,
diff --git a/app/vmui/packages/vmui/src/state/time/reducer.ts b/app/vmui/packages/vmui/src/state/time/reducer.ts
index 75d3231b1a..92ef5ef210 100644
--- a/app/vmui/packages/vmui/src/state/time/reducer.ts
+++ b/app/vmui/packages/vmui/src/state/time/reducer.ts
@@ -21,6 +21,7 @@ export interface TimeState {
 }
 
 export type TimeAction =
+  | { type: "SET_TIME_STATE", payload: { duration: string, period: TimeParams, relativeTime?: string; } }
   | { type: "SET_DURATION", payload: string }
   | { type: "SET_RELATIVE_TIME", payload: {id: string, duration: string, until: Date} }
   | { type: "SET_PERIOD", payload: TimePeriod }
@@ -32,24 +33,35 @@ export type TimeAction =
 const timezone = getFromStorage("TIMEZONE") as string || getBrowserTimezone().region;
 setTimezone(timezone);
 
-const defaultDuration = getQueryStringValue("g0.range_input") as string;
+export const getInitialTimeState = () => {
+  const defaultDuration = getQueryStringValue("g0.range_input") as string;
 
-const { duration, endInput, relativeTimeId } = getRelativeTime({
-  defaultDuration: defaultDuration || "1h",
-  defaultEndInput: formatDateToLocal(getQueryStringValue("g0.end_input", getDateNowUTC()) as string),
-  relativeTimeId: defaultDuration ? getQueryStringValue("g0.relative_time", "none") as string : undefined
-});
+  const { duration, endInput, relativeTimeId } = getRelativeTime({
+    defaultDuration: defaultDuration || "1h",
+    defaultEndInput: formatDateToLocal(getQueryStringValue("g0.end_input", getDateNowUTC()) as string),
+    relativeTimeId: defaultDuration ? getQueryStringValue("g0.relative_time", "none") as string : undefined
+  });
+
+  return {
+    duration,
+    period: getTimeperiodForDuration(duration, endInput),
+    relativeTime: relativeTimeId,
+  };
+};
 
 export const initialTimeState: TimeState = {
-  duration,
-  period: getTimeperiodForDuration(duration, endInput),
-  relativeTime: relativeTimeId,
+  ...getInitialTimeState(),
   timezone,
 };
 
 
 export function reducer(state: TimeState, action: TimeAction): TimeState {
   switch (action.type) {
+    case "SET_TIME_STATE":
+      return {
+        ...state,
+        ...action.payload
+      };
     case "SET_DURATION":
       return {
         ...state,
diff --git a/app/vmui/packages/vmui/src/utils/url.ts b/app/vmui/packages/vmui/src/utils/url.ts
index 8704fdbf61..e804b07a05 100644
--- a/app/vmui/packages/vmui/src/utils/url.ts
+++ b/app/vmui/packages/vmui/src/utils/url.ts
@@ -11,3 +11,17 @@ export const isValidHttpUrl = (str: string): boolean => {
 };
 
 export const removeTrailingSlash = (url: string) => url.replace(/\/$/, "");
+
+export const isEqualURLSearchParams = (params1: URLSearchParams, params2: URLSearchParams): boolean => {
+  if (Array.from(params1.entries()).length !== Array.from(params2.entries()).length) {
+    return false;
+  }
+
+  for (const [key, value] of params1) {
+    if (params2.get(key) !== value) {
+      return false;
+    }
+  }
+
+  return true;
+};
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index ab2791aa9d..89de24e0fe 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -50,6 +50,7 @@ See also [LTS releases](https://docs.victoriametrics.com/lts-releases/).
 * BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): fix bug that prevents the first query trace from expanding on click event. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6186). The issue was introduced in [v1.100.0](https://docs.victoriametrics.com/changelog/#v11000) release.
 * BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): fix calendar display when `UTC+00:00` timezone is set. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6239).
 * BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): remove redundant requests on the `Explore Cardinality` page. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6240).
+* BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): fix handling of URL params for browser history navigation (back and forward buttons). See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6126) and [this comment](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5516#issuecomment-1867507232).
 * BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent/): prevent potential panic during [stream aggregation](https://docs.victoriametrics.com/stream-aggregation.html) if more than one `--remoteWrite.streamAggr.dedupInterval` is configured. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6205).
 * BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent/): skip empty data blocks before sending to the remote write destination. Thanks to @viperstars for [the pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/6241).
 * BUGFIX: [stream aggregation](https://docs.victoriametrics.com/stream-aggregation/): set correct suffix `<output>_prometheus` for aggregation outputs [increase_prometheus](https://docs.victoriametrics.com/stream-aggregation/#increase_prometheus) and [total_prometheus](https://docs.victoriametrics.com/stream-aggregation/#total_prometheus). Before, outputs `total` and `total_prometheus` or `increase` and `increase_prometheus` had the same suffix.