Hide the LinkedIn feed. Keep everything else. Enter the Void.
VoidIn is a lightweight, privacy-focused Manifest V3 extension for Chrome and Firefox. It completely hides the LinkedIn main feed stream while leaving the rest of the layout β top navigation, messaging, notifications, left and right sidebars, and job search β fully intact and functional.
- π« No feed. The endless scroll, the suggested posts, the recruiter spam β gone.
- π§ Navigation intact. Top nav, My Network, Jobs, Messaging, your profile, search β all still work.
- π No tracking. No analytics. No remote config. The extension never
makes a network request. State is stored locally in
chrome.storage.sync. - π Zero dependencies. Pure vanilla HTML, CSS, and JavaScript. No frameworks, no build step, no transpilation. You can audit the entire codebase in five minutes.
- Open
chrome://extensions(oredge://extensions,brave://extensions). - Toggle Developer mode on (top-right).
- Click Load unpacked.
- Select the
VoidIn/directory (the one containingmanifest.json). - The VoidIn icon appears in your toolbar. Click the puzzle icon and pin VoidIn so it stays visible.
- Open
about:debugging#/runtime/this-firefox. - Click Load Temporary Add-onβ¦
- Select the
manifest.jsonfile insideVoidIn/. - The extension is active until you restart Firefox. For permanent loading, the add-on must be signed through addons.mozilla.org.
There is no build step. Clone the repo, point your browser's "Load unpacked" at the cloned directory, and you're done:
git clone https://github.com/malinele/voidin.git
cd voidin
# Then load this directory as an unpacked extension- Navigate to
https://www.linkedin.com/feed/. - The feed is gone. The rest of the page (top nav, sidebars, messaging widget, search) is intact.
- Click the VoidIn icon in your toolbar to open the popup.
- Use the Enter the Void / Return to Chaos toggle to switch states.
- State is synced across your devices via
chrome.storage.sync(when signed into Chrome).
The extension only runs on LinkedIn's /, /feed, and /feed/ pages. On
/mynetwork/, /jobs/, /messaging/, /in/<user>/, and other pages,
nothing is hidden β only a single [VoidIn] feed_not_applicable log line
appears in the console for transparency.
LinkedIn rotates its obfuscated class names frequently. We don't rely on
specific class names. We use a multi-strategy, defense-in-depth approach
in content.js:
-
URL guard β
shouldHideOnThisPage()only proceeds on/,/feed,/feed/. Other pages are no-ops. This is critical because LinkedIn's/mynetwork/,/jobs/, and/messaging/pages share the same 3-column layout as/feed/but the central column is not the feed β it's "people you may know", "job listings", "message threads", etc. Hiding those by mistake makes the page go blank. -
Main column detection β
findMainColumn()tries 9 semantic selectors in priority order (main[role="main"],main,[role="main"],.scaffold-layout__main,[class*="scaffold-layout__main"], etc.). If none match, a structural fallback picks the middle child of.scaffold-layoutor.application-outlet. -
Feed root detection β
findFeedRoot()runs 4 strategies in order:- Walk up from known feed markers (
[data-view-name*="feed"],.feed-shared-update-v2, etc.) to the direct child ofmain. - Scan direct children of
mainfor nested feed markers. - Heuristic: if the first direct child of
mainhas 2+<article>/[role="article"]/[data-urn]descendants, treat it as the feed. - Last resort:
findLargestChildOfMain()β keeps diving into single-child wrappers that contain 2+<aside>elements (signals a layout container), up to 5 levels deep. Then picks the largest non-aside, non-layout-wrapper child.<section>elements get a+5000score boost because modern LinkedIn wraps the feed in a<section>between two<aside>rails.
- Walk up from known feed markers (
-
Hard guard in
hideFeed()β short-circuits if the candidate contains any<aside>descendants. This is the last line of defense against accidentally hiding a page layout container. Even if all strategies above return the wrong element, this guard prevents the catastrophic "hide everything" outcome. -
Recovery on init β
unHideAll()is called at the start ofinit()to undo any incorrect hide from a previous extension version. Refresh = automatic recovery. -
MutationObserver β throttled to 120 ms, watches
document.bodyfor child-list, class-attribute, anddata-view-name-attribute changes, and re-hides any feed chunks the SPA lazy-loads. -
CSS at
document_startβcontent.cssis injected atdocument_startso the feed never paints before the JavaScript kicks in. No flicker.
The DOM structure for the modern LinkedIn home feed (as of 2026) is:
main
ββ div (outer wrapper, ~370 descendants)
ββ div (layout container, 3 children)
ββ aside (left rail β navigation)
ββ section (THE FEED) β VoidIn hides this
ββ aside (right rail β recommendations)
The content script auto-dumps a JSON diagnostic to the console on init, after 1.5 s, and after 4 s. To read it:
- Open LinkedIn.
- Open DevTools (F12) β Console.
- In the filter input, type
VoidIn. - Find the line
[VoidIn vX.Y.Z] DIAGNOSTIC (auto-dumped on init β copy this back to me):. - Below it, there's a JSON block with
path,shouldHideOnThisPage,mainChildren,mainGrandchildren,mainGreatGrandchildren,feedRootFound,feedRootTag,feedRootClass,hiddenCount, and the full selector/match-result tables.
Note on MV3 isolated worlds: in Manifest V3, content scripts execute in
an isolated JavaScript world. Setting window.__voidin = fn inside
content.js does not make __voidin callable from the page's DevTools
console. The auto-dumped diagnostic is the way to inspect the extension
state from the console.
| Browser | Minimum version | Notes |
|---|---|---|
| Chrome | 105+ | Required for :has() selector support in content.css |
| Edge | 105+ | Same as Chrome (Chromium-based) |
| Brave | 105+ | Same as Chrome (Chromium-based) |
| Firefox | 121+ | Required for :has() selector support |
| Safari | not supported | Safari Web Extensions have a different API; PRs welcome |
Older browsers will still install the extension, but the CSS-level instant-hide path may not work and you'll see a brief flash of the feed before the JavaScript kicks in.
VoidIn is zero-network, zero-analytics, zero-telemetry.
- The extension makes no network requests of its own.
- It does not read, transmit, or store any LinkedIn page content.
- It persists only the on/off toggle in
chrome.storage.sync. - The only DOM access is reading the structure of
*.linkedin.comto find the feed; no content is read or stored. - No third-party scripts, no remote config, no update pings.
You can verify this yourself: open the extension's background page in
DevTools (chrome://extensions β VoidIn β "service worker" link) and watch
the network tab. The only requests will be to LinkedIn itself, not from
VoidIn.
See SECURITY.md for the full audit checklist and vulnerability reporting policy.
- Open the popup. Is the toggle on (lime green)?
- Open DevTools β Console. Filter by
[VoidIn]. Look for[VoidIn] feed_hiddenβ that's a successful hide. - If you see
[VoidIn] feed_not_applicable, you are not on a feed page (/,/feed/,/feed). VoidIn intentionally does not run on/mynetwork/,/jobs/, etc. - If you see
[VoidIn] error, copy the full JSON diagnostic and open an issue.
The page is blank (everything is hidden)
This is the worst-case scenario. As of v1.6.1 it should be impossible because of the URL guard, the aside guard, and the auto-recovery on init. If you do see it:
- Toggle the VoidIn popup to OFF. Everything reappears.
- If the toggle doesn't fix it, hard refresh the page (
Cmd+Shift+R/Ctrl+Shift+R). - If it's still broken, copy the diagnostic and open an issue.
LinkedIn rotates obfuscated class names frequently. The hide logic is designed to be resilient, but if LinkedIn makes a major structural change the hide will silently fail and you'll see the feed back. Open an issue with the diagnostic JSON β we can usually ship a fix within a day.
Some of the errors you'll see in your DevTools console are not from VoidIn. The most common ones:
chrome-extension://invalid/ ...β comes from a previously-uninstalled extension's content script that's still trying to load. Not us.Uncaught (in promise) Error: Minified React error #418β LinkedIn's own React code (hydration mismatch). Not us.Attribute 'exception.tags' of type 'object' could not be converted to a proto attribute.β LinkedIn's telemetry code. Not us.VIDEOJS: WARN: ...β LinkedIn's video player. Not us.
To see only VoidIn's output, filter the console by VoidIn. You should
see only:
[VoidIn] initialized Object
[VoidIn] feed_hidden Object
[VoidIn vX.Y.Z] debug helper installed β call __voidin() to inspect the DOM
[VoidIn vX.Y.Z] DIAGNOSTIC (auto-dumped on init β copy this back to me):
[VoidIn vX.Y.Z] DIAGNOSTIC (auto-dumped on init β copy this back to me):
[VoidIn vX.Y.Z] DIAGNOSTIC (auto-dumped on init β copy this back to me):
(__voidin is installed in the content script's isolated JavaScript world
and is not directly callable from the page's DevTools console. The
auto-dumped diagnostic is the source of truth.)
We welcome bug reports, feature requests, documentation improvements, and pull requests. See CONTRIBUTING.md for the dev setup, the selector/heuristic checklist, and the manual test matrix.
If you want to add a new feed-hiding strategy, follow the strategy pattern
in content.js: each strategy returns null (or the candidate) and never
throws. The findFeedRoot() function chains them in priority order.
See SECURITY.md for what VoidIn accesses, what it does not do, the permission set, and how to report a vulnerability.
MIT Β© 2026 Nenad Mitrovic. Use it, fork it, modify it, ship it.
See CHANGELOG.md for the full version history.
Built with care. Not affiliated with LinkedIn or Microsoft.