Tari Ibaba

Tari Ibaba is a software developer with years of experience building websites and apps. He has written extensively on a wide range of programming topics and has created dozens of apps and open-source libraries.

10 essential VS Code tips & tricks for greater productivity: Part 2

I’m back with 10 more powerful VS Code tricks to greatly enhance your productivity.

From rapid navigation to speedy code editing, these tips will help you achieve your coding goals faster than ever with a top-notch development experience.

1. Publish repo to GitHub in literal seconds

Do you remember this?

Or maybe you still do this?

It’s the old-school way of publishing your code to GitHub. Create a repo, enter name and details, create remotes…

With VS Code you don’t need to do this anymore. From the Source Control panel you can now quickly publish any local repo to GitHub in just 2 clicks.

And forget git init: creating the repo is even easier with a single click:

You can safely forget about git init

Quickly commit with Ctrl + Enter:

Now easily publish your repo in seconds:

Okay this is definitely more than “seconds” — but that’s because I was somewhere with snail Internet! Ideally it should be like this:

Now we can enjoy our Sigma English rant directly on GitHub:

2. Workspaces for multi-codebase coding

There was a point when I couldn’t do without this feature.

And you won’t when your project is sprawling with numerous inter-connected codebases stored in different folders that all make an important part of the system.

In one of our projects, we had at least 3 folders.

  1. One folder for Firebase-y stuff: Functions, DB security rules, and more.
  2. One folder for the Next.js website/app.
  3. One folder for the cross-platform app codebase.

Imagine the pain of having to switch back and forth between these in 3 open VS Code windows; opening terminals here and there, searching for the wrong file in the wrong codebase, mixing up your Alt + Tab sequence with other open apps, along with the mental confusion and delay it causes anytime you switch apps.

And don’t forget this 👇. This is a pain.

The pain of clicking twice and having to select the correct window based on their titles.

This is why we need workspaces — one window with all the files and subfolders you need.

Sidenote: And if you’re a Windows user struggling to manage your File Explorer you probably want to try this tiny utility (WinENFET).

Every folder is a workspace to VS Code, so you can easily add more folders with File > Add Folder to Workspace…

When everything is done you’ll have all the folders you need and their files easily accessible on the File Explorer pane.

And when you search for files with Ctrl + P or Ctrl + Shift + F, it’ll apply to every file in all the folders:

You can also rapidly create new terminals with any of the folders as the working directory.

With this and Win 11 22H2 Tabs we’ve easily cut our open windows in half.

3. Power editing with side-by-side view

And when you need to work with multiple of those workspace files at once? Split mode has you covered.

I once ported a Flutter Dart class to its JavaScript equivalent and this feature made things much easier and faster.

You can also do this with one of the View: Split Editor... commands.

Split them down or split them right:

Master coder at work.

VS Code also uses this split view to show changes between different file versions saved in the Timeline view or source control.

4. Rapidly copy any line

In most text editors you would drag your mouse over the line and Ctrl + C.

But VS Code is not like most text editors; it’s a clean productivity beast! If not for the name I wouldn’t even have believed this came from Microsoft at first glance.

To copy a line just move the cursor to it and press Ctrl + C.

Whenever you do need to highlight the line you can always use Ctrl + L:

Press it again to highlight more lines below.

And when you just want part of the line you can use Shift + Left / Right to highlight one way or the other.

You can also use Ctrl + X to quickly cut the line where the cursor is without any highlighting.

5. Move line up and down

But if you’re cutting just to paste somewhere else in the same file, then there’s no need to pollute the clipboard.

Simply use the Alt + Up/Down to move the line to wherever you want:

You can even move large selections of multiple lines up and down.

Including code folds:

6. Code folding for monstrous files

I found this invaluable for those gigantic files with tons of functions and methods. Or a huge Flutter or JSX component with unbelievable amounts of deep nesting.

Why some people hate Flutter/Dart

By clicking those down-facing arrows you can easily collapse various segments of the code you’re not currently working with.

Better still the Ctrl + Shift + [ has us covered.

And Ctrl + Shift + [ has us uncovered.

7. Bird’s eye with Outline view

The Outline View is another brilliant way to keep track of large code files.

This feature gives a broad overview of all the symbols and nested symbols in the file: variables, classes, functions… you name it.

You can sort the top-level symbols shown by their position, name, or type.

And when you’re moving around a lot you probably want to keep the Follow Cursor option turned on, to make the selected symbol in the outline match the selected symbol in the file:

Normally you’ll find this view in the File Explorer pane along with Open Editors and Timeline, but as I started using them more often for my own monstrous files I moved it to a separate pane:

8. Go to symbol quickly

And when we stubbornly refuse to split up the file and it gets HUGE; so huge that navigating through the *outline* is becoming a chore, then it’s time to: Go To Symbol.

With the Ctrl + P, @ shortcuts.

And when we finally do the sensible thing and break up the file, we can still search across all those files and more, with Ctrl + T:

9. Go to definition

And once you get to a symbol, you can easily view the definition with Alt + Click or F12:

Double-click the definition popup to fully open the file:

10. Undo cursor quickly

After going to a symbol or viewing its definition we’ll probably want to return to where we were just a few moments prior.

Instead of wasting time relying on short-term memory, you can rest assured that the Ctrl + U keyboard will take you to exactly where you were before.

This is also essential when you go to a line by Ctrl + G.

These 10 powerful tips will elevate your efficiency and make day-to-day coding life easier and more enjoyable.

Key takeaways

  1. Create and publish a GitHub repo in seconds from the Source Control Panel (open with Ctrl + Shift + G G).
  2. Manage multiple folders in a workspace with File> Add Folder to Workspace....
  3. Code with multiple files at once with the View > Split Editor....
  4. Quickly copy a line with Ctrl + C.
  5. Move a line or up or down with Alt + Up / Down.
  6. Collapse code blocks and nested JSX with Ctrl + Shift [.
  7. View all symbols in a file at once with Outline View
  8. Search all symbols in the file with Ctrl + P, @, search in workspace with Ctrl + T
  9. Go to where a symbol was definite with Ctrl + Click or F12.
  10. Revert cursor to previous location with Ctrl + U.

5 reasons why I will never autosave code again (and why you shouldn’t)

Used to be a fan but the pains started outweighing the gains and I had to turn it off forever.

1. I actually don’t have unlimited CPU and RAM

You can’t dare call VS Code a “text editor” in 2023; The bloat is real.

Thanks to Electron, we now live in an era where a near-empty app boasts installation sizes of almost 100MB and proudly gobbles as much as 500MB of RAM.

My dozens of extension probably has something to do with this, but many of these extensions only run for specific types of projects and VS Code commands.

My Dart project here uses only 1 of these (the obvious one), and almost none of the ones coming after.

And none of this 1,200 MB RAM and 16.9% CPU came from my running anything in the terminal! I’m sure that there are some background tasks here and there, but I was doing absolutely nothing — no typing, no building, no long-running tasks.

So what do you think will happen when a build watcher like Create React App is also running? Or a massive memory hog like the Android emulator, constantly reloading on every autosave?

2. Where’s our formatting

VS Code doesn’t care about auto-save in its “auto-format on save” feature.

And why should it happen when you’ve just barely finished typing?

Format-on-save only makes sense for real saves; you usually only press Ctrl + S when you’ve reached some sort of checkpoint; like after a bug fix attempt or a UI change and you want to see our changes in action.

If I want auto-formatting I’m still going to be using Ctrl + D, D my personal keyboard shortcut for auto-formatting. So, no thanks — I’ll rather save and format together.

3. Autosave demands perfection

Autosave wants you to be superhuman.

There’s no breathing room to make mistakes with auto-save; any you type gets written to disk within seconds.

Like can’t you even make a single typo? Can’t you have doubts about which direction to take the code in?

Now you’ll say there’s Git and all, but here’s something to think about: within every commit, there are multiple “micro-commits”. Even if you’re blindly committing every single day there’ll be intermediary changes within those 24 hours that you won’t want to compromise or discard.

With autosave you’ll never get a warning when you close a file after making an unwanted and buggy change — potentially leading to hours of debugging code you thought was working just a few days back.

4. A save should be a micro-commit

Even if you Ctrl + S all the time you’ll probably wait until the end of the statement. Because subconsciously you see Ctrl + S as a micro-commit, and it would be senseless to make those micro-commits at random.

But build watchers like Create React App don’t see it this way.

These tools couldn’t care less about whether the code that’s been auto-saved is complete or not; they just re-build and re-build and re-build again.

Not cool. And at times this put pressure on me to keep typing until I was ready to see the result of my changes.

That’s distracting and unnatural. And you can’t avoid it — the best you can you is to set an autosave delay to reduce how much this happens or close the terminal. The former comes with its own set of problems (as we’ll see), and the latter is simply unthinkable.

5. The lag tradeoff is annoying

Imagine what would happen if VS Code and others weren’t sensible enough to have a delay before any autosave?

Your CPU usage would go crazy; Even if you’re an extra-terrestrial typing god going at 10000 words per minute, you’re still not going to be fast enough; there’ll be a disk write for every single character you put down.

Even worse when you have a heavy build tool like Create React App watching your files and re-building.

So yes there had to be an autosave delay.

But a delay is a delay and no one likes waiting, even if it’s a 3-second delay. Especially when you can get rid of it.

When I press Ctrl + S, I instantly know my changes are saved. I know my build watcher will run immediately. I have instant feedback and there is zero noticeable delay.

I find small things like these make a non-trivial impact on the development experience.

Final thoughts

Comic on Ctrl + S fatigue.
Image source: CommitStrip

So I’m no longer autosaving my code. I had it on for some months back after I discovered such a feature even existed in VS Code, and then I went on and off, and then finally — no more! Like many devs, I’m already used to Ctrl + S so it’s no big deal. Let’s see if my mind changes in the future — how about you?

Coding as digital creations growing into greatness

What if we see coding as a delicate act of nurturing, a digital development brought about by our creativity and the intricate logical reasoning of our minds?

There’s a reason we’re called software *developers*. Developers develop; developers build and grow.

If we can get ourselves to view a software project as a unit of its own, with all its capabilities, potential, and goals, we instantly start to observe numerous interesting connections between programming and other nurturing acts like parenting, gardening, and self-improvement.

From the onset: a freshly scaffolded project, a file, or a folder. There is always a beginning, a birth.

With every single line of code added, deleted, and refactored, the software grows into greatness. With every additional library, package, and API fastened into place, low-level combines into high-level; the big picture unfolds; a fully formed digital creation emerges.

Goal in sight: Plan

Similar to nurturing a budding sapling, the coding process begins with the seed of an idea. You envision the type of product you hope your software will become.

You and your team are to carefully plan and design the structure, algorithms, and flow of the code, much like parents are to create a loving and supportive home by providing the necessary resources, values, and guidance to foster their child’s development.

This thoughtful approach lays the foundation for the entire coding project and boosts the chances of a bright future.

From here to there: Grow

Your codebase needs to grow and improve, but growth on its own is a very vague and generic word. It’s just… an increase.

For any type of growth of any entity to be meaningful, it has to be in the direction of *something*. Something valuable and important.

What’s the first thing I get right now when I search for the meaning of “personal” growth on Google?

This definition prefers to remain vague and makes it clear that personal growth isn’t the same for everyone. You choose your values, set goals based on them, and develop yourself and your environment according to those goals.

But this doesn’t mean you can pick just any goal you want for a happy life; Certain goals are way more beneficial for your well-being than others. A target of progressively gobbling up to 10,000 calories of ice cream every day can never be put in the same league as running a marathon or building solid muscle at the gym.

So the same thing applies to coding. Consciously hoping to write 10,000 lines of code or add 1,000 random features to your software is a terrible aspiration, and will never guarantee high-quality software.

Apart from the explicitly defined requirements, here are some important growth metrics to measure your software project by:

On a lower level, all about the code:

  1. Readability: How easy is it for a stranger to understand your code, even if they know nothing about programming?
  2. Efficiency: How many units of a task can your program complete in X amount of time?
  3. Modularity: Is your code organized into separate modules for different responsibilities? Can one module be swapped out without the entire project crashing into ruins?
  4. Testability and test quality & quantity: How easy is it to test these modules? Do you keep side-effects and mutation to a minimum? And yes, how many actual tests have you written? Are these tests even useful and deliberate? Do you apply the 80/20 rule during testing?

On a higher level closer to the user:

  1. Effective, goal-focused communication: with copy, shape, icons, and more.
  2. Perceived performance: Including immediate responses to user actions, loading bars for long-running actions, buffering, etc.
  3. Number of cohesive features: Are the features enough, and relevant to the overall goal of the software?
  4. Sensual pleasure: Lovely aesthetics, stunning animations, sound effects, and more.
Was working on some open-source download manager a few years back.

This delightful growth process is one of the things that transforms coding from a cold logical activity into a creative, empowering, addictive pursuit. You continually strive, with the ultimate desired outcome in sight, line after line, commit after commit, feature after feature; everlasting progress in an energizing purposeful atmosphere.

What man actually needs is not a tensionless state but rather the striving and struggling for some goal worthy of him. What he needs is not the discharge of tension at any cost, but the call of a potential meaning waiting to be fulfilled by him.

Viktor E. Frankl (1966). “Man’s Search for Meaning”

And yes, just like raising a responsible, well-rounded individual, we may never actually reach a definite, final destination. But it gave us a strong sense of direction. We are going *somewhere* and we have the map.

Have you heard the popular estimates of devs spending on average as much as 75% of their time debugging software?

Yes, it would be nothing but a pipe dream to expect this journey to be easy.

Indeed, as a loving parent does all they can to shield their dearest child from harm’s way, programmers must safeguard their code from bugs and vulnerabilities to produce robust and reliable software. We’ve already talked about testing, and these precautions drastically reduce the chances of nasty errors and mistakes.

But just like with building good personal habits and social skills, you are still going to make mistakes along the way.

With any serious complex codebase, there are still going to be loud, frustrating bugs that bring you to the point of despair and hopelessness. But you simply cannot quit. You are responsible; you are the architect of this virtual world, the builder of digital solutions and innovations. You possess the power to overcome obstacles and transform difficulties into opportunities for growth — your growth, your software’s growth.

And what of when the path toward the goal changes dramatically? Close-mindedness won’t take you far.

Parents need to be adaptable and flexible in adjusting to the child’s changing unique needs and development stages: Software requirements are hardly set in stone. You must be able to incorporate new features into your codebase — and destroy them as time goes on.

Pro-active scalability is nice to have in every software system, but this isn’t always feasible; in the real world you have time and cost constraints and you need to move fast. What was a great algorithm at 1 thousand users could quickly become a huge performance bottleneck at 1 million users. You may need to migrate to a whole new database for increasingly complex datasets needing speedy retrieval.

Takeaway: What coding should be

Like tending to a seedling or guiding a child’s development, coding requires careful planning, thoughtful design, and continuous adaptation. It’s an everlasting process of meaningful growth, measured not only by lines of code but by metrics such as readability, efficiency, beauty, and user-focused features. With each line added, bugs debugged, and obstacles overcome, the software and its creator thrive together on a path filled with purpose, determination, and endless possibilities.

Top 10 new JavaScript features from 2021 to 2023

These 10 amazing features were all about writing shorter, safer, and more expressive code.

1. Private methods and fields

We need our privacy. There’s no two ways about it.

And so do OOP classes; but in JavaScript it was once impossible to strictly declare private members.

It was once impossible to declare private members in a JavaScript class.

A member was traditionally prefixed with an underscore (_) to indicate that it was meant to be private, but it could still be accessed and modified from outside the class.

JavaScript
class Person { _firstName = 'Coding'; _lastName = 'Beauty'; get name() { return `${this._firstName} ${this._lastName}`; } } const person = new Person(); console.log(person.name); // Coding Beauty // Members intended to be private can still be accessed // from outside the class console.log(person._firstName); // Coding console.log(person._lastName); // Beauty // They can also be modified person._firstName = 'Debugging'; person._lastName = 'Nightmares'; console.log(person.name); // Debugging Nightmares

With ES2022, we can now add private fields and members to a class, by prefixing it with a hashtag (#). Trying to access them from outside the class will cause an error:

JavaScript
class Person { #firstName = 'Coding'; #lastName = 'Beauty'; get name() { return `${this.#firstName} ${this.#lastName}`; } } const person = new Person(); console.log(person.name); // SyntaxError: Private field '#firstName' must be // declared in an enclosing class console.log(person.#firstName); console.log(person.#lastName);

Note that the error thrown here is a syntax error, which happens at compile time; the code doesn’t run at all; the compiler doesn’t expect you to even try to access private fields from outside a class, so it assumes you’re trying to declare one.

“Ergonomic brand” checks for private fields

With private fields come a new keyword to safely check if a class object contains a particular one — the in keyword:

JavaScript
class Car { #color; hasColor() { return #color in this; } } const car = new Car(); console.log(car.hasColor()); // true;

It correctly distinguishes private fields with the same names from different classes:

JavaScript
class Car { #color; hasColor() { return #color in this; } } class House { #color; hasColor() { return #color in this; } } const car = new Car(); const house = new House(); console.log(car.hasColor()); // true; console.log(car.hasColor.call(house)); // false console.log(house.hasColor()); // true console.log(house.hasColor.call(car)); // false

And don’t ask me about the name; I also have no idea why they’re called that (do you?).

Ergonomics as far as I’m concerned is all about keeping good sitting posture while using your computer (?) 🤔.

Although I guess you could twist this definition to allow for this new feature — or any new feature for that matter. They’re all about comfort, right? Less pain writing code.

But I guess English isn’t a closed language and you can always add new words and additional definitions (just ask Shakespeare).

2. Immutable sort(), splice(), and reverse()

ES2023 came fully packed with immutable versions of these 3 heavily used array methods.

Okay maybe splice() isn’t used as much as the others, but they all mutate the array in place.

JavaScript
const original = [5, 1, 3, 4, 2]; const reversed = original.reverse(); console.log(reversed); // [2, 4, 3, 1, 5] (same array) console.log(original); // [2, 4, 3, 1, 5] (mutated) const sorted = original.sort(); console.log(sorted); // [1, 2, 3, 4, 5] (same array) console.log(original); // [1, 2, 3, 4, 5] (mutated) const deleted = original.splice(1, 2, 7, 10); console.log(deleted); // [2, 3] (deleted elements) console.log(original); // [1, 7, 10, 4, 5] (mutated)

Immutability gives us predictable and safer code; debugging is much easier as we’re certain variables never change their value.

Arguments are exactly the same, with splice() and toSpliced() having to differ in their return value.

JavaScript
const original = [5, 1, 3, 4, 2]; const reversed = original.toReversed(); console.log(reversed); // [2, 4, 3, 1, 5] (copy) console.log(original); // [5, 1, 3, 4, 2] (unchanged) const sorted = original.toSorted(); console.log(sorted); // [1, 2, 3, 4, 5] (copy) console.log(original); // [5, 1, 3, 4, 2] (unchanged) const spliced = original.toSpliced(1, 2, 7, 10); console.log(spliced); // [1, 7, 10, 4, 5] (copy) console.log(original); // [5, 1, 3, 4, 2] (unchanged)

3. Top-level await

Did you know: F# was the first language to introduce async/await? As far back as 2007! But it took JavaScript 10 good years to catch up.

await pauses execution in the async context until a Promise resolves.

Previously we could only use this operator in an async function, but it could never work in the global scope.

JavaScript
function setTimeoutAsync(timeout) { return new Promise((resolve) => { setTimeout(() => { resolve(); }, timeout); }); } // SyntaxError: await is only valid in async functions await setTimeoutAsync(3000);

With ES2022, now we can:

JavaScript
function setTimeoutAsync(timeout) { return new Promise((resolve) => { setTimeout(() => { resolve(); }, timeout); }); } // ✅ Waits for timeout - no error thrown await setTimeoutAsync(3000);

4. Promise.any()

If you know Promise.all(), then you can easily guess what this does: wait for one Promise to resolve and return the result.

JavaScript
async function getHelpQuickly() { const response = await Promise.any([ cautiousHelper(), kindHelper(), wickedHelper(), ]); console.log(response); // Of course! } async function cautiousHelper() { await new Promise((resolve) => { setTimeout(() => { resolve('Uum, oohkaay?'); }, 2000); }); } async function kindHelper() { return 'Of course!'; } function wickedHelper() { return Promise.reject('Never, ha ha ha!!!'); }

Point to note: Promise.any() still waits for *all* the promises in the current async context to resolve, even though it only returns the result of the first one.

JavaScript
await getHelpQuickly(); // outputs "Of course!" immediately // Still waits for 2 seconds

5. Array find from last

Array find() searches for an array element that passes a specified test condition, and findIndex() gets the index of such an element.

While find() and findIndex() both start searching from the first element of the array, there are instances where it would be preferable to start the search from the last element instead.

There are scenarios where we know that finding from the last element might achieve better performance. For example, here we’re trying to get the item in the array with the value prop equal to y. With find() and findIndex():

JavaScript
const letters = [ { value: 'v' }, { value: 'w' }, { value: 'x' }, { value: 'y' }, { value: 'z' }, ]; const found = letters.find((item) => item.value === 'y'); const foundIndex = letters.findIndex((item) => item.value === 'y'); console.log(found); // { value: 'y' } console.log(foundIndex); // 3

This works, but as the target object is closer to the tail of the array, we might be able to make this program run faster if we use the new ES2022 findLast() and findLastIndex() methods to search the array from the end.

JavaScript
const letters = [ { value: 'v' }, { value: 'w' }, { value: 'x' }, { value: 'y' }, { value: 'z' }, ]; const found = letters.findLast((item) => item.value === 'y'); const foundIndex = letters.findLastIndex((item) => item.value === 'y'); console.log(found); // { value: 'y' } console.log(foundIndex); // 3

Another use case might require that we specifically search the array from the end to get the correct item.

If we’re finding the last even number in a list of numbers, find() and findIndex() produces a totally wrong result:

JavaScript
const nums = [7, 14, 3, 8, 10, 9]; // gives 14, instead of 10 const lastEven = nums.find((value) => value % 2 === 0); // gives 1, instead of 4 const lastEvenIndex = nums.findIndex((value) => value % 2 === 0); console.log(lastEven); // 14 console.log(lastEvenIndex); // 1

We could call the reverse() method on the array to reverse the order of the elements before calling find() and findIndex(). But this approach would cause unnecessary mutation of the array; reverse() reverses the elements of an array in place.

The only way to avoid this mutation would be to make a new copy of the entire array, which could cause performance problems for large arrays.

Also findIndex() still wouldn’t on the reversed array, as reversing the elements would also mean changing the indexes they had in the original array. To get the original index, we would need to perform an additional calculation, which means writing more code.

JavaScript
const nums = [7, 14, 3, 8, 10, 9]; // Copying the entire array with the spread syntax before // calling reverse() const reversed = [...nums].reverse(); // correctly gives 10 const lastEven = reversed.find((value) => value % 2 === 0); // gives 1, instead of 4 const reversedIndex = reversed.findIndex((value) => value % 2 === 0); // Need to re-calculate to get original index const lastEvenIndex = reversed.length - 1 - reversedIndex; console.log(lastEven); // 10 console.log(reversedIndex); // 1 console.log(lastEvenIndex); // 4

It’s cases like where the findLast() and findLastIndex() methods come in handy.

JavaScript
const nums = [7, 14, 3, 8, 10, 9]; const lastEven = nums.findLast((num) => num % 2 === 0); const lastEvenIndex = nums.findLastIndex((num) => num % 2 === 0); console.log(lastEven); // 10 console.log(lastEvenIndex); // 4

This code is shorter and more readable. Most importantly, it produces the correct result.

6. String replaceAll()

We already had replace() for quickly replace a substring within a string.

JavaScript
const str = 'JavaScript is so terrible, it is unbelievably terrible!!'; const result = str.replace('terrible', 'wonderful'); console.log(result); // JavaScript is so wonderful, it is unbelievably terrible!! // Huh?

But it only does so for the first occurrence of the substring unless you use a regex; now we have replaceAll() to replace every single instance of that substring.

JavaScript
const str = 'JavaScript is so terrible, it is unbelievably terrible.'; const result = str.replaceAll('terrible', 'wonderful'); console.log(result); // JavaScript is wonderful, it is unbelievably wonderful. // Now you're making sense!

7. Array with() and at()

at() came first and with() came a year after that in 2023.

They are the functional and immutable versions of single-element array modification and access.

JavaScript
const colors = ['pink', 'purple', 'red', 'yellow']; console.log(colors.at(1)); // purple console.log(colors.with(1, 'blue')); // ['pink', 'blue', 'red', 'yellow'] // Original not modified console.log(colors); // ['pink', 'purple', 'red', 'yellow']

The cool thing about these new methods is how they let you get and change element values with negative indexing.

JavaScript
// index -N is same as index arr.length - N const fruits = ['banana', 'apple', 'orange', 'butter???']; console.log(fruits.at(-3)); // apple console.log(fruits.at(-1)); // butter??? console.log(fruits.with(-1, 'watermelon')); // ['banana', 'apple', 'orange', 'watermelon'] ✅

8. static static static

Static class fields, static private methods (2022).

Static methods access other private/public static members in the class using the this keyword; instance methods with this.constructor:

JavaScript
class Person { static #count = 0; static getCount() { return this.#count; } // Instance method constructor() { this.constructor.#incrementCount(); } static #incrementCount() { this.#count++; } } const person1 = new Person(); const person2 = new Person(); console.log(Person.getCount()); // 2

Static blocks.

Executed only once when the *class* is created. It’s like static constructors in other OOP languages like C# and Java.

JavaScript
class Vehicle { static defaultColor = 'blue'; } class Car extends Vehicle { static colors = []; static { this.colors.push(super.defaultColor, 'red'); } static { this.colors.push('green'); } } console.log(Car.colors); // [ 'blue', 'red', 'green' ]

When they’re multiple static blocks, they’re executed in the order they’re declared, along with any static fields in between. The super property in a static block to access properties of the superclass.

9. Logical assignment operators

They let a variable perform a logical operation with another variable and re-assign the result to itself.

We use them like this:

JavaScript
left ??= right; left ||= right; left &&= right;

They’re as good as:

JavaScript
// More like exactly the same as left = (left ?? right); left = (left || right); left = (left && right);

??=. Quickly assign a value to a variable *if* it is null or undefined (“nullish”).

JavaScript
user.preferredName ??= generateDumbUserName();

||=. Like ??=, but assigns the value for any falsy value (0, undefined, null, '', NaN, or false).

JavaScript
user.profilePicture ||= "/angry-stranger.png";

And then &&=. Something like the reverse; only assigns when the value is truthy (not falsy).

JavaScript
user.favoriteLanguage = await setFavoriteLanguage(input.value); user.favoriteLanguage &&= 'Assembly'; // You're lying! It's Assembly!

10. Numerical separators

Tiny new addition to make big number literals more readable and human-friendly.

JavaScript
const isItPi = 3.1_415_926_535; const isItAvagadro = 602_214_076_000_000_000_000_000; const isItPlanck = 6.626_070_15e-34; const isItG = 6.674_30e-11; // Works for other number bases too...

The compiler completely ignores those pesky underscores — they’re all for you, the human!

Final thoughts

These are the juicy new JavaScript features that arrived in the last 3 years. Use them to boost your productivity as a developer and write cleaner code with greater conciseness, expressiveness and clarity.

New JavaScript pipeline operator: transform anything into a one-liner

With the pipeline operator you’ll stop writing code like this:

and start writing code like this:

So refreshingly clean — and elegant! All those temporary variables are gone — not to mention the time it took to come up with those names *and* type them (not everyone types like The Flash, unfortunately).

The Flash
A fast typist, apparently.

You may have heard this partially true quote attributed to Phil Karlton: “There are only two hard things in computer science: cache invalidation and naming things“.

Using the JavaScript pipeline operator clears out the clutter to boost readability and write data transforming code (basically all code) in a more intuitive manner.

Verbosity should be avoided as much as possible, and this works so much better to compact code than reusing short-named variables:

Hopefully, almost no one codes like this on a regular basis. It’s a pretty horrible technique when done in a large scale; a perfect example showing why we embrace immutability and type systems.

Unlike the pipeline operator, there’s no certainty that the variable always contains the value you set at any given point; you’ll need to climb up the scope to look for re-assignments. We can have used the _ at an earlier point in the code; the value it has at various points in the code is simply not guaranteed.

Now we’re just using an underscore, so without checking out the right-hand side of those re-assignments you can’t quickly know what the type of the variable is, unless you have a smart editor like VS Code (although I guess you could say that doesn’t matter since they’re supposed to be “temporary” — at least until they’re not!).

All in all, poor readability. Fragile and Unstable. 5 times harder for someone new to understand. Also, some would say underscores are “ugly”, especially in languages like JavaScript where they hardly show up.

Okay, so why don’t we just avoid this infestation of temporary underscores, and nest them into one gigantic one-liner?

It’s a mess. The underscore is gone, but who in the world can understand this at a glance? How easy is it to tell how the data flows throughout this code, and make any necessary adjustments.

Understanding, at a glance — this is what we should strive for with every line of code we write.

The pipeline operator greatly outshines every other method, giving us both freedom from temporary variables and readability. It was designed for this.

Here the % only exists within this particular pipeline.

Method chaining?

Who hasn’t used and combined heavily popular array methods like map, filter, and sort? Very hard to avoid in applications involving any form of list manipulation.

This is actually great. There aren’t any temporary variables or unreadable nesting here either and we can easily follow the chain from start to finish.

The formatting lets us easily add more methods at any point in the chain; feature-packed editor like VS Code can easily swap the processing order of two methods, with the Ctrl + Up and Ctrl + Down shortcuts.

There’s a reason why libraries like core http and jQuery are designed like this:

The problem with method chaining is that, we simply can’t use it everywhere. If the class wasn’t designed like that we’re stuck and out in the cold.

It doesn’t work very well with generator methods, async/await and function/method calls outside the object, like we saw here:

But all this and more work with the pipeline operator; even object literals and async import function.

Could have been F# pipes

We would have been using the pipeline operator very similarly to F# pipes, with the above turning out like this instead:

There was an alternative design. But you can already see how this makes for an inferior alternative: Only single-function arguments are allowed and the operation is more verbose. Unless the operation is already a single-argument function call.

It’s weird handling of async/await was also a key reason why it got rejected — along with memory usage concerns. So, forget about F# pipes in JS!

Use the pipeline operator right now

Yes you can — with Babel.

Babel has a nice habit of implementing features before they’re officially integrated in the language; it did this for top-level await, optional chaining, and many others. The pipeline operator couldn’t be an exception.

Just use the @babel/plugin-proposal-pipeline-operator plugin and you’re good to.

It’s optional of course — but not for long.

Prettier the code formatter is already prepared.

Even though we can’t say the same about VS Code or Node.js.

Right now there’s even speculation that % won’t be the final symbol pass around in the pipeline; let’s watch and see how it all plays out.

Final thoughts

It’s always great to see new and exciting features come to the language. With the JavaScript pipeline operator, you’ll cleanse your code of temporary variables and cryptic nesting, greatly boost code readability, efficiency, and quality.

Fix “Cannot read property ‘map’ of undefined” in React

Are you currently experiencing the “Cannot read property ‘map’ of undefined” error in your React app?

The "Cannot read properties of undefined (reading 'map')" error in the browser console.
The “Cannot read properties of undefined (reading ‘map’)” error in the browser console.

This error happens when you call the map() method on a variable that was meant to be an array, but is actually undefined. The variable could be a property or state variable.

For example:

JavaScript
import { useState, useEffect } from 'react'; import { fetchBooks } from './books'; function App() { // ❌ `books` is `undefined` const [books, setBooks] = useState(undefined); useEffect(() => { (async () => { setBooks(await fetchBooks()); })(); }, []); return ( <div> {books.map((book) => ( <div key={book.id}> <h2>Title: {book.title}</h2> <p>Author: {book.author}</p> </div> ))} </div> ); } export default App;

We’ll use this example to learn some easy ways to quickly fix this error in React.

Fix: Use optional chaining operator (?.)

To fix the “Cannot use property ‘map’ of undefined” error in React.js, use the optional chaining operator (?.) to access the map() method:

JavaScript
import { useState, useEffect } from 'react'; import { fetchBooks } from './books'; function App() { const [books, setBooks] = useState(undefined); useEffect(() => { (async () => { setBooks(await fetchBooks()); })(); }, []); return ( <div> {/* ✅ Fix with optional chaining (?.) */} {books?.map((book) => ( <div key={book.id}> <h2>Title: {book.title}</h2> <p>Author: {book.author}</p> </div> ))} </div> ); } export default App;

The optional chaining operator lets us safely access a property on a value that may be undefined or null. If it is, the operator will return undefined immediately and prevent the property access or method call.

JavaScript
const auth = undefined; console.log(auth); // undefined // ✅ No error console.log(auth?.user?.name); // undefined

Fix: Use AND operator (&&)

You can also use the && operator to check if the value is undefined before access it in the JSX:

JavaScript
import { useState, useEffect } from 'react'; import { fetchBooks } from './books'; function App() { const [books, setBooks] = useState(undefined); useEffect(() => { (async () => { setBooks(await fetchBooks()); })(); }, []); return ( <div> {/* ✅ Fix with AND operator (&&) */} {books && books.map((book) => ( <div key={book.id}> <h2>Title: {book.title}</h2> <p>Author: {book.author}</p> </div> ))} </div> ); } export default App;

Here’s what a chain of && does: it returns the right-most variable if all the variables are truthy. Otherwise, it returns the left-most falsy variable:

JavaScript
console.log(undefined && 0 && []); // undefined console.log(10 && null && 'coding'); // null console.log(10 && [5, 7] && 'coding'); // coding

Note: These are the falsy values in JavaScript: undefined, null, 0, false, '' (empty string), and NaN. Every other value is truthy.

Fix: Initialize state variable to empty array

The applies when the array value is a state variable like in our example.

We set the state to an empty array ([]) at the point where it’s created, so it never becomes undefined, preventing the “Cannot read property ‘map’ of undefined” error.

JavaScript
import { useState, useEffect } from 'react'; import { fetchBooks } from './books'; function App() { // ✅ `books` starts out as an array, not `undefined` const [books, setBooks] = useState([]); useEffect(() => { (async () => { setBooks(await fetchBooks()); })(); }, []); return ( <div> {books.map((book) => ( <div key={book.id}> <h2>Title: {book.title}</h2> <p>Author: {book.author}</p> </div> ))} </div> ); } export default App;

Fix: Create a loading state when fetching data

Apart from initializing the state to an empty array you can also display a loading indicator when performing network request, or some other time-consuming operation:

JavaScript
import { useState, useEffect } from 'react'; import { fetchBooks } from './books'; function App() { const [books, setBooks] = useState(undefined); const [loading, setLoading] = useState(true); useEffect(() => { (async () => { setBooks(await fetchBooks()); })(); // ✅ Lists books with .map() after loading completes setLoading(false); }, []); let listEl; if (loading) { listEl = <div>Loading...</div>; } else { listEl = ( <div> {books.map((book) => ( <div key={book.id}> <h2>Title: {book.title}</h2> <p>Author: {book.author}</p> </div> ))} </div> ); } return <div>{listEl}</div> } export default App;

Most web user interfaces have at least three states – loading, error, and success state, and we could be better off representing all three of them:

JavaScript
import { useState, useEffect } from 'react'; import { fetchBooks } from './books'; // TypeScript // type UIState = 'loading' | 'error' | 'success'; function App() { const [books, setBooks] = useState(undefined); const [state, setState] = useState('loading'); // TypeScript // const [state, setState] = useState<UIState>('loading'); useEffect(() => { try { (async () => { setBooks(await fetchBooks()); setState('success'); })(); // ✅ Lists books with .map() after loading completes } catch (err) { setState('error'); } }, []); const listViewMap = { loading: <div>Loading...</div>, error: <div>An error occured, try again later.</div>, success: ( <div> {books.map((book) => ( <div key={book.id}> <h2>Title: {book.title}</h2> <p>Author: {book.author}</p> </div> ))} </div> ), }; const listEl = listViewMap[state]; return <div>{listEl}</div>; } export default App;

Fix: Check the value assigned to the array

Maybe you initialized the array properly and did everything else right, but you set it to undefined at some point in the code.

If the array is to be filled with API data, are you sure you’re making the network request properly.

For example, if our fetchBooks() function was defined like below, it will cause a “Cannot read property ‘map’ of undefined” error when there’s a network error on the user’s device.

JavaScript
export async function fetchBooks() { const res = fetch('https://api.example/books', { headers: { 'Content-Type': 'application/json', Accept: 'application/json', }, }); // ❌ Will return `undefined` if status code is not 200 return res.status === 200 ? (await res).json() : undefined; }

Make sure you handle all the edge cases in your app.

Fix: Check your variable names

Sometimes the error happens due to a error in your variable names.

JavaScript
import { useState, useEffect } from 'react'; import { fetchBooks } from './books'; function App() { const [books, setBooks] = useState([]); useEffect(() => { (async () => { setBooks(await fetchBooks()); })(); }, []); return ( <div> {books.map((books) => ( <div key={book.id}> <h2>Title: {book.title}</h2> <p>Author: {book.author}</p> </div> ))} </div> ); } export default App;

Here we mistakenly create a books variable in the map() callback but we use book identifier in the JSX, which is what we actually intended. The causes the unfortunate error, aside from the fact that it shadows the outer books state variable.

Master JavaScript generators: 5 inspiring practical use cases

You will be an expert on JavaScript generators by the time you’re done reading this.

JavaScript generators are way more than just a fancy feature and we are going to discover many powerful use cases for them, including creating engaging animations, and streaming videos over the internet.

If you’ve never heard of them you may be missing out.

Generators are simply functions that you can pause and resume whenever you want — they don’t execute continuously.

The asterisk * marks the function as a generator, and yield generates values on demand from a .next() call, until the generator is done.

JavaScript
function* myGenerator() { yield 'Hello'; yield 'World'; } const iterator = myGenerator(); console.log(iterator.next()); // {value: 'Hello', done: false} console.log(iterator.next()); // {value: 'World', done: false} console.log(iterator.next()); // {value: undefined, done: true}

It’s just like how a physical generator produces electricity not all at once, but as time goes on.

Instead of calling next() you can use the for..of loop, which is great for when the generator generates a lot of data.

JavaScript
function* countTo100() { for (let i = 0; i < 100; i++) { yield i; } } for (const item of countTo100()) { console.log(item); } // 1, 2, ... 100

Lazy evaluation

“Calculate only when necessary.”

Much unlike regular functions in JavaScript that execute entirely and return the result.

Let’s say you want a sequence of numbers, but you’re not sure how many. Here’s how a generator helps:

JavaScript
function* numberGenerator() { let number = 1; // infinite loop won't cause freeze in generator // -- execution pauses after `yield` while (true) { yield number++; } } const numbers = numberGenerator(); console.log(numbers.next().value); // 1 console.log(numbers.next().value); // 2 // you can continue this as long as you need

With generators, you get the next number only when you ask for it.

Better memory utilization

Generators don’t hold all the results in memory, they generate them on the fly.

Imagine you need a sequence of a million numbers. With a regular function, you’d need to store all these numbers in an array, using up significant memory.

A generator is much more efficient:

JavaScript
function* bigNumberGenerator() { let number = 1; while (number <= 100000000) { yield number++; } } const bigNumbers = bigNumberGenerator(); const chunk = 10; for (let i = 0; i < chunk; i++) { const value = bigNumbers.next().value; // Use next 10 values... }

Handling asynchronous tasks

Did you know that Babel transpiles async/await to generators for JavaScript versions that don’t support it natively?

Babel tranforms this:

JavaScript
this.it('is a test', async function () { const name = await 'coding beauty' const num = await new Promise(resolve => resolve(10)); console.log(`Name: ${name}, Num: ${num}`); });

To this:

JavaScript
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then( function (value) { return step('next', value); }, function (err) { return step('throw', err); } ); } } return step('next'); }); }; } myFunc( 'generator async/await example', _asyncToGenerator(function* () { const name = yield 'coding beauty'; // yield, not await const num = yield new Promise((resolve) => resolve(10)); console.log(`Name: ${name}, Num: ${num}`); }) );

Typing animations

Typing animations grab the attention of users and make your website more visually appealing.

They add personality and character to a website by mimicking the typing behavior of a human to create a more human-like experience and establish a unique brand identity.

So with all these benefits you’re feeling excited about infusing your webpages with these energetic visual effects.

Here would be a decent way to go about it, using recursion and setTimeout():

JavaScript
function typeAnimation(text, index = 0) { if (index < text.length) { document.body.innerHTML += text.charAt(index); index++; setTimeout(typeAnimation, 500); } }

But, it’s cases like this where generators shine:

JavaScript
function* typeAnimation(text) { for(let char of text) { yield char; } }

Since we can generate values whenever we want, we can do this in time intervals using setInterval().

JavaScript
const generatorInstance = typeGenerator("Coding Beauty"); function startTypingEffect(elementId, generatorInstance) { let element = document.getElementById(elementId); setInterval(() => { let result = generatorInstance.next(); !result.done && element.innerHTML += result.value; }, 100); } startTypingEffect("type-here", generatorInstance);
JavaScript
<div id="type-here"></div>

Asynchronous handling

Note: This is NOT the same as being the bedrock of async/await, like we saw earlier. We’re talking about async generators here.

JavaScript
function* asyncGenerator() { yield new Promise(resolve => setTimeout(() => resolve('Task 1 completed'), 2000)); yield new Promise(resolve => setTimeout(() => resolve('Task 2 completed'), 3000)); // ... }

And here’s how we can use this generator:

JavaScript
const asyncGen = asyncGenerator(); asyncGen.next().value.then(console.log); asyncGen.next().value.then(console.log);

This is such a powerful tool for streaming data in web app in a structured, readable manner — just look at this function that buffers and streams data for a video-sharing app like YouTube:

JavaScript
async function* streamVideo({ id }) { let endOfVideo = false; const downloadChunk = async (sizeInBytes) => { const response = await fetch( `api.example.com/videos/${id}` ); const { chunk, done } = await response.json(); if (done) endOfVideo = true; return chunk; }; while (!endOfVideo) { const bufferSize = 500 * 1024 * 1024; yield await downloadChunk(bufferSize); } }

To consume this async generator, we’d use the for await..of loop:

JavaScript
for await (const chunk of streamVideo({ id: 2341 })) { // process video chunk }

redux-saga

redux-saga is a library for managing side effects in applications, boasting over 1 million weekly downloads.

Generators play a big role in this library, handling the redux actions to make testing and error handling easier.

Take a look at this simple saga:

JavaScript
import { call, put, takeEvery } from 'redux-saga/effects'; function* fetchData(action) { try { const data = yield call(Api.fetchUser, action.payload.userId); yield put({type: "USER_FETCH_SUCCEEDED", data}); } catch (e) { yield put({type: "USER_FETCH_FAILED", message: e.message}); } } function* mySaga() { yield takeEvery("USER_FETCH_REQUESTED", fetchData); }
JavaScript
import createSagaMiddleware from 'redux-saga'; import { createStore, applyMiddleware } from 'redux'; import mySaga from './sagas'; const sagaMiddleware = createSagaMiddleware(); const store = createStore( reducer, applyMiddleware(sagaMiddleware) ); sagaMiddleware.run(mySaga)

Whenever the USER_FETCH_REQUESTED action is dispatched, redux-saga runs the generator which in turn calls fetchData() to perform the asynchronous network request.

A note on return

What happens when you return a value in a generator function? Let’s see:

JavaScript
function* soccerPlayers() { yield 'Ronaldo'; yield 'Messi'; return 'Neymar'; } for (const player of soccerPlayers()) { console.log(player); } // Ronaldo // Messi

Why isn’t Neymar part of the generated values?

Let’s use .next() to find out if the done property has something to do with it:

JavaScript
function* soccerPlayers() { yield 'Ronaldo'; yield 'Messi'; return 'Neymar'; } const playerGen = soccerPlayers(); console.log(playerGen.next()); console.log(playerGen.next()); console.log(playerGen.next()); /* { value: 'Ronaldo', done: false } { value: 'Messi', done: false } { value: 'Neymar', done: true } */

It turns out that return is not considered a generated value, so for..of doesn’t process it.

Do you remember our very first example:

JavaScript
function* myGenerator() { yield 'Hello'; yield 'World'; } const iterator = myGenerator(); console.log(iterator.next()); // {value: 'Hello', done: false} console.log(iterator.next()); // {value: 'World', done: false} console.log(iterator.next()); // {value: undefined, done: true}

You can see that generators only produce values until, but not including when done is true.

So return completes the generator and terminates the function (like any other).

JavaScript
function* myGenerator() { yield 'Hello'; return 'End'; yield 'This will not be executed'; }

Final thoughts

JavaScript generators offer powerful solutions for control flow, memory efficiency, and asynchronous handling. They enhance web development with dynamic animations, streaming data, and managing side effects.

Let’s embrace the versatility of generators for elegant and efficient JavaScript programming.

[SOLVED] Cannot use import statement outside a module in JavaScript

Are you currently experiencing the “Cannot use import statement outside a module” error in JavaScript?

The "Cannot use import statement outside a module" error in a browser console.
The “Cannot use import statement outside a module” error in a browser console.

This is a well-known error that happens when you use the import keyword to load a file or package that is not a module.

Let’s learn how to easily fix this error in Node.js or the browser.

Fix “Cannot use import statement outside a module” in browser

Fix: Set type="module" in <script> tag

The “Cannot use import statement outside a module” error happens in a browser when you import a file that is a module without indicating this in the <script> tag.

index.html
<!DOCTYPE html> <html> <head> <title>Coding Beauty</title> <link rel="icon" href="favicon.ico" /> </head> <body> A site to make you enjoy coding <!-- ❌ SyntaxError: Cannot use import statement outside a module --> <script src="index.js"></script> </body> </html>

To fix it, add type="module" to the <script> tag.

index.html
<!DOCTYPE html> <html> <head> <title>Coding Beauty</title> <link rel="icon" href="favicon.ico" /> </head> <body> A site to make you enjoy coding <!-- ✅ Loads script successfully --> <script type="module" src="index.js"></script> </body> </html>

Fix “Cannot use import statement outside a module” in Node.js

The “Cannot use import statement outside a module” error happens when you use the import keyword in a Node.js project that doesn’t use the ES module import format.

index.js
// ❌ SyntaxError: Cannot use import statement outside a module import axios from 'axios'; const { data } = ( await axios.post('https://api.example.com/hi', { hello: 'world', }) ).data; console.log(data);

Fix: Set type: "module" in package.json

To fix the error in Node.js, set the package.json type field to “module”.

package.json
{ // ... "type": "module", // ... }

Now you can use the import statement with no errors:

index.js
// ✅ Imports successfully import axios from 'axios'; const { data } = ( await axios.post('https://api.example.com/hi', { hello: 'world', }) ).data; console.log(data);

If there’s no package.json in your project, you can initialize one with the npm init -y command.

Shell
npm init -y

The -y flags creates the package.json with all the default options – no user prompts for the fields.

Fix: Use require instead of import

Alternatively, you can fix the “Cannot use import statement outside a module” error in JavaScript by using the require() function in place of the import syntax:

index.js
// ✅ Works - require instead of import const axios = require('axios'); const { data } = ( await axios.post('https://api.example.com/hi', { hello: 'world', }) ).data; console.log(data);

But require() only works on the older, Node-specific CommonJS module system, and these days more and more modules are dropping support for CommonJS in favor of ES6+ modules.

For example, if you use require() for modules like got, node-fetch, and chalk, you’ll get the “require of ES modules is not supported error”.

index.js
// ❌ [ERR_REQUIRE_ESM]: require() of ES Module not supported const chalk = require('chalk'; console.log(chalk.blue('Coding Beauty'));

If you can’t switch to an ES module environment, you can fix this by dynamically importing the module with the async import() function. For example:

index.js
// CommonJS module // ✅ Imports successfully const chalk = (await import('chalk')).default; console.log(chalk.blue('Coding Beauty'));

Or, you can downgrade to an earlier version of the package that supported CommonJS and require().

Shell
npm install chalk@4 # Yarn yarn add got@4

Here are the versions you should install for various well-known NPM packages to avoid this ERR_REQUIRE_ESM error:

Package versions for downgrading to fix the "Cannot use import statement outside a module" error in JavaScript.

Fix “Cannot use import statement outside a module” in TypeScript

If the module field in tsconfig.json is set to commonjs, any import statement in the TypeScript source will change to require() calls after the compilation and potentially cause this error.

tsconfig.json
{ //... "type": "module" }

Prevent this by setting module to esnext or nodenext

tsconfig.json
{ "type": "module" }

nodenext indicates to the TypeScript compiler that the project can emit files in either ESM or CommonJS format.

The secret trick Google uses to track everything you search online

Google now has at least 3 solid ways to track your search clicks and visits.

Have you ever tried to copy a URL directly from Google Search?

When I did that a few months ago, I unexpectedly got something like this from my clipboard.

Plain text
https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&cad=rja&uact=8&ved=2ahUKEwjUmK2Tk-eCAxXtV0EAHX3jCyoQFnoECAkQAQ&url=https%3A%2F%2Fwww.codingbeautydev.com%2Fblog%2Fvscode-tips-tricks&usg=AOvVaw0xw4tT2wWNUxkHWf90XadI&opi=89978449

In my curiosity, I visited the page, and it took me straight to the URL I originally wanted.

This cryptic URL turned out to be a middleman that would redirect you to the actual page.

But what for?

After some investigation, I discovered that this was how Google Search had been recording our clicks and tracking every single visited page.

They set custom data- attributes and a mousedown event on each link in the search results page:

HTML
<a jsname="UWckNb" href="https://www.codingbeautydev.com/blog/vscode-tips-tricks" data-jsarwt="1" data-usg="AOvVaw0xw4tT2wWNUxkHWf90XadI" data-ved="2ahUKEwjUmK2Tk-eCAxXtV0EAHX3jCyoQFnoECAkQAQ">

A JavaScript function would change the href to a new URL with several parameters including the original URL, as soon as you start clicking on it.

JavaScript
import express from 'express'; const app = express(); app.get('/url', (req, res) => { // Record click and stuff... res.redirect(req.query); }); app.listen(3000);

So even though the browser would show the actual URL at the bottom-left on hover, once you clicked on it to copy, the href would change instantly.

Why mousedown over click? Probably because there won’t be a click event when users open the link in a new tab, which is something that happens quite often.

And so after right-clicking to copy like I did, mousedown would fire and the href would change, which would even update that preview URL at the bottom-left.

The new www.google.com/url page would log the visit and move out of the way so fast you’d barely notice it — unless your internet moves at snail speed.

They use this data for tools like Google Analytics and Search Console so site owners can improve the quality of their search results and pages by analyzing click-rate — something probably also using as a Search ranking factor. Not to mention recording clicks on Search ads to rake all the billions of yearly ad revenue.

Google Search Console. Source: Search Console Discover report now includes Chrome data

But Google got smarter.

They realized this URL tracking method had a serious issue for a certain group. For their users with slower internet speeds, the annoying redirect technique added a non-trivial amount of delay to the request and increased bounce rate.

So they did something new.

Now, instead of that cryptic www.google.com/url stuff, you get… the same exact URL?

With the <a> ping attribute, they have now successfully moved their tracking behind the scenes.

The ping attribute specifies one or more URLs that will be notified when the user visits the link. When a user opens the link, the browser asynchronously sends a short HTTP POST request to the URLs in ping.

The keyword here is asynchronously — www.google.com/url quietly records the click in the background without ever notifying the user, avoiding the redirect and keeping the user experience clean.

Browsers don’t visually indicate the ping attribute in any way to the user, which appears to go against the specification

When the ping attribute is present, user agents should clearly indicate to the user that following the hyperlink will also cause secondary requests to be sent in the background, possibly including listing the actual target URLs.

HTML Standard (whatwg.org)

Not to mention a privacy concern, which is why browsers like Firefox refuse to enable this feature by default.

In Firefox Google sticks with the mousedown event approach:

There are many reasons not to disable JavaScript in 2023, but even if you do, Google will simply replace the href with a direct link to www.google.com/url.

HTML
<a href="/url?sa=t&source=web&rct=j&url=https://www.codingbeautydev.com/blog/vscode-tips-tricks...">10 essential VS Code tips and tricks for greater productivity</a>

So, there’s really no built-in way to avoid this mostly invisible tracking.

Even the analytics are highly beneficial for Google and site owners in improving result relevancy and site quality, as users we should be aware of the existence and implications of these tracking methods.

It raises important questions about online privacy and the control we have over our personal information. As technology continues to advance, it’s crucial to critically evaluate the trade-offs between convenience and privacy in our digital lives.

[SOLVED] “auth/unauthorized domain” error in Firebase Auth

In this article, we’re going to learn how to easily fix the “auth/unauthorized domain” error which happens in the Firebase Authentication Web SDK.

What causes “auth/unauthorized domain” error in Firebase?

The Firebase “domain is not authorized” error happens when you try to use an API from Firebase Auth in a domain that is not authorized by Firebase.

It may occur in the FirebaseUI, or in your browser console.

The “auth/unauthorized” Firebase error occurring in FirebaseUI.

How to fix “auth/unauthorized domain” error in Firebase

To fix the “Domain not authorized” error in Firebase Auth, go to the Firebase Authentication Authorized domains settings for your project, using this link: https://console.firebase.google.com/project/_/authentication/settings.

Or by following these steps from the Firebase Console for your project in this image:

An image with steps to go to the Firebase Authentication Authorized domains settings page.

Enter the new domain in the dialog that shows after clicking Add domain:

Enter the new domain to register it as an authorized domain in Firebase Authentication.