AbsoluteJS

absolute/no-button-navigation

Enforce using anchor tags for navigation instead of buttons whose onClick handlers change the path.

React & JSXSuggestion
View on GitHub

#Rule Details

Pushes real navigation onto anchor tags instead of onClick handlers that change the path imperatively. Anchors are accessible, work with middle-click and "open in new tab", and let the browser and crawlers see where a link goes. The rule inspects onClick handlers attached directly to a lowercase <button> and flags path-changing calls.

It reports assignments to window.location (or its href/pathname sub-properties), window.location.replace(...), window.open(...), and history.pushState/replaceState calls. The escape hatch: if the handler reads window.location.search, .pathname, or .hash, an accompanying pushState/replaceState is allowed — that pattern updates the query or hash while preserving the path, which anchors cannot express.

#Examples

Incorrect

Assigning window.location

TSX
const Go = () => (
	<button onClick={() => { window.location = '/dashboard'; }}>Go</button>
);
TSX
const Go = () => (
	<button onClick={() => { window.location.replace('/dashboard'); }}>Go</button>
);

pushState without reading the current path

TSX
const Go = () => (
	<button onClick={() => { window.history.pushState({}, '', '/new'); }}>
		Go
	</button>
);
Correct

Use an anchor for navigation

TSX
const Link = () => <a href="/dashboard">Dashboard</a>;

Reading the path whitelists replaceState

TSX
// Query/hash-only update that preserves the path is allowed
const TabButton = () => (
	<button
		onClick={() => {
			const path = window.location.pathname;
			window.history.replaceState({}, '', path + '?tab=settings');
		}}
	>
		Settings
	</button>
);
TSX
const Logger = () => (
	<button onClick={() => console.log('clicked')}>Click</button>
);

#Configuration

Enable this rule in your ESLint configuration:

JS
{
	rules: {
		'absolute/no-button-navigation': 'error'
	}
}

#When Not To Use It

If you use a router whose navigation API genuinely requires a button handler and cannot be expressed as an anchor, or you have an established design-system button that performs navigation, disable the rule for those components.

#Resources