Ivan Akulov Profile Banner
Ivan Akulov Profile
Ivan Akulov

@iamakulov

Followers
7,035
Following
374
Media
1,173
Statuses
5,716

Web perf engineer @ Framer. Prev. web perf consultant (Google, Appsmith, Toggl, etc). Getting React interactions 2-4x faster. GDE. He/him 🏳️‍🌈

Amsterdam
Joined January 2015
Don't wanna be here? Send us removal request.
Explore trending content on Musk Viewer
Pinned Tweet
@iamakulov
Ivan Akulov
1 year
How I typically test performance (a thread for a client who asked about this):
8
93
444
@iamakulov
Ivan Akulov
4 years
Okay, let’s talk about reexports – and why they’re bad for loading and runtime performance. Thread ⬇️
Tweet media one
27
290
1K
@iamakulov
Ivan Akulov
6 years
🎉 Super glad to announce that I & @addyosmani made a web performance guide for webpack! — Making front-end smaller — Improving caching — Monitoring it to stay fast Live at Web Fundamentals:
Tweet media one
13
442
1K
@iamakulov
Ivan Akulov
6 years
🔥 TIL Chrome can measure the percentage of unused JS and CSS on the page! DevTools → Top-right menu → More tools → Coverage
Tweet media one
11
387
1K
@iamakulov
Ivan Akulov
2 years
Oh look, my favorite app Spotify being slow! Also it’s 1 am meaning it’s the best time to do some performance profiling. Thread ⬇️
@geoffreylitt
Geoffrey Litt
2 years
Not to pick on Spotify in particular, but it's interesting how slow some "desktop apps" to do things like switch between playlists that are already loaded on my machine. Feels more like a "website" than an "app"
2
3
80
8
242
940
@iamakulov
Ivan Akulov
1 year
Ever read an article that says “setTimeout(..., 0) fires not in 0 ms but in 4”? This is no longer true – at least not in Chrome and Safari, and not since a few months ago. But getting there was a bit of a bumpy road.
Tweet media one
20
186
876
@iamakulov
Ivan Akulov
4 years
Okay, let’s do a thing! One like of this tweet = one web perf tip. Loading perf, runtime perf, whatever ⬇
10
142
747
@iamakulov
Ivan Akulov
1 year
This weekend’s project: debugging a performance issue where running div​.style.transform = 'translateX(100px)' was triggering a style recalculation for 15K nodes 😨 (Guess why)
Tweet media one
9
51
619
@iamakulov
Ivan Akulov
9 months
Favorite hobby: rewriting third-party React libraries in pure CSS to avoid forced style recalcs. Today’s example: react-slick (a 15kB image gallery) was causing multiple style recalcs for a client. Replaced with a 20-line almost-pure-CSS implementation with scroll snapping:
Tweet media one
Tweet media two
Tweet media three
8
27
511
@iamakulov
Ivan Akulov
4 years
Got a question in DMs: “How do I grow my JavaScript skills?” My favorite trick:
Tweet media one
7
88
502
@iamakulov
Ivan Akulov
3 years
If you ever looked inside a non-minified bundle, you might’ve seen a lot of automatically generated comments that look like this: /*#​__PURE__*/ Even wondered what they are for? Here’s a short thread 🧵
Tweet media one
6
101
384
@iamakulov
Ivan Akulov
9 months
⚛️ React hydration is one of the first things I look at whenever I’m optimizing loading speed of a React app. It’s super expensive – because it has to render every single component on the page. But how do you measure how long it takes? And why is it so expensive? ↓
Tweet media one
5
55
382
@iamakulov
Ivan Akulov
6 years
🔥 We’ve updated the WebFundamentals’ “Web performance with webpack” guide for webpack 4! ✂ Now mentioning the production mode, the new code splitting approach, and replacing some plugins to work with the new webpack. ➡ Check it out →
Tweet media one
6
128
374
@iamakulov
Ivan Akulov
3 years
Thread of my favorite web performance tools 🚀
5
95
321
@iamakulov
Ivan Akulov
1 year
You might’ve seen this picture in the past. It shows the steps that happen in the browser whenever it needs to update the page: JavaScript → Style → Layout → Paint → Composite But what’s actually happening there is not that simple (and is pretty fascinating). Let’s dive in!
Tweet media one
7
75
291
@iamakulov
Ivan Akulov
4 years
Finally 100 🎉 A thread on how – a mostly static React/Gatsby site – reached the perfect PageSpeed Insights score, and how you can do that as well ⬇
Tweet media one
6
49
253
@iamakulov
Ivan Akulov
4 months
Life update: after ~6 years of doing web perf consulting, I turned the page and joined @framer . I’ll be leading site performance efforts there, together with a team of amazing engineers 🖤
Tweet media one
27
2
241
@iamakulov
Ivan Akulov
5 months
TIL Safari DevTools have a “Type Profiler”, which shows types the engine inferred for each JS variable ↓ Not sure why or when this can be useful in practice, but looks like a cool gimmick!
Tweet media one
7
39
217
@iamakulov
Ivan Akulov
7 years
A new post! Improved the size of a popular library in webpack and explained it step-by-step:
Tweet media one
5
75
210
@iamakulov
Ivan Akulov
4 years
Finally published the article I’ve been working on for the past 3 weeks! 📝 Case study: Analyzing Notion app performance Or how to make a React app load ~30% faster – just by tuning some configs and delaying some scripts. →
Tweet media one
12
70
205
@iamakulov
Ivan Akulov
6 months
My favorite HTTP/2 feature is Connection Coalescing. In HTTP2, normally, a browser opens a new connection for every new domain it sees. (So if your site loads via www. but serves images from img., that’d be 2 connections.) But new connections are costly! So browsers do a trick.
Tweet media one
4
13
200
@iamakulov
Ivan Akulov
6 years
A superb explainer for the `font-display` CSS attribute by @notwaldorf : (`font-display` allows to display the text while the font for it is still loading)
Tweet media one
0
44
188
@iamakulov
Ivan Akulov
4 years
awesome-webpack-perf just got updated! ✨ Added a bunch of new webpack tools for web perf, including — esbuild-webpack-plugin — zero-runtime CSS-in-JS libs — and more analysis tools
Tweet media one
0
37
175
@iamakulov
Ivan Akulov
3 years
okay this is now my favorite photo of this month (from )
Tweet media one
1
19
163
@iamakulov
Ivan Akulov
6 years
🎉 The new webpack 4.6 now supports prefetching chunks! Use this when you have a dynamic import and you know that you’ll need the imported file soon. The browser will prefetch the file in the background!
Tweet media one
0
65
157
@iamakulov
Ivan Akulov
6 months
Today’s learning: Cloudflare can break React hydration in the most funny way possible :D 1. Make a React site that renders `<p>My email: ivan @example .com</p>` 2. Put it behind Cloudflare 3. Hydration mismatch!
Tweet media one
7
11
150
@iamakulov
Ivan Akulov
5 years
🚀 Launching... a curated list of webpack tools for web performance! A whole bunch of plugins and loaders for: — Compressing JS, CSS, images and fonts — Service workers & PWA — Preloading & <script async/defer> — Bundle analysis
Tweet media one
1
62
134
@iamakulov
Ivan Akulov
4 years
Okay, let’s talk about bundle duplicates. A common issue in JS bundles is duplicated dependencies. E.g., lodash, core-js, some polyfill libs are frequently bundled multiple times. Here’s how to detect and solve this issue: a thread ⬇
Tweet media one
3
30
129
@iamakulov
Ivan Akulov
2 months
TIL of , which is like CanIUse but for email clients:
Tweet media one
1
15
123
@iamakulov
Ivan Akulov
7 years
@o_cee @kentcdodds The user has been removed, so, for a reference, here’s the full list of packages that they published:
Tweet media one
5
48
99
@iamakulov
Ivan Akulov
5 years
📝 Did you know HTML has as much as 5 different tags to preload something? Here’s a detailed deep-dive into each of them. 🔬 How they look ⚙ What they do 🤷‍♀️ And when to use each one
Tweet media one
6
31
103
@iamakulov
Ivan Akulov
2 years
🌅 <img> and background-image download images differently. — <img> tag fetches an image as soon as HTML arrives. — background-image fetches it much later, only when all CSS files download (even if you specify style="background-image: …" straight in HTML). Why is there a delay?
Tweet media one
2
30
97
@iamakulov
Ivan Akulov
5 years
@samccone Humble brag: I & @kurtextrem made , a 500-byte script that adds `font-display: swap` for Google Fonts
Tweet media one
2
18
92
@iamakulov
Ivan Akulov
3 years
🔖 This is the most complete and detailed guide into how React works under the hood I saw (and written by no one else than @acemarke ):
Tweet media one
2
14
91
@iamakulov
Ivan Akulov
4 years
CSS tip: if you use `overflow: scroll`, it’s likely you actually want `overflow: auto`. “overflow: scroll” means “*always* show scrollbars”. You won’t see the difference on a Mac because it autohides all scrollbars. But on Windows, it’d look like this:
Tweet media one
4
18
91
@iamakulov
Ivan Akulov
6 years
OK, seriously, the Zen of Python is one of the best things created in software development (and it doesn’t apply to Python only, of course). I keep and keep and keep returning to it when I’m deciding which one of a few similar interfaces or implementations to choose.
Tweet media one
3
28
84
@iamakulov
Ivan Akulov
6 years
@rauschma I like having a git-ignored .env file with secret keys in the root of the repo. 1) It’s compatible with 2) There’re common helpers like the dotenv package 3) It’s easy to create a .env.example file which describes the config variables
3
14
85
@iamakulov
Ivan Akulov
2 years
A performance check I like to do whenever I work with a server-rendered React site is: Open DevTools → ⌘⇧P → “Disable JavaScript” → reload the page. If the page looks very different with JS disabled, the site’s got performance issues.
Tweet media one
3
10
84
@iamakulov
Ivan Akulov
4 years
PSA: always set the Cache-Control (or Expires) header on your responses. If you don’t, browsers will cache responses based on their internal heuristics. And this could result in an unexpected behavior.
Tweet media one
0
21
79
@iamakulov
Ivan Akulov
11 months
Drew a flowchart (about how I debug React performance) for today’s React Summit workshop:
Tweet media one
1
5
81
@iamakulov
Ivan Akulov
7 years
It took me several months of using it to finally understand. What webpack’s ContextReplacementPlugin actually does:
Tweet media one
4
20
76
@iamakulov
Ivan Akulov
6 years
🔥 The “Web Performance 101” talk transcript is finally out! 94 slides about: 👑 Why performance matters 🚀 How to optimize JS, CSS, HTTP stuff, images and fonts ⚒ Tools that help you better understand you app’s perf →
Tweet media one
1
26
78
@iamakulov
Ivan Akulov
1 year
3) So now, when you’re calling `setTimeout(0)`, what actually happens is: • if it’s a fresh `setTimeout(0)` call, it will run immediately (in all browsers) • if it’s a nested `setTimeout(0)` call, it will run in 4 ms after 5 (Firefox), 10 (Safari), or 15 (Chrome) nested calls
Tweet media one
4
14
77
@iamakulov
Ivan Akulov
5 years
TIL about `patch-package`: an utility that prepares a Git patch after you change something directly in node_modules
Tweet media one
1
16
76
@iamakulov
Ivan Akulov
1 month
Tip: the easiest way to debug your JS performance is the Web Vitals extension, with console logging enabled. • Click around the page • See console logs for how long every click took + where the browser spent most of the time
Tweet media one
1
8
74
@iamakulov
Ivan Akulov
1 year
⚛️ My “React 18 Concurrency, Explained” talk slides are now fully out with text transcription! The talk is an under-the-hood look at how startTransition() & new <Suspense> hydration work in React 18, plus their drawbacks. Some of trivia & full slides below ↓
Tweet media one
2
10
74
@iamakulov
Ivan Akulov
5 years
Super glad to announce that I’m now a Google Developer Expert in web technologies! Hope that would allow me to spread the message about web performance and how it’s important even further.
Tweet media one
8
3
70
@iamakulov
Ivan Akulov
1 year
1) The HTML spec tells that `setTimeout()` calls nested 5+ levels deep must be throttled to 4 ms. This prevents poorly written sites from over-consuming CPU. (Note the word “nested” – unnested `setTimeout()`s were never intended to be throttled! But we’ll get to that in a bit.)
Tweet media one
1
2
71
@iamakulov
Ivan Akulov
3 years
How to use React Profiler:
Tweet media one
1
19
72
@iamakulov
Ivan Akulov
3 years
Ever had a case when you type something in a React app, and it reacts super super slowly? One of the reasons that may happen is unnecessary rerenders. Here’s how to find and fix them (a thread) ⬇️
3
20
71
@iamakulov
Ivan Akulov
2 years
some personal news: i have moved to the netherlands 🇳🇱
7
0
69
@iamakulov
Ivan Akulov
1 year
Shout-out to (by @csswizardry and @RyanTownsend ) which is a super easy way to get a “slow” file. It also added support for images and font files recently!
Tweet media one
3
17
69
@iamakulov
Ivan Akulov
6 years
Turns out if you can save 70 Mb of RAM if you launch a Node.js app with `node index.js` (instead of `yarn start`). Useful if you run lots of apps in a memory-sensitive environment. E.g., this is the process tree of my container. Yarn uses almost as much memory as the app itself!
Tweet media one
5
16
65
@iamakulov
Ivan Akulov
4 years
Okay, it’s Friday – let’s have some web perf fun! 1) Run a webpack build with webpack-bundle-analyzer 2) Take a screenshot and reply to this tweet 3) I’ll dig and see what to improve!
14
19
65
@iamakulov
Ivan Akulov
7 months
Hi #ReactSummitUS 👋 Was excited to speak about React Concurrency – hmu if you have any questions! (Also speaking in a planetarium is 🤯)
Tweet media one
Tweet media two
3
2
64
@iamakulov
Ivan Akulov
2 years
2) There’s not one but several React renders. This is likely an example of the Cascading Rerenders antipattern (gosh I should write an article on this): - you await on several promises - the first promise resolves → you render - the second promise resolves → you rerender - …
Tweet media one
1
2
62
@iamakulov
Ivan Akulov
7 months
Hi #perfnow 👋 Excited to be attending my favorite performance event of the year today and tomorrow. Hit me up to chat about anything React perf :)
Tweet media one
1
2
60
@iamakulov
Ivan Akulov
5 years
Tip from @tkadlec , hot off the press: When you’re profiling a website performance, type `-has-response-header:Content-Encoding` into the network filter. This will find you all resources that are not compressed with gzip/Brotli (= are a good low hanging fruit to optimize)
Tweet media one
1
12
61
@iamakulov
Ivan Akulov
6 years
Ever had cases in webpack where import { Grid, Row } from 'react-bootstrap'; creates a bundle larger than import Grid from 'react-bootstrap/es/Grid.js'; import Row from 'react-bootstrap/es/Row.js'; ? I explained why this happens:
Tweet media one
6
25
60
@iamakulov
Ivan Akulov
6 years
OK, now a serious question. Why is there no <link rel="stylesheet" async> for non-blocking stylesheets? Having this would help to load critical CSS first, and then fetch the remaining styles without blocking the rendering. Right now, you have to use JS hacks to achieve this.
Tweet media one
8
10
60
@iamakulov
Ivan Akulov
4 years
Woah, Chrome 85 will start marking fast sites with the “Fast” badge: The badge will be based on Core Web Vitals metrics. (The metrics data will, apparently, come from the Chrome UX report.)
Tweet media one
3
18
59
@iamakulov
Ivan Akulov
2 years
If you liked this thread, I’d appreciate if you retweet it: (Also consider following @iamakulov – I tweet perf stuff whenever I manage to prevail over the writer’s block. NO WARRANTIES NO REFUNDS)
@iamakulov
Ivan Akulov
2 years
Oh look, my favorite app Spotify being slow! Also it’s 1 am meaning it’s the best time to do some performance profiling. Thread ⬇️
8
242
940
0
3
56
@iamakulov
Ivan Akulov
2 years
Spotify is an Electron app, meaning we can use Chrome DevTools to profile it. To enable Chrome DevTools in Spotify, I install `spicetify` and run `spicetify enable-devtools`. Yay!
Tweet media one
2
5
56
@iamakulov
Ivan Akulov
6 years
🎉 The latest alpha of webpack 4 now minifies everything by default in the production mode! This means that for simple projects, you won’t need to configure anything – just run `webpack --mode production`, and you’ll have a production-ready bundle!
@iamakulov
Ivan Akulov
6 years
*** What surprised me though is that the UglifyJsPlugin is not enabled in the production mode by default. I was seriously expecting that the production mode would be like “I enable it, specify a couple of loaders, and it just works.” Turns out, nope, I still have to add plugins.
1
0
2
2
21
54
@iamakulov
Ivan Akulov
2 months
React Profiler > console.log(). Here’s how to use React Profiler to find out why a specific component rerenders: 1) Go to DevTools Settings and enable this setting:
1
10
55
@iamakulov
Ivan Akulov
4 years
Fun fact: if your webpack bundle has 1K modules, `__webpack_require__()` alone would take 25% of its initial execution time. (`s` is the minified `__webpack_require__`. Trust me)
Tweet media one
Tweet media two
2
11
53
@iamakulov
Ivan Akulov
4 years
Tip: If you’re writing a piece of code that’s logging an error, *always* log the error as a separate `console.log` argument. ✅ This is good: console.log('Error:', e) ❌ This is not: console.log(`Error: ${e.message}`) Here’s a bunch of common patterns & why they’re wrong ⬇
Tweet media one
4
10
53
@iamakulov
Ivan Akulov
2 years
Gah I’m really excited about <Suspense> and hydration in React 18. With React 16-17, `.hydrate()` is often the most expensive JS call in the whole app. You start hydrating the app – and the page freezes for, like, a second:
Tweet media one
1
8
53
@iamakulov
Ivan Akulov
4 years
One of the reasons your Lighthouse score might be bad is third parties. Many (not all, though!) third parties execute a lot of JavaScript. This blocks the main thread & worsens your JS-related Lighthouse metrics (TTI/TBT). Here’re several ways to optimize them – a thread ⬇️
3
11
52
@iamakulov
Ivan Akulov
6 years
A few months ago, I had to build a full-stack app having only front-end experience. So now, I’m writing a high-level backend intro to help you if you appear in the same situation. Here’s part 1 covering Node.js and popular backend libraries →
Tweet media one
1
9
51
@iamakulov
Ivan Akulov
3 years
Have an issue with Cumulative Layout Shift? Here’re a few tips on how to find and fix it:
1
8
50
@iamakulov
Ivan Akulov
3 years
Just discovered Fontsource by @vercel – and woah it’s an amazing project: Fontsource makes self-hosting Google Fonts much easier. You just `npm install` a font and import is as a regular module: import "@​fontsource/open-sans"; This is so great.
Tweet media one
3
16
51
@iamakulov
Ivan Akulov
6 years
@addyosmani Plus: 💻 A training app to optimize everything: 📝 Hints for optimizing a specific library (you can contribute too!):
Tweet media one
1
9
51
@iamakulov
Ivan Akulov
4 years
And this adds up. For one of clients I worked with, replacing the `./components/index.js` file with direct imports dropped around 200 KBs off the main bundle (~10% of its size).
Tweet media one
1
0
51
@iamakulov
Ivan Akulov
4 years
1) It makes code splitting ineffective. When you do import { Button } from './components' you’re importing not only Button but the whole ‘./components’ file. Which means you’re *bundling* the whole file – with all the components it exports.
5
1
50
@iamakulov
Ivan Akulov
1 year
Spent the last couple weeks getting Total Blocking Time down for @framer sites. This chart 🥺
Tweet media one
4
3
50
@iamakulov
Ivan Akulov
4 years
“But shouldn’t tree shaking help?” Tree shaking still works as expected here – it still drops all unused components (“unused” as in “unused in the application”). Tree shaking just doesn’t apply to components that are used in the app but are unused in the current chunk.
10
0
50
@iamakulov
Ivan Akulov
3 years
How Chrome handles — <script> in <head>, vs. — <script async>, vs. — <script defer>, vs. — <script> in <body>:
Tweet media one
0
8
49
@iamakulov
Ivan Akulov
4 years
2) Have a static site? Serving fonts from your own server? Subset these fonts to characters that are actually used on your pages. This will help to load fonts faster. You can easily do this at the build time using — or —
Tweet media one
1
2
50
@iamakulov
Ivan Akulov
5 years
2) How I find what to preload: — Go to DevTools and run a performance recording of how the page loads — Scroll through the Network section and find stuff that blocks page rendering but gets downloaded too late (= too much down in Network section). That’s usually CSS or JS 1/2
Tweet media one
3
15
49
@iamakulov
Ivan Akulov
4 years
@kentcdodds — There’s also a 307 redirect. It works like 302 but preserves the request method. E.g. if you do POST /url, and /url returns “302, go to /url2”, the browser will do GET /url2 next. But if you do POST /url, and it returns “307, go to /url2”, the browser will do POST /url2.
1
2
49
@iamakulov
Ivan Akulov
4 years
TIL Netlify sends all its responses with Cache-Control: max-age=0, must-revalidate (which, effectively, disables caching). And it’s a feature, not a bug:
Tweet media one
3
8
48
@iamakulov
Ivan Akulov
4 years
This is a thread with all links from my today’s HolyJS talk:
2
12
48
@iamakulov
Ivan Akulov
8 years
Probably the best article I’ve read this month. “Prefer duplicat. over the wrong abstract.”:
Tweet media one
6
25
49
@iamakulov
Ivan Akulov
7 years
TIL about webpack’s AgressiveSplittingPlugin which splits your app so it loads faster with HTTP/2:
Tweet media one
0
13
49
@iamakulov
Ivan Akulov
6 years
🔥 A new case study! Analyzed the performance of a section on the Walmart site and wrote what to improve:
Tweet media one
1
14
48
@iamakulov
Ivan Akulov
6 years
That’s seriously one of the best practical articles about design I’ve read in a while:
Tweet media one
0
12
48
@iamakulov
Ivan Akulov
2 years
I’ve been experimenting with tracking app responsiveness and talking to product teams, on and off, over the last couple years. So here’s a guide on how to set up your own React runtime perf monitoring:
5
5
47
@iamakulov
Ivan Akulov
2 years
1) Spotify uses React. This `t.unstable_runWithPriority` function in the flamechart is a key sign of React. Whenever you see it, it’s React running. Which means it’s not surprising that switching playlists is slow! With all my love for React, it defaults into slow performance.
Tweet media one
1
3
45
@iamakulov
Ivan Akulov
6 years
✨ Published a webpack plugin to strip unused locales from Moment.js:
Tweet media one
4
19
46
@iamakulov
Ivan Akulov
1 year
(“Everything” = “all descendants of the changed node”. Although in this case, this was pretty much the whole page.) 6) And that’s how you get a 15K-node recalc from changing a single inline CSS `transform` rule.
3
0
44
@iamakulov
Ivan Akulov
3 years
🧊 How to do partial hydration in React, 2021 edition ↓ “How to do what?” Partial hydration (also “lazy hydration”) lets you skip hydrating components that don’t have any logic. This helps Next.js/Gatsby/SSR sites to initialise faster – and improves Total Blocking Time.
Tweet media one
3
8
44
@iamakulov
Ivan Akulov
4 years
A pattern I see pretty often is when a single file re-exports stuff from lots of other files. If your project has a `components/index.js` file which re-exports all your components (and does nothing else), that’s one example. This is bad for performance – for two reasons.
2
1
44
@iamakulov
Ivan Akulov
4 years
TIL that browsers won’t execute any script until all stylesheets have been downloaded ↓ (Via )
Tweet media one
1
6
44
@iamakulov
Ivan Akulov
5 years
I have two favorite absurdist programming talks. They’re funny and not that serious but have made me think for quite a while: — The Birth and Death of JavaScript — The Future of Programming
1
5
44
@iamakulov
Ivan Akulov
7 years
Hint: when disabling rules in ESLint, comment why you do this. See how easier it is to understand the config:
Tweet media one
Tweet media two
1
9
43