CSE 134B Web Client Languages
  1. Interaction
  2. JavaScript
  3. How much JavaScript?

Interaction · JavaScript 11

How much JavaScript?

The earlier pages taught how the language behaves. This one asks how much of it a page should use. It looks at what script costs people, how much of that cost comes from other people's code, how to build one small feature with more or less script and measure the difference, what happens when a browser lacks what your code assumes, and how to keep up with a field that never stops changing.

The argument of this section ends here, and it has two parts. Fight fatigue by knowing why something matters to you. Start with vanilla JavaScript and work upwards. Every number on this page has a source and a date, because numbers in this field age quickly: the deck's own figures came from 2018, and they appear below as history.

1. JavaScript smells

The deck lists what smells like trouble in JavaScript development:

  • Legacy framework use.
  • Newer framework abuse and tribalism, including tool chain tribalism.
  • Observed performance profiles, and the assumption that the mobile web can absorb them.
  • The rapidly changing nature of browsers and their variable support for JavaScript APIs, which makes web development significantly difficult.

None of these is a flaw in the language. Page 01 covers the history behind them: DHTML and Ajax added confusion about what the language was for, JavaScript moved to the server and outside the browser, and versions ran from ES3 through ES2019 and beyond while everything became an "app". The deck draws a conclusion from that history that this page keeps: some of the bad aspects of JavaScript are really the bad aspects of the environment in which it is used.

The deck names one more problem, framework primacy and the "Vanilla JS vs X" problem. When a framework is the first thing a developer learns, each comparison is framed as a choice between frameworks, and a plain script is treated as the thing you do if you lack one. The result is that the question "how much script does this need" rarely gets asked. This course's position is that it should be asked first, with numbers.

2. What script costs

Not all bytes are equal

The deck asks whether JavaScript should be considered harmful, and shows a slide from Addy Osmani's 2018 work on the cost of JavaScript, which calls script the most expensive part of your site. The deck's note on that slide is "Not all bytes are equal". An image has to be downloaded and decoded. A script has to be downloaded, then parsed, compiled and run, and much of that happens on the same main thread that responds to the person using the page. The event loop module explains why that thread is the bottleneck, and RAIL gives the response times people notice.

The 2019 follow-up from the V8 team, The cost of JavaScript in 2019, reports that parsing and compiling had become much faster and states that "the dominant costs of processing scripts are now download and CPU execution time". Downloading and running are both things you control by sending less.

The figures in 2018

The deck's figures, which it took from Osmani in 2018, are history now. The median page then sent 350 KB of JavaScript, over 1 MB uncompressed, and took 15 seconds to become interactive. Processing 1 MB of uncompressed script took about 280 ms on an iPhone 8, about 1.8 seconds on a Moto G4 and about 6 seconds on an Alcatel 1X, and Google News over slow 3G took 55 seconds to become interactive on that Alcatel. Devices and networks have changed since. What carried forward is the spread between phones: the same bytes do not cost everyone the same.

The figures now

The slide that follows those charts asks, "That was then, what about now?" and answers, "Not good..not good at all". The most recent large survey is the Page Weight chapter of the 2025 Web Almanac, from HTTP Archive's July 2025 crawl. It finds that 98.1% of pages request at least one JavaScript file. The median mobile home page weighs 2,559 KB, and 632 KB of that is JavaScript.

What the median mobile home page loaded in July 2025A bar chart of kilobytes by resource type. Images 911, JavaScript 632, fonts 122, CSS 77, HTML 22. JavaScript is highlighted as the second largest.Images911 KBJavaScript632 KBFonts122 KBCSS77 KBHTML22 KB
Median mobile home page by content type, July 2025. Outside data from the 2025 Web Almanac, Page Weight, figure 14.5. The bars are medians of each type, so they do not add up to the 2,559 KB total.

The same chapter reports Lighthouse's Total Blocking Time, the time the main thread spends too busy to respond during load. The median is 78 ms on desktop and 1,835 ms on mobile. The chapter does not break this down by cause, so it does not prove that script is responsible for all of it, and part of the gap comes from Lighthouse's emulated mobile test profile, not real phones. It still points the same way as the deck's 2018 phones.

The deck also cites Alex Russell's yearly budget posts, whose 2023 edition called the overshoot an ethical crisis for front-end work. The current edition, The Performance Inequality Gap, 2026, models the 75th percentile user: a Samsung Galaxy A24 4G or similar on a connection of 9 Mbps down with 100 ms of latency. For a three-second target on that setup, it budgets 0.3 MiB of JavaScript for a page that is 15% script, and 0.62 MiB for one that is 50% script. It reports median mobile JavaScript at 680 KiB, which is above both.

You can do part of that arithmetic yourself. This playground computes only download time at the Russell post's 9 Mbps, leaving out latency and everything the CPU has to do afterwards.

Who pays

A slow page is a cost someone else pays. The deck points to Zach Leatherman's collected notes on React criticism, which recorded in February 2023 that sites using React and Next.js had worse Core Web Vitals than the rest of the archive. The same comparison can be run today in the HTTP Archive technology report. Those 2023 notes gave 39.37% of all sites good Core Web Vitals, 32.81% of React sites and 26% of Next.js sites. These are the September 2026 figures.

Outside data: share of sites with good Core Web Vitals, HTTP Archive technology report, September 2026
Sites usingMobileDesktop
All sites in the report52.8%60.8%
jQuery54.7%59.5%
React43.9%54.7%
Vue.js36.2%50.9%
Next.js35.7%56.9%

Read that table as a correlation. The sites that choose each tool differ in many other ways, and the shares have risen across the board since 2023, frameworks included. The table does show that adopting a popular framework does not make a site fast by itself, and that every row looks better on desktop than on mobile.

The deck's last example is Eric Bailey's Modern Health, frameworks, performance, and harm (2023), on a mental health service whose signup never finished loading. The deck's phrase for this is actual real-life harm. Bailey's sentence is the one to remember: "A person seeking help in a time of crisis does not care about TypeScript, tree shaking, hot module replacement, A/B tests, burndown charts, NPS, OKRs, KPIs, or other startup jargon."

3. Much of the cost is other people's code

Everything in this section is outside data, labeled with its source. No framework is part of this page's demo. The deck's argument here is that much of the cost comes from dependencies, and less of it from the language.

The last dedicated JavaScript chapter in the Web Almanac is in the 2024 edition, since the 2025 edition lists none. On the median mobile page it found 168 KB of first-party JavaScript and 375 KB of third-party. It found about 206 KB of the JavaScript sent to a mobile page, 44%, unused during load. jQuery appeared on 74% of pages, core-js on 41%, Lodash on 11%, and React on 10%.

Published framework sizes give a sense of the floor, before any of your own code. These are compressed sizes for the current versions on 5 November 2026. They are not directly comparable, since these packages do different amounts of work.

Outside data: published framework sizes, gzipped
PackageGzippedSource
React and React DOM 19.3.1, client120 KBgzip -9 of the published react (4.6 KB) and react-dom client (110.5 KB) files, plus the react-dom (1.9 KB) and scheduler (2.5 KB) files it requires
Vue 3.5.4548.1 KBBundlephobia
Lit 3.3.36.0 KBBundlephobia
Preact 11.1.15.1 KBBundlephobia

React's row needs its own source because Bundlephobia's listing for react-dom measures the package's main entry, about 1.5 KB, and the client renderer lives behind a separate react-dom/client path. Size tools answer the question you asked them, so check what they measured. Tree shaking and a build's own choices will move all of these numbers. A framework's size is also the smallest part of what it costs if the app built on it pulls in dozens of further packages.

4. From maximal to modest

The deck's source for this section is Pascal Laliberté's book Modest JS Works. Its address, modestjs.works, now redirects to the author's own site, so what follows is the book as the deck carries it, stated as the book's claims and not as measurements.

The book's opening is that today's JavaScript is too bloated, heavy and dependent on other people's code. As the deck says, having dozens or even more than 100 dependencies for a simple app has become a running joke because it is a sad reality. The deck's summary of the book then states three facts of the matter:

  • You have no way of knowing if there's malicious code in your dependencies.
  • Your app is 10x larger than it needs to be.
  • Having all of this external code just lets you shift the blame for why your app doesn't work properly.

The deck's summary gives the 10x without having seen your app, so measure your own before repeating it. The first claim has since been demonstrated. On 8 September 2025, malicious versions of popular npm packages including chalk and debug were published after a maintainer's account was phished, and the code targeted cryptocurrency activity in browsers. Those packages sit deep in many dependency trees, so many of the projects that received them had never chosen them. The film Everything You Embed follows this kind of risk through a page.

Reducing this does not start in the code. To start making your JavaScript more modest, you can go all the way up the design process to where the idea originates, and have discussions with the people in charge:

  • What is this for?
  • At the end, what will this do to our company's top line?
  • Is there any way we can connect to the end goal we're trying to get to with something smaller, more modest?

If the company where you work is large, convoluted or immodest, the only way you're going to bring in more modesty is by talking the language of business, which is why those questions are about goals and revenue and do not mention bundlers. The deck treats asking them as a duty. In its words, "It's immoral to just assume that the people who won't be coding the app thought about all of the details that will heavily affect how you build the app."

5. A modest stack

A modest stack starts with modest HTML, CSS and native elements:

  • CSS that doesn't need to be optimized to work with hundreds of developers.
  • Keeping JS out of CSS when possible.
  • HTML that is mostly rendered server-side. Multi-page apps do just fine despite popular advice.

If you really want a single-page app, the deck asks you to make sure of three things first: it's the best approach to serve your users, it won't be broken in 6 months from dependencies, and it won't require a huge team to maintain.

The deck's good news is that this course has been on the path all along. It shows the field as a spectrum, with the course sitting at the boundary where static pages begin to be enhanced. Everything so far in the course's HTML and CSS sections sits to the left of that arrow.

From static HTML to a single-page appThree columns between two arrows. The top arrow is data model locality, from server to client. The bottom arrow is interaction depth, from shallow to deep. The columns are static HTML with server app state and refresh updates, progressive enhancement with server app state and Ajax partial updates, and single-page app with client app state and optimistic commit. A marker labeled we are here points at the boundary between static HTML and progressive enhancement.We are hereServerData model localityClientStatic HTMLServer app state,refresh updatesProgressive enhancementServer app state,Ajax partial updatesSingle-page appClient app state,optimistic commitShallowInteraction depthDeep
Where the state lives moves with how deep the interaction goes. Redrawn from the deck's slide, which credits Alex Russell's The Market for Lemons.

Russell's essay gives the reason a choice along this line depends on the site: "Sites with shorter average sessions can afford less JS up-front." A person who opens a store page once from a search result gets little return on a large script, while a person who spends all day in a mail client may recover that cost over many interactions. The same essay argues for "semantic markup, CSS, and progressive enhancement for most sites", and describes script in that approach as "special occasion food".

6. The JS gradient

These positions are not a binary choice. The deck calls the range a gradient, and a new app built using modest, yet modern, JavaScript could use several parts of it. From least script to most:

  1. Mostly server-generated HTML and CSS.
  2. Globally, some JavaScript sprinkled to enhance certain interactions on most pages.
  3. In some areas, certain sections of the HTML are augmented with JavaScript.
  4. In some areas, JavaScript is used to more deeply morph the view based on user interactions.
  5. In some areas, whole sections of a page are taken over by JavaScript.
  6. For other, more deeply interactive uses, whole pages are taken over by JavaScript, as a single-page app.

One feature at each level

The demo builds one feature six times, a list of terms that a reader can narrow by category, in plain JavaScript with no libraries. Choose a level to load it in the frame. Each level uses the files it would need in production and nothing else.

  • Level 1 has no script. The list is in the HTML, and CSS does the filter, using :has() to react to a checked radio button.
  • Level 2 is the same document plus a sprinkle, which adds a count of what is showing and remembers the choice across reloads. If the script fails, the filter still works.
  • Level 3 augments that one section with a search box. Script creates the box, so a reader without script never sees a control that does nothing.
  • Level 4 reads the terms out of the HTML and redraws them as sorted groups with the match marked, animating with a view transition where the browser has one.
  • Level 5 starts with an empty section. Script fetches a JSON file and builds everything, and now has to handle loading and failure.
  • Level 6 starts with an empty page. Script builds a list view and a view for each term, and has to supply a URL for each view, the back button, the document title and the focus move that the browser gave level 1 for nothing.

The cost, measured

This command reads the demo's own files, which are listed per level in manifest.json, and adds up each level's bytes. Run it from the root of the site's repository. It prints the level, its JavaScript bytes, the bytes of all of its files, and those files gzipped one at a time, as a server would send them.

node -e " let fs = require('node:fs'), zlib = require('node:zlib'); let dir = 'src/interaction/javascript/demos/gradient/'; let manifest = JSON.parse(fs.readFileSync(dir + 'manifest.json')); for (let level of manifest.levels) { let js = 0, all = 0, gzip = 0; for (let name of level.files) { let bytes = fs.readFileSync(dir + name); all += bytes.length; gzip += zlib.gzipSync(bytes).length; if (name.endsWith('.js')) js += bytes.length; } console.log(level.n, js, all, gzip); }" 1 0 3795 1527 2 720 4559 1966 3 1009 4847 2085 4 2447 6283 2609 5 2444 5880 2541 6 3593 6999 2983
JavaScript bytes at each level of the gradientA bar chart running from least to most script. Level 1, HTML and CSS, 0 bytes. Level 2, a sprinkle, 720. Level 3, a section augmented, 1,009. Level 4, the view morphs, 2,447. Level 5, a section taken over, 2,444. Level 6, the page taken over, 3,593.Least scriptMost script1. HTML and CSS0 B2. A sprinkle720 B3. A section augmented1,009 B4. The view morphs2,447 B5. A section taken over2,444 B6. The page taken over3,593 B
JavaScript bytes per level, measured by the command above. Levels 4 and 5 spend about the same on different work.

The demo's own table reads the same files in your browser, with fetch and the byte length of each response, so its first two number columns match the output above exactly. Its gzip column uses the browser's CompressionStream, which came within about 13 bytes per file of Node when tested in Chromium. The page itself and manifest.json are measuring tools and are not counted. These files are unminified, with their comments.

Reading the numbers

The script grows overall as you go down the list, with levels 4 and 5 about equal, and at no level does this small feature's script reach 4 KB. Gzipped, the whole of level 6, HTML, CSS, script and data, is 2,983 bytes. Compare that with the outside figures earlier: 632 KB of JavaScript on the median mobile home page, and 120 KB for the React packages before any app code. A framework does a great deal that this demo does not, and a product has more features than a list of twelve terms. The comparison only shows where the bytes on a typical page are not coming from: the language and a plain feature built in it.

Total bytes do not rise in a straight line. Level 5's total is lower than level 4's, because its HTML drops the twelve terms and a separate JSON file carries them. That file costs an extra request, and it means the content does not exist until script has run.

That last point is the larger cost, and a byte count does not show it. Levels 1 to 4 keep their content if the script fails, is blocked or arrives late. Levels 5 and 6 show a sentence in a noscript element when script is off, and nothing useful if the script fails to load with script on. Level 6's script spends much of its length rebuilding browser behavior, and each thing it rebuilds is a thing that can break. Moving down the gradient buys richer interaction, such as level 6's per-term views. Move only as far as the interaction needs, and use different levels for different parts of a site.

7. Language feature dangers

If you use a language feature not supported in some browsers, the error is catastrophic. A browser parses a classic script whole before running any of it, so one piece of syntax it does not recognize means none of the file runs, including the parts that would have worked. A missing function fails differently: everything before the call has already run, and the error is one you can catch. The playground runs both, using new Function to parse code from a string so that the failure can be caught and printed.

The deck lists the traditional approaches, in its own words:

  1. Only lame people haven't upgraded, forget about them.
  2. ZOMG we have to SUPPORT Internet Explorer.
  3. Detect and adapt, oof what a headache.

History: module, nomodule and ES3

The deck showed two ways out, and both are history for most sites. One relied on old browsers ignoring type="module" and new ones ignoring nomodule, which sent modern code to one group and a fallback to the other. The other transpiled everything down to ES3 and sent that to everybody, usually from a build run by a task runner such as Gulp.

<!-- Provide for ES6+ by default, and fail to ES3/5 for others --> <script type="module" src="moduleScript.js"></script> <script nomodule src="classicScript.js"></script> <!-- Transpile ES6 to ES3 for everybody --> <script src="transpiled.js"></script>

Microsoft ended support for the Internet Explorer 11 desktop app on certain versions of Windows 10 in June 2022, and JavaScript modules have been Baseline Widely available since November 2020. nomodule still works, but a page rarely has a reason to use it.

The habit outlived the need. The 2024 Web Almanac found 67% of mobile pages still shipping JavaScript that has been transformed for old browsers or contains unnecessary legacy code. That is weight spent on browsers those pages' visitors may not use. If you do transpile, tell the tool what you target. Browserslist accepts the query baseline widely available, for example, and the question to ask is what your own visitors run, which your site's analytics answer. The deck's reality check for language support, the ES6 compatibility table, has moved to a new address and still shows support by feature.

8. API availability dangers

APIs are always changing because browsers are always evolving, and old browsers don't go away quickly enough. The deck offers the same three approaches as for language features, with Internet Explorer 6 as the old enemy this time. Missing APIs are easier to deal with than missing syntax, because you can ask the browser in ordinary code.

Detect and adapt

A feature check asks about the feature, and leaves the browser's name out of it. It is usually a test of whether a property exists, using in or a truth test. CSS has the equivalent in @supports. This playground reports what your browser has, then uses Temporal if it can and the older Date if it cannot, which is a detect-and-adapt in five lines. The output depends on your browser: at the time of writing Web Platform Status lists Temporal in Chrome, Edge and Firefox, and not yet in Safari.

Level 4 of the demo does this for its animation. document.startViewTransition is called where it exists, and otherwise the list redraws without the animation. The feature is an improvement and the page works without it. That approach is progressive enhancement, which the film The Localhost Effect also covers.

Warn or polyfill

When the feature is something the page needs, the deck says you have two realistic choices:

  1. Use the feature, and warn or show an error to those not getting the proper experience.
  2. Simulate the feature, which is called a polyfill.

The first is a line of text in place of a blank area, and it should say what the person can do.

let message = document.querySelector('#message'); if (!('share' in navigator)) { message.textContent = 'This browser cannot open the share menu. Copy the address instead.'; }

A polyfill checks for the feature and defines it only if it is missing, so that the rest of your code can use the standard name, and browsers with the feature keep their own. This one pretends to be an old browser by deleting Object.groupBy, then supplies a simple version.

A polyfill is still a dependency, and it is sent to every visitor unless you load it conditionally. The Almanac's 41% of pages with core-js shows how common that is. It can also be a security problem. In June 2024, Sansec reported that cdn.polyfill.io was injecting malicious code into more than 100,000 sites, after the domain was sold that February. Those sites had added a script from someone else's server to support old browsers. If you need a polyfill, host a copy yourself and set a date to remove it.

Many old dependencies are in this position now, because the platform caught up. Each line below once brought a library or a helper into a project, and each is Baseline Widely available.

Built-ins covers these in more depth. Check the standard library before you add a package, since that keeps a package's bytes, and its risks, out of your page.

9. Baseline

The deck's favorite site for support questions is caniuse.com, which is still the place to look when you need version-by-version detail. A shorter answer now exists, called Baseline. It reports where a feature stands across a core set of browsers: Chrome and Edge, Firefox, and Safari, on desktop and on mobile where they exist.

The stages of BaselineThree stages in order. Limited availability means some core browsers lack the feature. When all core browsers support it, the feature becomes Newly available. Thirty months later it becomes Widely available.Limited availabilitysome core browsers lack itNewly availablein all core browsersWidely availablein all, for 30+ monthsthe last core browser ships it30 months pass
A feature's Baseline status, from the definition at web.dev/baseline.

Web Platform Status tracks each feature. The statuses below were read from it on 5 November 2026, and they will move, so check before you rely on them.

Statuses from Web Platform Status, 5 November 2026
FeatureStatusSince
fetchWidely availableSeptember 2019
JavaScript modulesWidely availableNovember 2020
<dialog>Widely availableSeptember 2024
:has()Widely availableJune 2026
Object.groupBy, array groupingWidely availableSeptember 2026
Set methodsNewly availableJune 2024
PopoverNewly availableJanuary 2025
View transitionsNewly availableOctober 2025
Navigation APINewly availableJanuary 2026
TemporalLimited availabilityNot in all core browsers

Level 1 of the demo depends on :has(), which has only just reached Widely available, while level 4's view transition is Newly available and is used behind a check. This course's default follows that pattern: use Widely available features freely, use Newly available ones behind detection or as enhancement, and treat Limited ones as an enhancement that must fail quietly. Baseline describes browsers in general, and your own analytics describe your visitors, so a site with many older devices may want a stricter rule.

10. Fighting fatigue

Like learning a spoken language, learning JavaScript goes beyond syntax. It also takes the culture and trends necessary to understand it. JavaScript changes so rapidly in that sense that many complain of "JavaScript fatigue". The deck's response is short, and the whole section has been building toward it. Fight fatigue by:

  • Knowing "why" something matters to you.
  • Having reasons for the things you do.
  • Starting with "Vanilla JS" and working upwards.

In practice, when a new tool appears, ask what problem it solves and whether you have that problem. If you do, measure what it adds, as the command in this page's demo does, and compare it with what the platform already offers. A reason can be good and still lead to a framework: a large team, a long-session app, or a job whose codebase already uses one. Starting with vanilla JavaScript does not rule these out. It means you learn what a framework is doing for you, and you can read its errors when it stops.

The deck's summary is a useful checklist of what you now know. JavaScript supports a basic syntax very similar to C, but it is quite different: it is a weakly typed language, with coercion rules of its own and closures. It is highly object oriented but uses prototypal inheritance, now also written with classes since ES6. It doesn't force object-oriented programming on developers, and it easily supports procedural and functional styles.

The deck closes with the host environment, which it calls the challenge. There are many host environment details, in the browser, Node.js and elsewhere, that can lead to trouble. Its advice is the order this section followed: "Get language right first then attack those details so you know where to place blame." When something breaks, you can now ask whether the language did what it said, whether the host lacks a feature, or whether a dependency brought the trouble with it. Those details continue in the DOM, events, components and client-side communication, and performance takes up the cost of what you ship.

11. Summary

ConceptKey points
The cost of scriptA script is downloaded, parsed, compiled and run, much of it on the main thread. Median mobile home page, July 2025: 632 KB of JavaScript of 2,559 KB. The same bytes cost a cheap phone far more.
DependenciesOutside data: on the median mobile page in 2024, 375 KB of script was third-party and 168 KB first-party, and 44% of what was sent went unused during load. Dependencies carry risk as well as weight.
Modest JSStart at the idea. Ask what it is for, what it does for the business, and whether something smaller reaches the goal. Asking is part of a developer's job.
A modest stackModest HTML, CSS and native elements, mostly rendered on the server. A single-page app has to justify itself: best for users, not broken in 6 months, no huge team.
The JS gradientSix levels from no script to whole pages taken over. Use different levels for different parts of a site. Past level 4, content depends on script having run.
Measured levelsOne feature at each level, from 0 to 3,593 bytes of JavaScript, by a command you can rerun. Under 3 KB gzipped for all of level 6's files.
Language feature dangersUnknown syntax stops the whole script at parse time. ES3 transpiling and nomodule are history. Target what your visitors run, and do not ship legacy code by habit.
API availability dangersA missing API can be detected with ordinary code. Adapt, warn or polyfill. Self-host a polyfill, and plan to remove it.
BaselineLimited, Newly available in all core browsers, Widely available 30 months later. Check Web Platform Status and your own analytics.
FatigueKnow why something matters to you, have reasons for what you do, start with vanilla JavaScript and work upwards. Get the language right first, so you know where to place blame.