惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

G
GRAHAM CLULEY
T
Tenable Blog
Know Your Adversary
Know Your Adversary
C
CXSECURITY Database RSS Feed - CXSecurity.com
P
Privacy International News Feed
S
Security Affairs
NISL@THU
NISL@THU
O
OpenAI News
Attack and Defense Labs
Attack and Defense Labs
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Hacker News: Ask HN
Hacker News: Ask HN
Webroot Blog
Webroot Blog
Schneier on Security
Schneier on Security
S
SegmentFault 最新的问题
S
Schneier on Security
G
Google Developers Blog
V
V2EX
C
Check Point Blog
U
Unit 42
Google DeepMind News
Google DeepMind News
T
Threatpost
阮一峰的网络日志
阮一峰的网络日志
T
The Exploit Database - CXSecurity.com
Recent Announcements
Recent Announcements
M
MIT News - Artificial intelligence
S
Secure Thoughts
博客园 - 司徒正美
Recorded Future
Recorded Future
P
Proofpoint News Feed
Spread Privacy
Spread Privacy
K
Kaspersky official blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
AI
AI
博客园 - 聂微东
N
News and Events Feed by Topic
SecWiki News
SecWiki News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
V
Vulnerabilities – Threatpost
P
Palo Alto Networks Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Engineering at Meta
Engineering at Meta
Recent Commits to openclaw:main
Recent Commits to openclaw:main
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
酷 壳 – CoolShell
酷 壳 – CoolShell
WordPress大学
WordPress大学
The Hacker News
The Hacker News
The Last Watchdog
The Last Watchdog
Project Zero
Project Zero
W
WeLiveSecurity
博客园 - Franky

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Learning the Web Platform APIs As If You Built Them Yourself
Mohamed Idri · 2026-05-17 · via DEV Community

If you have ever reached for a library to do something the browser already does, you have met the gap this post is about. Need a debounce? You wrote a useEffect. Need to detect when a card scrolls into view? You added a 300 line library. Need offline support? You shrugged and gave up.

The browser has changed. Modern browsers ship a generous, capable platform. Most of the libraries we still install in 2026 were written when the platform was missing the feature. They are not missing anymore.

A senior frontend engineer knows what the browser already gives them, and reaches for it before reaching for npm.

That is the gap this post fills.

What is the web platform, really

Think of the browser as a small operating system that runs in a tab. It has storage, a network stack, threads, scheduling, sensors, sometimes even a file system and Bluetooth. Each capability has an API. Many of them are excellent. Many of them are five lines of code instead of a 12KB dependency.

Two ideas drive the whole thing:

  • The platform is doing more than you think. Capabilities ship every six weeks across all the major browsers.
  • Promises and observers are the shape. Most modern APIs are either awaitable or "watch this thing and call me back".

That is the whole vibe.

Let's pretend we are building one

We are not building the platform. We are doing a senior level tour of it. For the running example, we will sprinkle a tiny read later notes app with native APIs as we go. Save notes offline, fetch articles, observe the page, schedule work, broadcast across tabs.

API 1: fetch (and AbortController), the network everyone forgets has features

Every frontend engineer knows fetch. Half of them only use 20% of it.

const res = await fetch("/api/notes", {
  method:      "POST",
  headers:     { "Content-Type": "application/json", Accept: "application/json" },
  body:        JSON.stringify({ title: "Read later" }),
  credentials: "include",        // send cookies on cross origin requests
  cache:       "no-cache",       // override default
  mode:        "cors",           // also "same-origin", "no-cors"
  signal:      ctrl.signal,      // AbortController
});

if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();

Enter fullscreen mode Exit fullscreen mode

The features senior engineers actually use:

AbortController for cancelling

const ctrl = new AbortController();
const timer = setTimeout(() => ctrl.abort(), 5000);   // 5s timeout

try {
  const res = await fetch(url, { signal: ctrl.signal });
} finally {
  clearTimeout(timer);
}

Enter fullscreen mode Exit fullscreen mode

The same AbortController works with addEventListener, with most modern Promise APIs, and with React Query's queries. Make it your default mental model: any long lived async work should be cancellable.

The shortcut for "fetch with timeout":

fetch(url, { signal: AbortSignal.timeout(5000) });

Enter fullscreen mode Exit fullscreen mode

Streaming responses

The body is a ReadableStream. You can consume it as it arrives, perfect for AI streaming or large downloads:

const res = await fetch("/api/stream");
const reader = res.body!.pipeThrough(new TextDecoderStream()).getReader();

while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  console.log("chunk:", value);
}

Enter fullscreen mode Exit fullscreen mode

Response and Request are real classes

You can build them, store them, clone them. Service Workers rely on this. The same Response API you receive from fetch is the one you return from a Service Worker.

API 2: Storage, three flavors

The browser has three storage mechanisms. Pick on purpose.

localStorage and sessionStorage

Tiny key value store. Synchronous. Strings only. Good for: small UI state (theme, last opened tab), tiny preferences. Bad for: anything large, anything secret, anything that should sync.

localStorage.setItem("theme", "dark");
localStorage.getItem("theme");
localStorage.removeItem("theme");

Enter fullscreen mode Exit fullscreen mode

sessionStorage is the same, scoped to the tab. Cleared on close.

A senior level rule: never store auth tokens in localStorage. JavaScript can read it, which means every third party script can too.

Cookies

Sent automatically with every same origin request. The browser respects HttpOnly, Secure, SameSite. We covered them in the auth post. Use them for sessions and CSRF tokens.

IndexedDB, the actual database

A real client side database. Asynchronous. Stores anything structurable, including blobs. Indexed for fast queries. Quotas in the megabytes to gigabytes range.

The native API is famously verbose. Use a tiny wrapper like idb-keyval for simple cases or Dexie.js for richer querying.

import { get, set, del } from "idb-keyval";

await set("note:42", { title: "Read later", body: "..." });
const note = await get("note:42");
await del("note:42");

Enter fullscreen mode Exit fullscreen mode

Use IndexedDB for: offline caches, drafts, full text search indexes, large user data, anything you need to survive a refresh and a flaky network. It is the storage that makes serious offline apps possible.

In 2026 there is also an exciting trend: SQLite in the browser, via wasm libraries like sql.js and wa-sqlite, often backed by IndexedDB or the Origin Private File System (OPFS). Real SQL queries on the client. Worth knowing about for offline first apps.

API 3: Service Workers, the engine of offline

A Service Worker is a script that runs in the background, separate from any page, and can intercept network requests for a whole origin. It is how websites become installable apps that work offline.

// app/main.ts
if ("serviceWorker" in navigator) {
  navigator.serviceWorker.register("/sw.js");
}

// public/sw.js
self.addEventListener("install", (e) => {
  e.waitUntil(caches.open("v1").then((c) => c.addAll(["/", "/styles.css", "/app.js"])));
});

self.addEventListener("fetch", (e) => {
  e.respondWith(
    caches.match(e.request).then((cached) => cached || fetch(e.request))
  );
});

Enter fullscreen mode Exit fullscreen mode

What that gives you: the page loads from cache instantly, even offline.

The senior level patterns:

  • Cache first for static assets that change rarely.
  • Network first, cache fallback for HTML.
  • Stale while revalidate for API responses.
  • Use a tool, do not hand roll. Workbox (Google) and Vite PWA Plugin scaffold a sane Service Worker in a few lines. They handle caching strategies, precaching, runtime caching, navigation fallback, and updates.

The full PWA story (offline + installable + push notifications) is a real thing in 2026. Browsers across Mac, Windows, Linux, Android, and even iOS support it. For an app that runs daily, "Add to Home Screen" is real distribution.

API 4: Web Workers, threads for compute

JavaScript runs on a single thread. Long work blocks the page. A Web Worker runs a script on a separate thread, communicating by message passing.

// worker.ts
self.onmessage = (e) => {
  const result = expensiveCompute(e.data);
  self.postMessage(result);
};

// main.ts
const worker = new Worker(new URL("./worker.ts", import.meta.url), { type: "module" });
worker.postMessage(input);
worker.onmessage = (e) => setResult(e.data);

Enter fullscreen mode Exit fullscreen mode

The friendlier modern API: Comlink turns the worker into a proxy you can await:

// worker.ts
import { expose } from "comlink";

expose({
  parseMarkdown(md: string) { return slowParser(md); },
});

// main.ts
import { wrap } from "comlink";
const api = wrap<{ parseMarkdown(md: string): string }>(
  new Worker(new URL("./worker.ts", import.meta.url), { type: "module" })
);
const html = await api.parseMarkdown(text);

Enter fullscreen mode Exit fullscreen mode

Use Web Workers for: heavy parsing (Markdown, syntax highlighting, JSON), image processing, data transforms, anything more than 50ms of compute. The page stays smooth, INP stays under 200ms.

A close cousin: requestIdleCallback runs a function when the browser is idle. Great for non urgent work like analytics flushing.

requestIdleCallback(() => sendQueuedAnalytics(), { timeout: 2000 });

Enter fullscreen mode Exit fullscreen mode

API 5: IntersectionObserver, "tell me when this is on screen"

Before this API, "is the element visible" was solved by listening to scroll and reading layout in a tight loop. It was awful.

Now:

const obs = new IntersectionObserver((entries) => {
  for (const entry of entries) {
    if (entry.isIntersecting) {
      console.log("visible:", entry.target);
    }
  }
}, { rootMargin: "200px" });

document.querySelectorAll(".lazy").forEach((el) => obs.observe(el));

Enter fullscreen mode Exit fullscreen mode

Uses for senior frontends:

  • Lazy load images and components before they are needed.
  • Infinite scroll: observe a sentinel at the bottom of the list.
  • Trigger animations when an element scrolls into view.
  • Track impressions for analytics.

rootMargin lets you start the work early, so the user never sees the loading state.

A close cousin: ResizeObserver fires when an element changes size. Great for charts, tables, and components that need to re-render on layout changes:

new ResizeObserver(([entry]) => {
  drawChart(entry.contentRect.width, entry.contentRect.height);
}).observe(chartEl);

Enter fullscreen mode Exit fullscreen mode

And MutationObserver for "tell me when the DOM changed". Niche but lifesaving for browser extensions and integrations with markup you do not control.

API 6: BroadcastChannel, talking across tabs

If a user has your app open in three tabs and logs out in one, the others should know. BroadcastChannel posts messages across same origin tabs:

const ch = new BroadcastChannel("auth");
ch.postMessage({ type: "logout" });
ch.onmessage = (e) => { if (e.data.type === "logout") goToLogin(); };

Enter fullscreen mode Exit fullscreen mode

Five lines, no library. Use it for: logout sync, cache invalidation across tabs, "this item just changed" notifications.

For more general cross window coordination, the Web Locks API ensures only one tab does a piece of work at a time:

await navigator.locks.request("sync-notes", async () => {
  await syncNotesWithServer();
});

Enter fullscreen mode Exit fullscreen mode

Other tabs that try to acquire the lock will queue. Brilliant for "only one tab should be syncing".

API 7: Clipboard, Share, File access, the human integration layer

These are the APIs that make a web app feel native.

Clipboard

await navigator.clipboard.writeText("hello");
const text = await navigator.clipboard.readText();

Enter fullscreen mode Exit fullscreen mode

Modern, async, permission gated. The old document.execCommand("copy") is deprecated. For images and other rich data, use navigator.clipboard.write with ClipboardItem.

Web Share API

if (navigator.share) {
  await navigator.share({
    title: "Mochi's Blog",
    text:  "A great post",
    url:   "https://example.com/post",
  });
}

Enter fullscreen mode Exit fullscreen mode

On mobile, this opens the system share sheet. On desktop, it falls back gracefully. One line replaces a custom share modal.

File System Access

const handle = await window.showSaveFilePicker({
  suggestedName: "notes.json",
  types: [{ description: "JSON", accept: { "application/json": [".json"] } }],
});
const writable = await handle.createWritable();
await writable.write(JSON.stringify(data));
await writable.close();

Enter fullscreen mode Exit fullscreen mode

Real "save as" dialog, real file. Limited to Chromium browsers in 2026 but excellent for productivity apps.

The simpler alternative for downloads is the venerable <a download>:

const url = URL.createObjectURL(new Blob([data], { type: "application/json" }));
const a = Object.assign(document.createElement("a"), { href: url, download: "notes.json" });
a.click();
URL.revokeObjectURL(url);

Enter fullscreen mode Exit fullscreen mode

API 8: View Transitions, navigation feels smooth

A genuinely magical API. Animate between any two DOM states with one line.

document.startViewTransition(() => {
  // any DOM mutation here
  setTheme("dark");
});

Enter fullscreen mode Exit fullscreen mode

The browser captures a snapshot before, applies your changes, captures after, and animates between them. With CSS view-transition-name on shared elements, you get FLIP-style transitions for free.

In 2026, the cross document version (@view-transition) lets multi page apps animate between full page navigations as smoothly as SPAs. This is one of the reasons SPAs are no longer the only path to good UX.

API 9: Scheduler, Idle Detection, and modern timing

The scheduler family of APIs, increasingly supported, gives you fine grained control over priority:

// new in 2024+ browsers
await scheduler.yield();        // give the browser a frame
scheduler.postTask(work, { priority: "background" });

Enter fullscreen mode Exit fullscreen mode

If scheduler.yield() is not available, the polyfill is a setTimeout(0) plus a microtask check.

The classic timing tools still matter:

  • requestAnimationFrame for any visual update tied to the next frame. Always use it for animations driven by JS.
  • performance.now() for high resolution timing.
  • PerformanceObserver to subscribe to LCP, INP, CLS, long tasks, navigation timing, resource timing. This is what web-vitals is built on.
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) console.log(entry);
}).observe({ entryTypes: ["longtask", "layout-shift", "largest-contentful-paint"] });

Enter fullscreen mode Exit fullscreen mode

Drop into devtools when you want to see what is actually slow.

API 10: Notifications, Push, Background Sync

For "this app feels real" features:

  • Notifications: new Notification("Hi") with permission. Used as the visual layer for Push.
  • Push: a Service Worker can receive pushes from your server even when the page is closed. Requires the user to grant permission and your server to send via the Web Push protocol.
  • Background Sync: the browser will retry a queued task when the user comes back online. Perfect for "send this comment whenever there is connectivity".

These are senior level features. Only enable them where they pay off. Most users have notification fatigue, so ask politely or not at all.

API 11: The "knows about the user" APIs (use sparingly)

The platform has a long tail of "tell me about the device" APIs. Senior engineers know they exist and use them with care.

  • matchMedia("(prefers-color-scheme: dark)").matches for theme detection.
  • matchMedia("(prefers-reduced-motion: reduce)").matches to respect motion preferences.
  • navigator.connection (Network Information API) for adaptive loading. Slow 3G? Send fewer images.
  • document.visibilityState to pause work when the tab is hidden.
  • navigator.locks (already mentioned).
  • Battery, Bluetooth, USB, Serial, Geolocation: each is a permissioned API, useful for very specific apps.

The principle: respect the user, ask before you peek. Permission prompts kill conversion if used carelessly.

API 12: Modern selection, drag, paste, undo

A handful of small APIs that delete entire libraries:

  • getSelection() for the current text selection.
  • document.execCommand is deprecated. For rich text, use contenteditable plus a library like TipTap or Lexical.
  • InputEvent with inputType ("insertText", "deleteContentBackward", etc.) for fine grained input handling.
  • HTMLDialogElement.showModal() for native modals (we covered this in the HTML post).
  • <details> and <summary> for native disclosures, no JS needed.
  • <input type="search">, type="date", type="time", type="color" for native pickers on mobile.

A surprising amount of "I need a library for this" turns into "the browser already does it" once you check first.

API 13: WebRTC, WebSockets, SSE, beyond request/response

Three transports for real time:

  • WebSockets for bidirectional persistent connections. We mentioned this in the HTTP post.
  • Server-Sent Events (EventSource) for one way streaming from server to client. Simpler than WebSockets, plays nicely with HTTP infrastructure.
  • WebRTC for peer to peer audio, video, and data. The transport behind every browser based video call.

Most apps need SSE for live updates and never touch the others. Reach for them when you have a specific need (real time collaboration, video, gaming).

A peek under the hood

What really happens when you call a platform API:

  1. JavaScript calls into the browser's C++ implementation.
  2. The browser does the work (often on another thread or in another process).
  3. The result comes back as a Promise resolution, an event, or a callback.
  4. Your code runs in the JS event loop.

Two consequences for senior engineers:

  • Native APIs are usually faster than userland equivalents because the heavy lifting happens off the main thread.
  • Browser support varies. Use caniuse.com before committing. For features you must have everywhere, polyfills exist. For features that gracefully degrade, feature detect with if ("foo" in window) and ship the better experience to capable browsers.

Tiny tips that will save you later

  • Read MDN. It is the single best web platform documentation, and it is free.
  • Use caniuse.com before adopting any platform API. Browser share matters.
  • Feature detect, do not user agent sniff. if ("share" in navigator) is the right check.
  • Cancel everything. AbortController works with fetch, with addEventListener, with most modern APIs.
  • Use IntersectionObserver instead of scroll listeners.
  • Use ResizeObserver instead of window resize listeners when you only care about an element.
  • Use BroadcastChannel for cross tab communication.
  • Use idb-keyval or Dexie for IndexedDB unless you really enjoy callbacks.
  • Use Workbox or Vite PWA for Service Workers.
  • Use view-transition for navigation animations. Free polish.
  • Respect prefers-reduced-motion and prefers-color-scheme. They are one query away.
  • Keep platform APIs small. Wrap each in a tiny module so the call sites do not get noisy.

Wrapping up

So that is the whole story. The web platform stopped being a dumb document viewer years ago. Modern browsers ship a small operating system: a network stack with cancellation and streaming, three storage tiers, real threads, real schedulers, observers for everything that used to need a polling loop, broadcast channels for cross tab life, file system access, share sheets, push notifications, and beautiful view transitions.

A senior frontend engineer treats these as the first toolkit, not the last resort. The libraries on npm exist because the platform was missing the feature. Many features have shipped. Many libraries can come out of your package.json if you check what the browser already does.

Once that map is in your head, you write less code, ship smaller bundles, and build apps that feel like the device they run on.

Happy platforming, and may your dependency list stay short.