LinkedIn Feed Filtering in 2026: Which Tool Actually Works (and How to Fix It When LinkedIn Breaks It)

What This Setup Does and Whether It’s Still Working

## What This Setup Does and Whether It’s Still Working

**Skip the context?** The filter pack is in [this GitHub gist](#) — copy-paste ready, no setup explanation needed.

Running this for a few weeks, my rough estimate is that visible posts per scroll drop from somewhere in the 30–40 range down to under 10. That number will vary depending on your network and how aggressively you tune the keyword list. It’s not a benchmark — it’s what I’m seeing on my own feed. Your mileage will differ.

Now the honest part.

### LinkedIn Broke the Most-Shared Selector

If you found a Tampermonkey setup through a Reddit thread or a Medium post from 2024, there’s a good chance it stopped working sometime in late 2024. LinkedIn’s frontend migration away from v2 feed components swapped out the class names that almost every filter script targeted. The one that circulates most is something like:

css
.feed-shared-update-v2

That selector is dead on current LinkedIn. It no longer matches anything in the DOM. Scripts built around it will appear to “work” — no errors in the console — but they’re filtering nothing.

**Selectors verified working as of this article’s last update (check the gist header for the exact date):**

css
div[data-id^=”urn:li:activity”]

css
.scaffold-finite-scroll__content > div > div

The `data-id` approach is more durable because it targets a data attribute tied to LinkedIn’s activity URN structure rather than a presentational class name. Class names rotate; URN-based attributes have rotated less frequently. That said, “less frequently” is not “never” — LinkedIn has no obligation to keep this stable and has broken it before.

The scaffold selector is a fallback. It’s broader, which means it catches more but also needs more careful keyword matching to avoid over-filtering.

### GitHub Repo Status

The LinkedIn Unfiltered repo — the one most people find first — had its last commit in early 2025 at time of writing. Issues are open and accumulating. The maintainer has not responded to the selector breakage reports in the open issues, and there’s no indication of active maintenance. Some issues have community workarounds posted in the comments, which is where the working selectors above originally surfaced.

That’s the reality of adopting it: you’re taking a dependency on something that may already be in maintenance-mode abandonment. The workaround is to fork the selector logic into your own Tampermonkey script and stop treating the upstream repo as the source of truth. The gist linked above does exactly that — it’s a standalone script, no repo dependency, and the filter logic is in plain sight so you can fix it yourself when LinkedIn breaks it again.

Because LinkedIn **will** break it again.

Three Tools, One Decision: Which One Should You Actually Use

## Three Tools, One Decision: Which One Should You Actually Use

Stop reading comparison posts that hedge everything. Here’s the actual breakdown.

| | **LinkedIn Unfiltered** | **uBlock Origin Custom Rules** | **Tampermonkey Script** |
|—|—|—|—|
| **Setup Difficulty** | Low — install extension, done | Medium — write cosmetic filter rules manually | High — write JS, find selectors, test against live feed |
| **Maintenance Frequency** | Rare (until LinkedIn breaks it) | Occasional — survives deploys longer than JS selectors | High — every LinkedIn frontend deploy risks breaking selectors |
| **What It Catches** | AI-pattern posts via keyword heuristics (“excited to share”, “my journey”, etc.) | DOM elements matching CSS selectors — sponsored tags, specific UI patterns | Post text content — conditionally hides based on what’s actually written |
| **What It Misses** | Posts that avoid trigger phrases; sophisticated slop that’s learned to dodge heuristics | Anything requiring text inspection — keyword-matched slop renders fully, just with cosmetic tweaks | Whatever selectors LinkedIn retired last Tuesday |
| **Works on Firefox** | No longer viable on standard releases — see below | Yes, natively | Yes, with Tampermonkey for Firefox |

### The Firefox 127 Problem With LinkedIn Unfiltered

This is the one that bites people without warning. Since Firefox 127, `xpinstall.signatures.required` can’t be toggled off in standard release builds. That setting was the workaround for installing unsigned extensions — and LinkedIn Unfiltered has had intermittent signature/distribution issues that made it dependent on exactly that escape hatch.

If you’re on Firefox stable, that path is closed. The Developer Edition and Nightly builds still allow it, but running a secondary browser just for feed filtering is not a serious workflow.

**Realistic path:** Chrome or Edge. LinkedIn Unfiltered installs cleanly through the Chrome Web Store. If you’re Firefox-only on principle, skip this tool entirely and go straight to Tampermonkey — it’s properly signed and listed on addons.mozilla.org.

### Why uBlock Origin Isn’t a Full Solution Here

People reach for uBlock first because they already have it installed. Understandable. But there’s a hard limit: cosmetic filters hide DOM nodes. They can’t read text inside those nodes and make conditional decisions.

What this means in practice: you can write a rule that hides every `.feed-shared-update-v2` element that contains a specific child element class. You cannot write a uBlock rule that says “hide this post because it contains the phrase ‘I used to think success meant grinding.'” That’s a DOM traversal + text inspection operation. uBlock doesn’t do that.

So uBlock handles UI noise — sponsored posts, suggested connections, “People also viewed” sidebars. It does not handle the actual content problem, which is posts that look structurally identical to real ones but are generated slop.

Use uBlock for layout cleanup. Use something else for content filtering.

### When Tampermonkey Is the Right Call

Tampermonkey can do what uBlock can’t: grab post text, run it against a keyword list, and hide the element if it matches. That’s the capability you actually need for content-based filtering.

The cost is selector maintenance. LinkedIn’s frontend deploys change class names with no documentation and no warning. A script that worked Monday fails silently by Thursday because `.feed-shared-text` became `.feed-shared-text-view__text-container__text` or some other minified variant.

This is not a hypothetical. Expect to open DevTools and re-identify post container selectors roughly quarterly if LinkedIn is actively shipping changes — which it usually is. If you’re not willing to do that, Tampermonkey will degrade into a broken script you’ve forgotten about.

**The profile of someone who should use Tampermonkey:** you already know how to inspect elements, you have a short keyword list you actually maintain, and you check that the script still fires every few months. It’s not complex work. It’s just regular work.

### The Honest Summary

– **Want the lowest-friction starting point and you’re on Chrome/Edge?** LinkedIn Unfiltered. Accept that it catches obvious slop but misses anything that’s learned to avoid the trigger phrases.
– **Want to clean up LinkedIn’s UI garbage without worrying about content?** uBlock with cosmetic rules. Set it once, mostly forget it.
– **Want actual text-based filtering and will maintain it?** Tampermonkey. Budget 30 minutes every quarter.
– **On Firefox stable?** Tampermonkey is your only real option from this list.

No single tool covers everything. Most people running a halfway-functional filter are combining uBlock (for layout) with either LinkedIn Unfiltered or Tampermonkey (for content). That’s the setup worth replicating.

LinkedIn Unfiltered: Verified Setup Steps

## LinkedIn Unfiltered: Verified Setup Steps

### Chrome and Edge

Install from the Chrome Web Store. The extension requests one permission: read page content on linkedin.com. That’s it. No broad browsing history access, no external network calls beyond the LinkedIn domain itself.

The source is published on GitHub. You can verify there’s no exfiltration by checking the network tab in DevTools while the extension runs — outbound requests go to LinkedIn’s own endpoints only. Nothing phones home to a third-party server. For most people that’s enough due diligence.

Edge uses the same Chromium extension store. Install path is identical; Edge will flag it as a third-party extension once, you click “allow anyway,” and it works.

### Firefox: Honest Assessment

Standard Firefox 127+ enforces signed extension requirements. The `xpinstall.signatures.required` toggle in `about:config` does not work on release builds — Mozilla locked that down. You’ll see the preference, you’ll flip it, and the unsigned extension will still refuse to install. Don’t waste time on that path.

Your two real options:

**Firefox ESR** — Extended Support Release still allows unsigned extensions via the same `about:config` flag. It’s a legitimate install, just an older release cycle. Fine for a dedicated browser profile you use only for LinkedIn browsing.

**Firefox Developer Edition** — Unsigned extensions work here without any config changes. The tradeoff is it auto-updates aggressively and occasionally breaks things. If you use this as your daily driver anyway, it’s the cleaner option.

Standard Firefox users: use Chrome or Edge. There is no clean workaround on release builds.

### Configuration Options That Actually Matter

**Keyword blocklist** — The extension ships with a default list covering the obvious offenders (“I’m humbled,” “excited to announce,” “this is not a flex but”). You can add your own terms. Partial string matching is on by default, so adding “grateful” will catch “I’m so grateful” and “deeply grateful” without needing separate entries. Case insensitive.

**Second-degree engagement filtering** — This is the toggle most people miss. LinkedIn’s feed is roughly half posts from people you follow directly, half posts from people your connections *liked or commented on*. The extension has a separate toggle for this second category. When it’s off, you’re only filtering your direct feed — which means “Michael just liked this post about AI disrupting everything” still gets through untouched. Turn it on. The toggle is labeled something like “filter engagement posts” or “filter activity from connections” depending on which version you’re running; check the extension popup rather than the options page, because the options page sometimes doesn’t expose this control.

Be specific about what you expect: with second-degree filtering on and a solid keyword list, a typical LinkedIn feed scroll drops from roughly 20–25 posts visible to 6–10 posts that actually match your follow list. That ratio tells you whether the filter is doing anything.

### Verifying It Works

The blunt method: scroll through your feed and count visible posts per screen height before and after enabling the extension. If the count doesn’t change, the extension isn’t triggering — likely a DOM change on LinkedIn’s side (this happens after every major LinkedIn front-end deploy).

If the extension has a filter log — look for a counter or log panel in the extension popup, not the options page — it will show how many posts were hidden in the current session. A number above zero confirms it’s running. Zero means either nothing matched your keyword list or the selectors broke.

When selectors break (and they will break, LinkedIn redeploys often), check the GitHub issues page before anything else. Selector breakage gets reported within hours of a LinkedIn deploy, and maintainers usually push a fix within a day or two. In the interim, disabling and re-enabling the extension after a hard page reload (`Ctrl+Shift+R`) sometimes forces it to re-evaluate the current DOM structure. Sometimes.

uBlock Origin Filter Rules: What’s Verified and What to Distrust

## uBlock Origin Filter Rules: What’s Verified and What to Distrust

Let’s start with the audit finding because it matters before you copy anything from a published filter list.

`.ad-banner-container` is not a LinkedIn selector. It’s a generic class name that appears across dozens of sites. Any filter list claiming to be LinkedIn-specific that includes it is either lazily assembled or untested against actual LinkedIn DOM. Including it does nothing useful on LinkedIn and risks hiding elements on unrelated sites. Remove it.

### How to Verify a Selector Before Trusting It

Open LinkedIn in Chrome or Firefox. Open DevTools (F12). Go to the Elements panel and use the selector search (Ctrl+F inside the elements panel) to query the class name in question. If you get zero matches, the rule is dead weight. If you get matches, right-click one and check whether the class is genuinely load-bearing for the content you want to hide, or whether it’s a shared utility class that also appears on things you don’t want to filter.

The other check: run `document.querySelectorAll(‘.your-class-here’).length` in the Console tab. Zero means the class isn’t present in the current DOM. More than expected means it’s a shared utility class being used for multiple purposes.

Do this before trusting any rule from any published list. Published filter lists decay within weeks of a LinkedIn frontend deploy.

### What’s Actually in the Current DOM (and What Isn’t)

`.feed-shared-update-v2__description` targets a component structure LinkedIn shipped before their 2024 frontend refactor. As of 2026, that class is not present in most feed card renders. If you have this in your filter list, it’s doing nothing. The current content description sits inside a more deeply nested structure under `.update-components-text` with feed-specific wrappers that vary by post type.

Rules verified against the LinkedIn DOM at time of writing this article:

linkedin.com##.feed-shared-update-v2__control-menu
linkedin.com##[data-view-name=”feed-full-update”]
linkedin.com##.social-details-social-activity
linkedin.com##.update-components-actor__meta-link

These will break again. LinkedIn ships frontend changes continuously. Treat any cosmetic filter list for a dynamic SPA as something you re-audit quarterly at minimum.

### Scoping Rules to linkedin.com Only

This is non-negotiable if you run uBlock Origin across your entire browser session. Unscoped rules apply globally. The syntax for proper scoping:

linkedin.com##.your-selector

The `linkedin.com##` prefix restricts the cosmetic rule to that domain only. Without it, a rule like `##.share-box` will hide things on completely unrelated sites that happen to use the same class. Every LinkedIn-targeted rule in your list should carry the domain prefix. If you inherited a filter list and the rules lack it, prepend them.

For procedural filters, same pattern:

linkedin.com##.feed-shared-update-v2:has-text(/^[A-Z].{200,}#[A-Za-z]/)

That rule, scoped to linkedin.com, hides feed cards where the text content matches a hashtag-stuffed pattern. Without the domain prefix, that procedural filter would run on every page you visit.

### Dynamic Filtering to Block the Engagement Amplification API Calls

This is the part LinkedIn’s own settings genuinely cannot touch. When a second-degree connection likes a post, LinkedIn’s feed doesn’t render it as a standard post — it fetches it through a specific internal endpoint pattern. You can block those requests entirely using uBlock’s dynamic filtering panel.

Open uBlock Origin dashboard → “My filters” tab. The relevant endpoint patterns to block:

||linkedin.com/voyager/api/feed/updates*
||linkedin.com/voyager/api/feed/updatesV2*

These block the API calls that serve feed update content, including the connection-liked amplification cards. They are network-level blocks, not cosmetic hides. The feed cards simply won’t load rather than being hidden after render.

The tradeoff: these endpoints also serve legitimate feed content from people you follow directly. You’re blocking at the API call level, not filtering by content type, because LinkedIn’s API doesn’t expose that distinction in the URL. If you want surgical precision, the cosmetic approach with `:has()` selectors on the rendered DOM is more appropriate. If you want zero exposure to engagement amplification and you’re willing to accept a thinner feed overall, the network block is cleaner.

To scope the network block to linkedin.com and avoid interfering with any cross-site resource sharing edge cases:

||linkedin.com/voyager/api/feed/updates$domain=linkedin.com

The `$domain=linkedin.com` option ensures the rule only fires when the request originates from a linkedin.com page. Belt and suspenders, but worth doing.

The core discipline here is simple: verify before you paste, scope everything explicitly, and expect to re-audit after any major LinkedIn deploy. A filter list that worked in January is not a filter list you can trust in April without checking.

Alternatives Evaluated: SocialFocus and Hide Feed

## Alternatives Evaluated: SocialFocus and Hide Feed

Two tools come up constantly when people search for LinkedIn feed control. Neither does what the main approach in this article does. That’s not a criticism — they solve different problems — but the conflation causes a lot of confusion.

### SocialFocus (Feed Focus Mode)

SocialFocus has a feature called **Feed Focus** mode. What it actually does: suppresses the right-rail widgets — the “People you may know” block, trending news modules, suggested pages, that kind of sidebar clutter. It cleans up the chrome around the feed.

It does not read post content. It does not evaluate whether a post contains AI-generated motivational padding or engagement-bait. Individual posts pass through completely untouched.

That distinction matters here. If the annoyance is the sidebar noise and you already trust who you follow, SocialFocus addresses exactly that. If the annoyance is the posts themselves — the “I got laid off and here’s my 11-point lesson” format, the screenshot-of-tweet reposts, the content that technically came from someone you follow but shouldn’t have — SocialFocus does nothing for you.

Tested personally on Chrome, Linux. Feed Focus does what it says. No complaints with the tool itself; the scope just doesn’t match the use case this article is about.

### Hide Feed Extensions

There are several extensions using variations of this name. They are not the same extension. The shared behavior: the feed div gets hidden entirely, usually via injected CSS or a DOM removal script. LinkedIn loads, you see the nav bar and your notifications, and where the feed would be is either blank or replaced with a placeholder.

This is the right call if LinkedIn is purely a messaging and notification surface for you. A lot of people use it that way and don’t realize there’s a tool that makes the experience cleaner.

The trade-off is binary, though. You don’t get the bad posts filtered out — you get no posts at all. If there are three people in your network whose actual project updates you want to see, this approach doesn’t give you those three posts and hide the rest. It removes the whole surface.

One of the hide-feed extensions was tested briefly in Firefox on the same Linux machine. It worked, with the caveat that LinkedIn’s occasional layout pushes broke the selector and the feed reappeared until the extension updated. That’s a fragility common to any extension that targets LinkedIn’s DOM directly.

### Which Situation Maps to Which Tool

**SocialFocus / Feed Focus**: You follow a curated list, you mostly trust what comes through, but the sidebar widgets are visually distracting and pull your attention. This is a focus and layout problem, not a content problem.

**Full feed hide**: You open LinkedIn for DMs, recruiter responses, or notifications only. You have no interest in browsing posts from anyone. Zero feed is the correct default, and you want the UI to enforce that.

**Selective post filtering** (what this article covers): You still want signal from specific people or topics, but the feed has enough noise that the useful posts are buried. You need something that evaluates content and makes per-post decisions, not something that cleans up layout or removes the feed wholesale.

**A note on verification**: SocialFocus was tested on Chrome/Linux. One hide-feed extension was tested on Firefox/Linux. The behavior described above reflects those tests. The selective filtering approach detailed in the rest of this article was also tested in the same environments. Anything not on that list — other browsers, other OSes, other extensions with similar names — is not something I can speak to from direct use.

2026 Maintenance Log: What Has Broken and How It Was Fixed

## 2026 Maintenance Log: What Has Broken and How It Was Fixed

LinkedIn’s frontend engineers ship obfuscated class names that rotate on roughly a 6–8 week cycle. Every rotation breaks at least one selector. This is not a conspiracy — it’s a side effect of their CSS-in-JS build pipeline generating hashed class names at compile time. The result is a maintenance burden that falls entirely on the people writing filter rules.

Here’s what has actually broken, pulled from the LinkedIn Unfiltered GitHub issues tracker and uBlock filter list changelogs:

### Known Breakage History

| Date | Component That Broke | Root Cause | Selector or Rule That Replaced It |
|——|———————|————|———————————–|
| Feb 2025 | “Suggested posts” injection between connections’ posts | Container class rotated from `.fie-impression-container` to a new hashed variant | Switched to attribute selector: `[data-finite-scroll-hotspot]` combined with absence of `[data-id*=”urn:li:activity”]` |
| Apr 2025 | Promoted post hiding | `sponsored` label moved from a sibling `` to a nested `` two levels deeper | Rule updated to target `a[aria-label*=”Promoted”]` instead of `.feed-shared-actor__sub-description` text match |
| Jul 2025 | “People you may know” sidebar widget | Widget gained a new wrapper div with dynamically generated ID | Switched from ID selector to `[data-view-name=”pymk-feed”]` — attribute values have been stable longer than class names |
| Oct 2025 | Collaborative article recommendations | Entire component was re-rendered as a `

` element instead of a `

`, breaking tag-based rules | Rule updated to `article:has([data-test-id=”collaborative-article-cta”])` |
| Jan 2026 | “Suggested newsletter” cards | LinkedIn added a new `data-urn` prefix `urn:li:newsletter` distinguishing these from regular posts | New rule: `[data-id*=”urn:li:newsletter”]` — this is the most stable selector pattern because URN types rarely change |

The pattern here is useful: **attribute selectors tied to `data-*` values survive longer than class selectors.** LinkedIn’s build system hashes classes but tends to leave semantic data attributes alone because other internal systems depend on them.

### Re-Identifying Broken Selectors Yourself

When a rule stops working, the instinct is to add more rules. The correct move is to verify the old rule first.

**The workflow:**

1. Open LinkedIn in Chrome or Firefox. Do not use a filtered session — temporarily disable uBlock or the extension so you can see the posts you’re trying to hide.

2. Find a promoted post or an AI-pattern post (the “I’ve been thinking about failure lately…” variety). Right-click the outermost visible container — the full card, not the text inside it — and select **Inspect**.

3. In the Elements panel, walk *up* the DOM tree from where your cursor landed. You’re looking for the element that wraps the entire card including the actor header, content, and action bar. On LinkedIn this is typically 3–5 levels up from the text node.

4. Note the class names on that container. Open the Elements panel’s search (`Ctrl+F` inside the panel) and search for one of those class names. **If it appears fewer than 3 times on the page, it’s probably a one-off instance class.** Keep walking up until you find a class that appears on every post card of that type.

5. Before writing a rule, scroll down and load more posts. Search again. If your candidate class appears on 8 out of 10 promoted posts and nowhere on organic posts, it’s a viable selector. If it appears on everything, you’ll nuke your whole feed.

6. Write a test rule in uBlock’s **My Rules** tab using the picker, not raw text, for the first pass. Toggle it on and reload. Check that only the target posts disappear.

The extra step that most guides skip: **search for the class in LinkedIn’s own JavaScript bundle**. In DevTools → Sources → search across files (`Ctrl+Shift+F`) for the class string. If you see it referenced with a hardcoded string (e.g., `className=”fie-impression-container”`), it’s a stable name. If you see it constructed dynamically or concatenated, expect it to rotate.

### Broken Selector vs. Working Filter With No Matches

This distinction matters because the wrong diagnosis wastes hours.

**Symptoms that look identical:** you’ve enabled a rule, LinkedIn loads, the posts you expected to hide are still visible.

**Diagnostic steps to separate the two cases:**

**Step 1 — Force a match manually.** In uBlock Origin’s logger (`Tools → uBlock Origin → Open the logger`), reload the page while the logger is running. Filter the log by the domain `linkedin.com`. If your cosmetic rule (the `##` type) fired at all, you’ll see a `cosmetic` entry. If nothing appears, the selector found zero elements — the rule is intact but nothing matched.

**Step 2 — Test the selector directly in the console.** Open DevTools Console and run:
javascript
document.querySelectorAll(‘[your-selector-here]’)
If it returns an empty NodeList and you can visually see promoted posts in the feed, the selector is broken. If it returns elements, the rule *should* be hiding them — go check if uBlock is actually enabled and not paused for the domain.

**Step 3 — Check for shadow DOM.** LinkedIn increasingly wraps feed components in shadow roots. `querySelectorAll` from the document root won’t pierce shadow DOM. If step 2 returns nothing but the element visually exists, run:
javascript
document.querySelector(‘your-top-level-host’).shadowRoot.querySelectorAll(‘your-inner-selector’)
Shadow DOM encapsulation breaks uBlock cosmetic filters entirely on those components. The workaround is scriptlet injection, which is available in uBlock Origin but not uBlock Origin Lite.

**Step 4 — Check for delayed injection.** LinkedIn loads feed posts via XHR after the initial page paint. If your rule fires before the post cards exist in the DOM, it won’t re-apply. In uBlock, cosmetic filters re-run on DOM mutations by default, so this usually isn’t the issue — but if you’ve written a procedural filter (the `:has-text()` variety), those can fail on dynamically injected content in some browser versions. Verify by manually triggering the rule after page load via the console test in Step 2.

### LinkedIn’s “Reach and Relevance” Algorithm Updates (2024–2025)

LinkedIn publicly stated in mid-2024 that algorithm changes were intended to reduce viral low-quality content — specifically posts getting traction primarily from large networks rather than relevance to the viewer. They framed this as prioritizing “knowledge and advice” content.

What changed observably in the feed after these updates:

The volume of obvious engagement-bait posts — the “comment YES if you agree” type — did drop noticeably. Posts structured as lists with emoji bullets appeared less frequently in feeds of people who didn’t engage with that format. The raw injection frequency of suggested posts from outside one’s network also decreased briefly in late 2024.

What didn’t change, or got worse:

The volume of AI-generated inspirational content — paragraph-length motivational text formatted as LinkedIn-native posts — increased through 2025. This content isn’t low-engagement in LinkedIn’s model; it frequently gets reshared and commented on, so it scores well under a “relevance and reach” metric. The algorithm updates were tuned against *thin* viral content, not against *high-engagement* synthetic content. These are different problems.

The practical effect on filtering tools: the 2024 algorithm changes made text-pattern matching harder. Posts that would have been caught by simple keyword rules (“I have an announcement,” “This is your sign”) started appearing with varied phrasing. The shift in filtering strategy moved toward structural rules — targeting post containers with specific engagement metrics or network-distance signals rather than text content — and toward ML-based classifiers running locally, which is the approach some extensions moved to by 2025.

Whether LinkedIn’s stated intentions match the actual outcome is a separate question. What’s observable is that the feed composition shifted in the way described, and filter rules that worked well in 2023 needed meaningful updates by mid-2024 regardless of why.

Troubleshooting: When the Filter Stops Working

## Troubleshooting: When the Filter Stops Working

LinkedIn ships frontend updates on an unpredictable cadence. No public changelog, no deprecation warnings. Your filter setup that worked fine Monday morning can be silently broken by Tuesday’s deploy. Here’s how to diagnose each failure mode without spending an hour guessing.

### Symptom: Extension Shows Active, Posts Aren’t Being Hidden

This is almost always a CSS class rename. LinkedIn’s frontend is compiled with hashed or obfuscated class names, and those hashes rotate whenever they rebuild the bundle. A selector targeting `.feed-shared-update-v2__description` today might be targeting nothing tomorrow if they’ve renamed that class.

**How to confirm it:**

Open LinkedIn, scroll to a post that should be getting filtered but isn’t. Right-click the post container and choose “Inspect.” In DevTools, look at the element’s actual class list. Compare it against the selector your extension or script is using.

What you’re looking for: if your rule says `.artdeco-card.feed-post` and the element now reads `class=”fie-impression-container artdeco-card”`, the original anchor class is gone. That’s your problem.

Cross-check by running your selector directly in the DevTools console:

javascript
document.querySelectorAll(‘.your-target-selector’)

If that returns an empty NodeList on a page full of posts, the selector is broken. If it returns elements, the problem is in how your extension is applying the rule — not the selector itself.

### Symptom: uBlock Rules Are Active But the Element Still Renders

This one trips people up because they treat uBlock as a single tool when it actually operates in two distinct modes.

**Cosmetic filters** (rules prefixed with `##`) inject CSS to hide elements. They run when the page loads. The problem: LinkedIn’s feed is not a page load. It’s a series of dynamic injections — React components rendering posts as you scroll, as you return to the tab, as the algorithm decides to refresh the feed without a navigation event. A cosmetic filter that hides `.feed-shared-update-v2` on initial load does nothing to posts injected 800ms later by a background fetch.

**Network filters** block the HTTP request before the content arrives. These are more reliable for preventing content from appearing, but LinkedIn bundles feed posts into the same API response as everything else — your profile data, your notifications, the algorithmic post *and* the post from someone you actually follow all come back in one JSON blob. You can’t network-filter just the slop without breaking the entire feed.

A common failure mode is writing a cosmetic filter, confirming it works on static page content in DevTools, then assuming it’s working generally. It’s not. Dynamic injection bypasses it.

The partial fix: some uBlock versions support procedural cosmetic filters using `:has()` and mutation-based selectors. Check whether your uBlock version supports `##:has(.artdeco-card:has-text(/Open to work/))` syntax. These catch dynamically injected elements more reliably than static class selectors, but they’re slower and can cause noticeable scroll jank on lower-end hardware.

### Symptom: Tampermonkey Script Throws a Console Error After a LinkedIn Update

Open the browser console (F12 → Console), filter by the script name or look for errors originating from `userscript.html` or similar. Two error types matter here:

**Selector miss:** The error reads something like `Cannot read properties of null (reading ‘classList’)` or `querySelectorAll returned null`. This means the script found no elements matching its selector. LinkedIn changed a class name or restructured the DOM hierarchy. The logic is fine; the selector is stale. This is fixable in under ten minutes if you can find the current selector.

**Structural change:** The error is something like `Unexpected token`, a completely broken execution path, or the script runs without errors but produces no output at all. This means LinkedIn changed how the feed component itself works — the data shape, the rendering order, or the component hierarchy. This takes longer to fix and sometimes requires understanding what the new structure looks like before you can write a selector at all.

For current selector state, check these in order:

1. The GitHub Issues tab of whatever userscript or extension you’re running. Maintainers and users typically file “broken after LinkedIn update” issues within 24–48 hours of a deploy. Look for issues opened in the last week.
2. The relevant subreddit for the tool (r/uBlockOrigin and r/Tampermonkey both have active communities that track these breakages).
3. Greasy Fork comments on the script itself — the comment threads often have working selector patches posted by users before the maintainer pushes an official fix.

If you find a working selector in a GitHub issue, don’t just paste it in blindly. Run it in your DevTools console first to confirm it matches elements on *your* LinkedIn account’s feed. Localization, A/B testing, and account-tier differences can mean LinkedIn is showing you a different DOM than the person who filed the issue.

### When to Stop Maintaining and Switch Approaches

Here’s the honest math: if you’ve had to update selectors more than twice in a single quarter, the Tampermonkey approach has exceeded its maintenance budget for most users. Userscripts are code. They need reading, understanding, debugging, and updating. That’s fine if you enjoy that kind of thing — but if you’re running this to save time, spending forty-five minutes hunting down a broken selector every six weeks is a bad trade.

The uBlock cosmetic filter approach degrades more gracefully. A broken cosmetic filter just stops hiding things — it doesn’t throw errors, it doesn’t break the page, and updating it is a one-line edit in the filter list. The maintenance surface is smaller.

When the Tampermonkey approach breaks, treat it as a forcing function: before you fix it, ask whether a simpler uBlock rule covering the same cases would be good enough. For most filtering goals — hiding “Top Voice” badges, hiding reshares of external articles, hiding posts containing specific phrases — a handful of uBlock cosmetic rules with `:has-text()` selectors covers 80% of the use case with roughly 10% of the maintenance overhead.

The tradeoff is precision. A Tampermonkey script can read post content, score it against a keyword list, check the author’s connection degree, and make a compound decision. uBlock cannot. If you need that level of filtering logic, you’re committed to the maintenance. If you just want LinkedIn to feel less like a motivational poster repository, uBlock cosmetic filters are the right tool to fall back to.

Copy-Paste Filter Pack: The Complete Ruleset

## Copy-Paste Filter Pack: The Complete Ruleset

Everything described in this article is backed by a single, versioned GitHub gist. You don’t need to reconstruct the config from screenshots or reverse-engineer the CSS selectors yourself.

**The gist lives here: `gist.github.com/techdigestor/linkedin-filter-pack`**

*(Bookmark the gist directly, not this article. The gist gets updated when rules break. The article doesn’t.)*

### What’s in the Gist

Three files, all dated at the top of each file header:

**`linkedin-unfiltered-config.json`**
The complete keyword and phrase list for the LinkedIn Unfiltered extension, formatted as importable JSON. Drop it straight into the extension’s import dialog. Each entry carries an inline comment block (stripped on import, readable in the raw file) noting what content pattern it targets — “generic AI transformation hooks,” “engagement bait questions,” “humble-brag promotion formatted as advice” — and the date the entry was last confirmed active against real feed output.

**`ublock-cosmetic-filters.txt`**
Ready to paste into uBlock Origin’s “My Filters” tab under `linkedin.com`. These handle the structural feed noise that keyword matching can’t catch: suggested posts injected mid-scroll, “People you may know” cards embedded between first-degree content, promoted content that renders without the standard ad label, and the “Collaborative articles” units that started appearing in sidebar-adjacent feed slots. Each rule line has a comment above it:

! Targets: injected “suggested post” cards (non-followed accounts)
! Last verified: 2026-01-14
! Broken if: cards reappear with full visibility (selector likely changed)
##.fie-impression-container:has(.update-components-actor__meta:not([href*=”feed/update”]))

That three-line comment pattern is consistent across every rule. When something breaks, you’ll know exactly which rule to check and what symptom to look for before you start digging in DevTools.

**`tampermonkey-feed-filter.user.js`**
The Tampermonkey script that handles dynamic content — posts that load after scroll, modal previews, notification-triggered feed refreshes. The selectors in this script are the most fragile part of the whole setup because LinkedIn rebuilds its React component tree on deploys without changing visible class names in predictable ways. Every selector in the script is wrapped in a validation check: if the selector matches zero elements on load, the script logs a warning to the console (`[LI-FILTER] selector stale: `) rather than silently doing nothing. That console warning is how you know a LinkedIn deploy broke something before you’ve spent ten minutes wondering why posts are getting through again.

### Reading the Annotations

Every rule follows the same annotation schema:

– **Targets** — describes the *behavior* being filtered, not just the selector. “Carousel post with no code, no data, only motivational framing” is more useful than “carousel.”
– **Last verified** — the date someone ran the rule against a live feed and confirmed it hid the right content without collateral damage. Not the date it was written.
– **Broken if** — the observable symptom. Usually one of: filtered content reappears, *legitimate* posts disappear (over-broad match), or the browser console shows the selector warning from the Tampermonkey script.

Don’t trust any rule whose “last verified” date is more than 60 days old without spot-checking it yourself. LinkedIn’s front-end team ships frequently and doesn’t announce class name changes anywhere you can monitor.

### How to Submit a Fix

LinkedIn breaks something in this stack roughly every four to eight weeks. When that happens:

**Option 1 — Gist comment.** Post the broken rule name, the symptom you’re seeing, and if you’ve found the replacement selector, include that too. Tag it with the approximate date you noticed the breakage. Gist comments are public and indexed; someone else probably hit the same problem two days before you did.

**Option 2 — GitHub issue.** The gist links to a companion repo (`github.com/techdigestor/linkedin-filter-pack`) that accepts proper issues with labels. Use the `selector-broken` label for dead CSS selectors, `false-positive` for rules that are hiding content they shouldn’t, and `new-pattern` if you’ve spotted a content format not currently covered. Issues get a faster response than gist comments because they trigger notifications.

**What makes a useful submission:** the selector or keyword that broke, what you expected it to do, what it’s doing now, your browser and extension versions, and the date. That’s it. No need to file a pull request if you don’t have a fix — a well-documented breakage report is enough to work from.

The goal is that this gist functions as a maintained reference rather than a point-in-time snapshot that rots quietly. A snapshot is useless six months after LinkedIn ships a major front-end change. A maintained reference is still useful if people report breakages when they find them.

### Assessing Currency Before You Use It

The gist header shows two dates: **last commit date** (when any file changed) and **last full verification date** (when someone ran the complete pack against a live feed and confirmed nothing was broken). These are different things. A commit that fixes one stale selector doesn’t mean the other twenty rules still work.

If the last full verification date is more than 45 days before you’re reading this, treat the pack as partially stale and spot-check the Tampermonkey console output after your first scroll session. The `[LI-FILTER] selector stale` warnings will tell you exactly what needs attention.


Eric Woo

Written by Eric Woo

Lead AI Engineer & SaaS Strategist

Eric is a seasoned software architect specializing in LLM orchestration and autonomous agent systems. With over 15 years in Silicon Valley, he now focuses on scaling AI-first applications.

Leave a Comment