no-history-mutation
Disallow in-place mutation of browser history state.
Targeted pattern scopeâ
This rule targets direct usage of global history objects (including window.history, self.history, and aliases) when mutation APIs are called or history properties are written.
What this rule reportsâ
history.pushState(...)history.replaceState(...)history.back(),history.forward(),history.go(...)- Property assignments/deletes/updates on
historyand tracked aliases
Why this rule existsâ
Browser history is a shared mutable global state machine. In-place navigation mutations create hidden side effects that are difficult to reason about in deterministic dataflow architectures.
This rule helps preserve immutability boundaries by preventing direct history mutations.
â Incorrectâ
history.pushState({ step: 2 }, "", "/step-2");
â Correctâ
const currentState = history.state;
Additional examplesâ
// â Mutates global history state
const navHistory = history;
navHistory.replaceState({}, "", "/next");
// â
Reads history state without mutation
const navHistory = history;
navHistory.state;
ESLint flat config exampleâ
import immutable from "eslint-plugin-immutable-2";
export default [
{
files: ["**/*.{js,mjs,cjs,ts,mts,cts,tsx}"],
plugins: { immutable },
rules: {
"immutable/no-history-mutation": "error",
},
},
];
When not to use itâ
If your app intentionally centralizes imperative browser navigation in a dedicated adapter layer, this rule may be too strict outside that boundary.
Rule catalog ID: R929