
Large PRs come with large file lists. If your project mixes source files with generated metadata, assets, or config, the GitHub extension filter quickly becomes a chore. You want to review one file type. GitHub has no built-in way to solo it. No "select only this" option. You're unchecking a dozen others to get there. Every. Single. Time.
I got tired of it. So I fixed it.
The idea
Alt+Click any extension → everything else disappears. One click, instant filter, no reload.
Simple enough concept. The implementation took some digging.
Getting there
GitHub's filter is a React component, which means the usual tricks don't work. .click() on list items is silently ignored. URL rewrites cause a full page reload. Workable, but slow. history.pushState updates the URL but React doesn't react to it.
The actual solution: walk up the React fiber tree from the dropdown's DOM element until you find the component that owns the filter state. It has a function called setFileFilterState. Call it directly with all other extensions marked unselected, and the file list updates instantly. No navigation, no reload.
function getFilterProps() {
const el = document.querySelector('.prc-ActionList-ActionList-rPFF2');
if (!el) return null;
const fiber = el[Object.keys(el).find(k => k.startsWith('__reactFiber'))];
let node = fiber;
for (let i = 0; i < 50; i++) {
if (node?.memoizedProps?.setFileFilterState) return node.memoizedProps;
node = node?.return;
if (!node) break;
}
return null;
}
Once you have it, soloing an extension is one call:
const allExts = Object.keys(props.fileFilterState.fileExtensions);
props.setFileFilterState({
...props.fileFilterState,
unselectedFileExtensions: new Set(allExts.filter(e => e !== '.cs'))
});
Setup
- Install Tampermonkey
- Create a new script, paste the code below, save
- Make sure Tampermonkey has permission to run on github.com. Easy to miss, and it will silently break everything
Full script
// ==UserScript==
// @name GitHub PR Filter - Solo Click
// @namespace http://tampermonkey.net/
// @version 1.0
// @match *://*/*
// @run-at document-start
// @grant none
// ==/UserScript==
(function() {
'use strict';
function getFilterProps() {
const el = document.querySelector('.prc-ActionList-ActionList-rPFF2');
if (!el) return null;
const fiber = el[Object.keys(el).find(k => k.startsWith('__reactFiber'))];
let node = fiber;
for (let i = 0; i < 50; i++) {
if (node?.memoizedProps?.setFileFilterState && node?.memoizedProps?.fileFilterState) {
return node.memoizedProps;
}
node = node?.return;
if (!node) break;
}
return null;
}
document.addEventListener('click', function(e) {
if (!e.altKey) return;
const item = e.target.closest('.prc-ActionList-ActionListItem-So4vC');
if (!item) return;
const text = item.innerText?.trim().split('\n')[0];
if (!text || !text.startsWith('.')) return;
e.preventDefault();
e.stopPropagation();
const props = getFilterProps();
if (!props) return;
const allExts = Object.keys(props.fileFilterState.fileExtensions);
props.setFileFilterState({
...props.fileFilterState,
unselectedFileExtensions: new Set(allExts.filter(e => e !== text))
});
}, true);
})();
Usage: open the extension filter on any PR, hold Alt, click any extension. Done.
⚠️ GitHub's class names change occasionally. If it stops working, inspect the dropdown and update the two .prc-ActionList-* selectors to whatever GitHub is using now.