JavaScript & Web Tech

Why do we use javascript for web development? (and not any other language)

JavaScript is a popular programming language used for web development due to several key reasons:

  1. Client-Side Interactivity: JavaScript is primarily used to add interactivity and dynamic behaviour to websites on the client-side, meaning it runs directly in the user's web browser.
  2. Browser Compatibility: JavaScript is supported by all major web browsers. Browsers have built-in JavaScript engines that execute JavaScript code directly in the browser, making it a consistent and cross-browser solution. Therefore we do not need to maintain codebase in different languages for different browsers
  3. Versatility: JavaScript can be used for many use cases of web development, like
  1. doing form validation
  2. creating interactive elements
  3. creating animations
  4. handling events (e.g., clicks, mouse movements)
  5. manipulating the Document Object Model (DOM)
  6. handling AJAX requests and interacting with server-side APIs
  7. updating the page content without requiring a full page reload, providing a more seamless user experience.
  1. Large Community and Libraries: JavaScript has an extensive and active developer community. This means there are countless libraries, frameworks, and tools available that can significantly speed up the development process and help solve common web development challenges. Examples of popular JavaScript libraries and frameworks include React, Angular, and Vue.js.
  2. Support Native Web Technologies: Modern web technologies like HTML5, CSS3, and Web APIs are designed to work seamlessly with JavaScript. Many of these technologies are integrated into web browsers and accessible via JavaScript APIs.
  3. Security and Browser Sandboxing: JavaScript runs within a browser's sandboxed environment, ensuring security by preventing direct access to system resources. This is crucial for maintaining user safety and data privacy.

How does the chrome v8 JS engine work?

The V8 engine is an open-source JavaScript engine developed by the Google Chrome team.

The combination of parsing, profiling, optimization, and JIT compilation in the Chrome V8 engine contributes to the fast and efficient execution of JavaScript code in web applications.

  1. Parsing:
  1. The V8 engine's first step is to parse the JavaScript code into an Abstract Syntax Tree (AST), which represents the structure of the code in a tree-like format.
  1. Profiling:
  1. After parsing, the JavaScript code is first executed in an "interpreted" mode.
  2. As the JavaScript code is interpreted, the V8 engine uses profilers to collect runtime information about the code's behaviour. This includes tracking which parts of the code are executed frequently (hot paths) and which data types are commonly used.
  1. Optimisation & Just-In-Time (JIT) Compilation:
  1. Based on the gathered information, the V8 engine employs JIT compilation to optimise specific code paths.
  2. JIT compilation generates machine code that is tailored for the actual runtime behaviour of the code, taking advantage of the specific data types encountered during interpretation.
  1. Execution:
  1. Once a code path has been compiled, the resulting machine code is stored in an "executable memory" area.
  2. Subsequent executions of the same code path can directly use the compiled machine code, which significantly speeds up execution compared to interpretation.
  1. Garbage Collection:
  1. JavaScript is a garbage-collected language, meaning that memory management is handled automatically.
  2. V8's garbage collector periodically identifies and deallocates memory that is no longer needed, preventing memory leaks.
  1. Asynchronous Execution:
  1. V8 handles asynchronous operations like timers and network requests using the event loop, which allows non-blocking execution and responsiveness.

In summary, the V8 engine first parses the JS code and converts it into AST(a tree-like representation of the structure of the code) then it executes the code in interpretation mode and profiles the code (like which part of the code is frequently used and commonly used data types). Based on the runtime behaviour and profiling data, it optimises the code using JIT Compilation to selectively compile frequently executed code paths into highly optimised machine code and store it in a “executable memory”. Subsequent executions of the same code path can directly use the compiled machine code, which significantly speeds up execution compared to interpretation.

This combination of interpretation and JIT compilation helps achieve a balance between startup speed and runtime performance for JavaScript execution.

Does v8 javascript engine compile or interpret the code?

The V8 JavaScript engine employs a combination of both compilation and interpretation to execute JavaScript code. This approach is known as "Just-In-Time" (JIT) compilation, which aims to balance the benefits of both compilation and interpretation for optimising the performance of JavaScript execution.

Compiler vs Interpreter (Compilation vs execution)

Everything in JavaScript happens inside an Execution Context

When JavaScript code is executed, Execution Context is created and it is called Global Execution Context (GEC).

  1. JavaScript program is executed in TWO PHASES inside Execution Context
  1. MEMORY ALLOCATION PHASE - JS engine goes throughout the program and allocates memory of Variables and Functions declared and stores undefined and function code respectively as its value.
  2. CODE EXECUTION PHASE -  JS engine now goes through the code line by line and executes the code.
  1. A Function is invoked when it is called and it acts as another MINI PROGRAM and creates its own Execution Context.
  2. Return keyword returns the Control back to the Parent Execution-Context, from where the Function is called and the Execution Context of that function is DELETED.
  3. CALL STACK maintains the ORDER of execution of Execution Contexts. It CREATES Execution Context whenever a Program starts or a Function is invoked and it pops out the Execution Context when a Function or Program ENDS.
  4. Undefined is like a placeholder till a variable is not assigned a value.

Execution Context

JavaScript interpreter creates a new context whenever it’s about to execute a function or script. Every script/code starts with an execution context called a global execution context (GEC). And every time we call a function, a new execution context is created and is put on top of the call stack. The same pattern follows when we call the nested function which can call another nested function.

Execution context has two components

  1. Memory component / variable environment:  variable and function values are stored in a key value format.
  2. Code component / thread of execution: It is a place where whole JavaScript code is executed

this

this is a property of the execution context. only function calls establish a new this context (because it creates a new execution context)

Hoisting

JavaScript Hoisting refers to the process whereby the interpreter appears to move the declaration of functions, variables or classes to the top of their scope, prior to execution of the code.

https://developer.mozilla.org/en-US/docs/Glossary/Hoisting

variables defined with var and function declaration are hoisted, var takes undefined value and function contains its code.

let and const are also hoisted (in a different way: they exist in Temporal Dead Zone (TDZ) for the time being)

classes are hoisted — but they're in the Temporal Dead Zone (TDZ) until their declaration line runs (identical to let/const)

The Temporal Dead Zone exists until a variable is declared and assigned a value.

window.<variable> OR this.<variable> will not give the value of a variable defined using let or const.

Kind

What gets hoisted?

Value before declaration

Example

var

Binding + initialized to undefined

undefined

console.log(x); // undefined

var x = 5;

function declaration

Binding + the function itself

the function code

foo(); // function code

function foo() {}

let, const, class

Binding only — TDZ until init

throws ReferenceError

new Foo(); // throws ReferenceError

class Foo {}

precedence order:  variable assignment > function declaration > variable definition

e.g

var a = 10;

function a() {}

typeof a will be number

var a;

function a() {}

typeof a will be function because variable is not assigned only defined

function a() {}

var a;

typeof a will be function because variable is not assigned only defined

Blocked Scope

let & const is block scoped, means it is not accessible outside of the block, but var can be accessible because var is function scoped.

Hoisting vs Memory Component of Execution Context

Hoisting is a mechanism in JavaScript where variable and function declarations are moved to the top of their containing scope during the compile phase. This means that regardless of where variables and functions are declared in the code, they are moved to the top of their scope.

The memory component of an execution context refers to the memory space allocated for variables and function references during the execution of a JavaScript program.

Event Loop

Event loop is used to handle asynchronous operations in JS.

The event loop is a constantly running process that monitors both the callback queue and the call stack.If the call stack is not empty, the event loop waits until it is empty and places the next function from the callback queue to the call stack. If the callback queue is empty, nothing will happen:

https://www.javascripttutorial.net/javascript-event-loop/

  1. Callback functions of Web APIs are first stored in the Web API environment and then transferred to some queue from where the event loop picks them up and places it in the call stack, whenever the call stack is empty.
  2. All Callback functions (except promise callback and mutation observer) are transferred to callback queue or task queue or macrotask queue.
  3. Promises callback and mutation observer are transferred to the microtask queue.
  4. Microtask queue tasks are given priority over Macrotask queue tasks, event loop will pick tasks from callback queue, only when all tasks of microtask queue are done.
  5. Too many microtask queue tasks generated can cause Starvation (not giving time to callback queue tasks to execute).
  6. If the value of the expression following the await operator is not a Promise, it's converted to a resolved Promise i.e. await will also trigger an item into microtask queue

Order:

    - Statement

    - Microtask Queue - Promise callbacks/mutation observer (microtask queue)

    - Web worker (port/window.eventHander, etc..)

    - Macrotask Queue - all other callbacks e.g. setTimeout (callback/macrotask queue)

JavaScript is synchronous single-threaded language

Synchronous means one at a time i.e. one line of code is being executed at a time in order the code appears.

Single threaded means that one command is being executed at a time.

So in JavaScript one thing is happening at a time.

Sync kar lo/ sync me rakhna (we generally use in our day-to-day life): means keep everything in a single thread, means data in all devices are the same.

JavaScript is single-threaded, meaning it processes one task at a time. However, it can handle asynchronous operations efficiently using the event loop.

Synchronous

One task at a time (single threaded)

Asynchronous

multiple task at a time (multi threaded)

parallel programming

Asynchronous Function

Functions running in parallel with other functions are k/n as asynchronous function

e.g. fetch, setTimeout, setInterval, etc.

How to perform a specific task on completion of asynchronous function

  1. Promise
  2. callback function - it grows the code horizontally and gives control to the asynchronous function

Inversion of control

When we pass a callback (instead of using a promise) to an asynchronous function to process (after the async task is done), we give control to that async. function. This is k/n as inversion of control.

Callback Hell

Callback hell or "pyramid of doom," is a situation where there is a deep and complex nesting of callback functions. This generally happens while handling asynchronous code, where operations are performed one after the other, and each operation requires a callback to handle its result.

It grows the code horizontally

Async flow

Async flow (asynchronous control flow) refers to the design patterns, mechanisms, and architectural structures used in programming to manage operations that take time to complete without freezing or blocking the execution of the main program thread.

In a traditional synchronous flow, code executes strictly line-by-line. If line 2 requests data from a slow database, lines 3, 4, and 5 must sit completely frozen until that network round-trip finishes.

In an asynchronous flow, the program initiates a time-consuming task, sets it aside in the background, and immediately moves on to execute subsequent lines of code. When the background task finally completes, the program hooks back into it and handles the result.

Asynchronous control flow can be handled in 4 ways

  1. Callbacks
  2. then/catch in Promises
  3. async/await in Promises
  4. Reactive Streams & Observables: For highly complex async workflows where data arrives continuously over time (like WebSockets, mouse coordinates, or user typing events), arrays or single promises aren't enough, Reactive Streams (via libraries like RxJS) can be used

Reactive Streams

A Reactive Stream is an architectural standard for handling continuous, real-time data feeds asynchronously without blocking memory. Its primary superpower is managing Backpressure—preventing a fast data producer from crashing a slow data consumer.

Without Reactive Streams: If a server pushes 10,000 logs/second to a database that can only process 1,000/second, the unmatched data builds up in memory, causing an Out of Memory (OOM) crash.

With Reactive Streams: The system switches from a Push model (forcing data onto the consumer) to a Pull model (the consumer explicitly asks for what it can handle). The consumer can tell the producer: "Send me exactly 5 items, then wait until I ask for more."

The Stream Lifecycle (The Handshake)

  1. Subscribe: The Subscriber connects to the Publisher.
  2. Handshake: The Publisher hands over a Subscription object.
  3. Request: The Subscriber requests specific delivery limits (e.g., request(2)).
  4. Deliver: The Publisher fires onNext() to deliver those 2 items, then halts completely until the next explicit request token arrives.

JavaScript (Frontend): Implemented via RxJS (library) using Observables to handle complex, messy UI streams like real-time search typing debounces, drag-and-drop mechanics, and WebSocket updates.

Promise

Promise is an object that represents the eventual completion of asynchronous operation. It represents a result that may be available now, or in the future or never.

Promises are used to handle asynchronous operations in JavaScript.

Promise can be consumed/handled in 2 ways:

  1. then, catch
  2. async/await

Promise.all vs allSettled, race, any

if the given array of promises does include a value instead of promise, then it directly returns the value in the result array

Async/Await

  1. Async function is used to handle asynchronous tasks in JavaScript.
  2. Asynchronous tasks are generally handled though promises and promises can be consumed in 2 ways, async/await or then/catch.
  3. Await keyword can only be used inside an async function.
  4. Async function always returns a promise, if we directly return a promise from the async function then it gets returned as it is, otherwise if we return any value, then resolved promise with that value will be returned.

Difference b/w .then/catch & async/await

In async/await JS engine appears/fakes to wait at the line where await is used, till the given promise is resolved and does not proceed further in the code.

While in .then/catch, the JS engine does not wait for the promise to resolve and proceed further in the code, and whenever the promise gets resolved it calls the attached callback function.

And None of them block the thread or call stack.

Closure

Closures occurs when a function retains access to variables from its outer (enclosing) function even after that outer function has completed execution.

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). i.e. closure = function + its lexical environment

Whenever a function is returned from a function, even if it is vanished in the execution context, It's not just that function alone it returns but the entire closure (function along with its lexical scope).

In closure reference of the variables in the scope is there, but not its value

ref: https://www.youtube.com/watch?v=eBTBG4nda2A&list=PLlasXeu85E9cQ32gLCvAvr9vNaUccPVNP&index=13

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures

Uses of Closure: Memoization, Currying, Once Function, Data hiding and encapsulation, debounce/throttle

Cons: it can take lots of memory, because those variables are not garbage collected.

Garbage Collector

It frees variables from memory which are no longer in use, or have never been used.

JavaScript is a garbage-collected language, meaning that memory management is handled automatically.

(GC) is a mechanism that automatically manages memory by identifying and reclaiming memory that is no longer needed or reachable by the program. It helps prevent memory leaks and ensures efficient memory usage in programs.

Mark and Sweep Algorithm

"Mark and Sweep" is a garbage collection algorithm used by programming languages like JavaScript to manage memory and automatically reclaim memory that is no longer in use.

The "Mark and Sweep" algorithm ensures that memory occupied by objects that are no longer reachable or referenced by the program is properly released, preventing memory leaks and improving the overall performance of the application.

Here's how the Mark and Sweep algorithm works:

Lexical Environment

The Local memory (Created during Execution Context of that function) + Lexical environment of its parent.

e.g when the execution reach a use of a variable/function, it will search for its existence in the local memory of that function, if it's not found it will go to the lexical environment of its parent (there is a reference of lexical environment of its parent in local memory of a function), and so on. This chain is known as the Scope Chain.

Scope

Scope is till where we can access a function/variable.

Block

Multiple statements formed in a group enclosed in curly brackets forms a block

Block is needed because, sometimes Javascript expects a single statement to run, but we need to run commands with multiple statements which is only possible by block. e.g if else, for, etc

Error Types

There are three types of error:

  1. referenceError - given where variable/function does not have memory allocation
  2. typeError - given when we change type that is not supposed to be changed
  3. syntaxError - when proper syntax(way of writing a statement) is not used

Anonymous Functions

It is a function that does not have any name associated with it. An anonymous function is not accessible after its initial creation, it can only be accessed by the variable it is stored in as a function, as a value.

It is generally used at places where function is used as values e.g. arrow function, and callback function

https://www.geeksforgeeks.org/javascript-anonymous-functions/

High Order Function

A high order function is a function that accepts one or more functions as arguments or returns a function.

Function Statement / Function Declaration

regular function

<function>.name gives name of the function

<function>.length gives count of parameters of the function

Function Expression

A function stored in a variable as its value

Function expression can’t be called before its declaration because the value of the variable will be undefined till then.

First Class Function / First Class Citizen

The ability of function to be used as values is k/n as First Class Function.

IIFE

immediately Invoked Function Expression - self executing function

(function() {

        // logic

})()

Generator Function

Generator functions are used to generate values on-demand (on the fly).

Generator functions are a special type of function in JS that can be paused and resumed. They allow us to generate a sequence of values lazily.

Generators are used in iterable functions of in-built data structures of JS.

Pure Function

Deterministic: A Pure Function is a function that always returns the same result for a given set of arguments. It does not depend on any state or data change during a program’s execution, rather it only depends on its input arguments.

No Side Effects: A pure function doesn't cause any observable effects outside of its scope. It means it doesn't modify variables outside of its own scope, it doesn't interact with the DOM, it doesn't make API calls, and it doesn't change any external state.

E.g.

function add(a, b) {

return a + b;

}

here add is a pure function


let total = 0;

function addToTotal(value) {

total += value; return total;

}

here addToTotal function modifies the total variable outside of its scope. Therefore, it is not a pure function.

Constructor Function

In JavaScript, a constructor function is used to create objects.

// constructor function

function Person () {

this.name = 'John',

this.age = 23

}

// create an object

const person = new Person();

Difference b/w regular and arrow function

ref: https://medium.com/swlh/javascript-arrow-functions-vs-regular-functions-5ec4a9076796

  1. Syntax
  2. this binding
    In regular functions,
    this keyword represents the object that calls the function, which could be the window, the document, a button or whatever.
    In arrow functions the
    this keyword represents, this of the object where it is defined.
  3. Argument binding
    Unlike regular functions, arrow functions do not have an arguments binding. However, they have access to the arguments object of the closest non-arrow parent function.
  4. Using new keyword
    Regular functions are constructible (constructor function) and callable.
    Arrow functions are only ‘callable’ and not constructible
    hence new keyword can’t be used with arrow function

JSON and Object literals

Object Literals: It is a JS Object. it is a comma-separated list of key-value pairs wrapped in curly braces. It can be taken as a 'container' for data.

JSON: JavaScript Object Notation is a textual/string format of a JS Object.

JSON cannot be an object. JSON is a string format. The data is only JSON when it is in a string format. When it is converted to a JavaScript variable, it becomes a JavaScript object.

const myJSON = '{"name":"John", "age":30, "car":null}';

const myObj = JSON.parse(myJSON);

.call() & .apply()

The call() method calls the function with a given this value (object) and arguments provided individually.

When we don't specify this, it'll refer to the globalThis i.e. window in the browser's context.

in apply() we pass arguments in array

.bind()

the bind() method creates copy of the function with a given this value(object) and arguments provided individually

bind is same as call() but instead of calling the function, it's just create clone of the function, which we can invoke/call later

https://www.youtube.com/watch?v=75W8UPQ5l7k

Polyfill (Shims)

Polyfills are used to add missing features to a browser that it does not natively support. It’s like browser fallback.

Shims are often used to provide a consistent API or bridge differences between environments, which can include polyfilling missing features. e.g. jQuery

In practice, the terms "polyfill" and "shim" are sometimes used interchangeably, and their specific meanings can vary depending on the context in which they are used.

ProtoType

Whenever we create a function/object/array, the JS engine attaches a prototype property in it.

Prototype property is basically an object (also known as Prototype object), which contains some functions like toString, valueOf, map, reduce, etc.

We can also attach methods and properties in a prototype object, which enables all the other functions/object/array to inherit these methods and properties.

We can access the prototype of an object using: Object.getPrototypeOf(<object name>) or <object name>.__proto__

Prototypes are the mechanism by which JavaScript objects inherit features from one another. Basically, when you try to access a property of an object: if the property can't be found in the object itself, the prototype is searched for the property. If the property still can't be found, then the prototype's prototype is searched, and so on until either the property is found.

Compose & Pipe function

The compose() function takes a list of functions and returns a new function. This new function, when called with an argument, will apply the functions from right to left.

The pipe() function is similar to compose(), but it applies the functions from left to right.

Reduce Function

It is used to get a single value out of array, value can be anything, string, number, object, array, etc

If you don't supply an initial value then the FIRST element of the array is used as initial value (and that element is skipped in the loop)

Proxy

In JavaScript, a Proxy is a built-in object that allows us to customise the fundamental operations (such as property access, assignment, function invocation, etc.) of an object.

It provides a way to define custom behaviour for these operations, effectively allowing us to create a "wrapper" around an object and control how it interacts with the outside world.

const proxy = new Proxy(target, handler);

Object.defineProperty() vs Proxy

Object.defineProperty

Proxy

Observer

Observer APIs are used to detect changes in the applications.

  1. MutationObserver: Mutation Observer observes the DOM tree.
  2. IntersectionObserver: Intersection observer observes a DOM element’s visibility and positions.
  3. ResizeObserver: ResizeObserver observes the changes in the dimensions of a DOM element.
  4. PerformanceObserver: PerformanceObserver is used to observe performance measurement events and be notified of new performance entries as they are recorded in the browser's performance timeline.

Module

JavaScript modules allow us to break up our code into separate files. This makes it easier to maintain the code-base.

It relies on the import and export statements.

e.g.

person.js

const name = "Jesse”, age = 40;

export { name, age };

main.html

<script type="module">

        import { name, age } from "./person.js";

</script>

Falsy

A falsy (sometimes written falsey) value is a value that is considered false when encountered in a Boolean context.

E.g. null, undefined, 0, -0, “”, false, NaN, 0n, document.all()

Catch in try/catch

A Catch can catch errors for all the then/catch (promises) above it

e.g. 1.

getUser(1)

.then(user => getProfileImage(user.username))

.then(image => optimizeImage(image))

.then(result =>console.log("Done!"))

.catch(err => console.error(err));

Here the last catch can catch errors from all the 3 then’s above it

e.g. 2.

getUser(1)

  .then(user => getProfileImage(user.username))

  .catch(err => {

    console.warn("Image failed, using default avatar!");

    return "/images/default-avatar.png"; // 🟢 Heals the chain by returning a fallback value

  })

  .then(image => optimizeImage(image))   // 🟡 This will STILL run!

  .then(result => console.log("Done!"))

  .catch(err => console.error("Final catch:", err));

Here the 1st catch will only catch errors from the then’s above it (here only 1) and the main getUser function

And the 2nd catch will catch errors from the 2 then’s b/w the two catches

Finally

The finally statement in try catch finally defines a code block that always runs regardless of the result.

finally() does not receive any argument.

Throttle & Debounce

Debounce & Throttle is used to limit the rate of the given function call

JS Data Structure & Time Complexity

Data Structure

Insertion

Deletion

Access

Search

Iteration

Size

MAP: map = new Map()

map.set(key, val)

map.delete(key)

map.get(key)

map.has(key)

map.keys(), map.values(), map.entries()

map.size

Average: O(1)

Average: O(1)

Average: O(1)

Average: O(1)

Average: O(n)

Average: O(1)

Worst: O(log n)

Worst: O(log n)

Worst: O(log n)

Worst: O(log n)

Worst: O(n)

Worst: O(1)

OBJECT: obj = { }

obj[key] = val

delete obj[key]

obj[key]

obj.hasOwnProperty(key)

Object.keys(obj), Object.values(obj), Object.entries(obj)

Object.keys(obj).length, Object.values(obj).length, Object.entries(obj).length

Average: O(1)

Average: O(1)

Average: O(1)

Average: O(1)

Average: O(n)

Average: O(n)

Worst: O(n)

Worst: O(n)

Worst: O(n)

Worst: O(n)

Worst: O(n)

Worst: O(n)

SET: set = new Set()

set.add(val)

set.delete(val)

set.has(val)

set.has(val)

set.values()

set.size

Average: O(1)

Average: O(1)

Average: O(1)

Average: O(1)

Average: O(n)

Average: O(1)

Worst: O(n)

Worst: O(n)

Worst: O(n)

Worst: O(n)

Worst: O(n)

Worst: O(1)

ARRAY: arr = [ ]

arr.push(val), arr.unshift(val)

arr.pop(), arr.shift()

arr[idx]

arr.indexOf(val), arr.includes(val)

arr.forEach()

arr.length

At the end

push: O(1)

pop: O(1)

Average: O(1)

Average: O(n)

Average: O(n)

Average: O(1)

At the Beginning

unshift: O(n)

shift: O(n)

Worst: O(1)

Worst: O(n)

Worst: O(n)

Worst: O(1)

Spread vs Rest Operator

The main difference between rest and spread is that the rest operator puts the rest of some specific user-supplied values into a JavaScript array.

But the spread syntax expands iterables into individual elements.

Spread operator deep copies the primitive properties(1st depth) of the object but for the nested objects it does only shallow copying

Object Spread operator actually is internally the same as Object.assign(). Following 2 lines of code are totally the same.

let aClone = { ...a };

let aClone = Object.assign({}, a);

null vs undefined

null is a actual value assigned to a variable, which represents no value/null

and when a variable is declared but not initialised by any value, it is by default assigned undefined to it

console.log(null == undefined) // true because both of then does not represents a value

console.log(null === undefined) // false because typeof null is object, but type of undefined is undefined

undefined vs undeclared

An undeclared variable is one that hasn't been declared at all, while an undefined variable is one that has been declared but hasn't been assigned a value (not initialised).

e.g.

let y;

console.log(y); // undefined, because y is declared but has not been assigned any value (not initialised)

console.log(x); // ReferenceError x is not defined

z = 10; // in strict mode // ReferenceError z is not defined

var vs let vs const

var is function scoped, let & const are block scoped.

when we use const to declare a variable, it means that the variable cannot be reassigned to a different value. However, it does not make the value itself immutable.

e.g.

const myObject = { key: 'value' };

myObject.key = 'new value'; // This is allowed

myObject = { anotherKey: 'another value' }; // This will throw an error because we are trying to reassign myObject

In this case, the myObject variable itself cannot be reassigned, but we can still modify the properties of the object it points to.

In the case of strings, numbers, and booleans, we cannot reassign the variable because they are primitive values, and const prevents the variable from being reassigned.

Objects, on the other hand, are reference types in JavaScript. When we create an object, we are actually creating a reference to a location in memory. Using const with an object prevents reassignment of the variable to a different memory location, but it does not prevent us from modifying the properties of the object at that memory location.

Object.freeze() vs Object.seal()

Object.freeze() does not let to add/modify/remove any property/key to object

but Object.seal() can modify any existing property but doesn't let us add/delete property.

If we attempt to modify a property on a frozen object in strict mode, JavaScript will fail silently or throw an error (in non-strict mode, it will fail silently).

Argument vs Parameter

The parameters are the aliases for the values that will be passed to the function. The arguments are the actual values.

function foo( a, b, c ) { }; // a, b, and c are the parameters

foo( 1, 2, 3 ); // 1, 2, and 3 are the arguments

Math.max/min

console.log(Math.min()) // Infinity

console.log(Math.max()) // -Infinity

NaN = Not a Number

console.log(NaN == NaN) // false

console.log(NaN === NaN) // false

console.log(Math.max(NaN, 1)) // NaN

console.log(Math.min(NaN, 1)) // NaN

console.log(Math.min(NaN, Infinity)) // NaN

console.log(Math.max("any string", 1)) // NaN

The Math.max() and Math.min() functions return NaN if any parameter isn't a number and can't be converted into one (of course NaN cannot be converted into a number).

Infinity

1/0 = Infinity

-1/0 = -Infinity

0 / Infinity = 0

Infinity x 0 = NaN

Infinity x 1 = Infinity

Infinity + Infinity = Infinity

Infinity - Infinity = NaN

Infinity x Infinity = Infinity

Infinity / Infinity = NaN

Operator Precedence

Number > String > Boolean

When using > to compare two operands, if either operand is a number, Javascript will first convert the string/boolean to its equivalent number and then numerically compare.

i.e. ’10’ > 9

Only when both operands are string, they are compared lexicographically. i.e. character by character until they are not equal or there aren't any characters left. The first character of '10' is less than the first character of '9' hence '10' is < '9' i.e. ’10’ < ’9’

Also, these operators work from left to right

== Operator

  1. Comparing Primitives (String, Number, Boolean):
  1. If both of the operands are primitives and of the same type, == performs a simple value check.
  2. e.g.
  1. 5 == 5; // true
  2. 'hello' == 'hello'; // true
  3. true == false; // false
  1. Comparing Objects:
  1. if both of the operands are objects, then JS compares them by their reference not value.
  2. i.e. if both objects are pointing to same memory location, if yes then it will return true otherwise false
  3. e.g.
  1. const obj1 = { key: 'value' };
    const obj2 = { key: 'value' };
    console.log(obj1 == obj2); // false, because they are different objects in memory
  2. const arr = [1];
    console.log(arr == arr); // true, because they are same objects in memory
  3. console.log({} == {}); // false, because they are different objects in memory
  1. Comparing Different Types: Number > String > Boolean
  1. Number and String:
  1. If one operand is a number and the other is a string, JavaScript tries to convert the string to a number and then compares them.
  2. e.g.
  1. Boolean and Non-Boolean:
  1. Number and Boolean
  1. String and Boolean
  1. Null and Undefined:
  1. null and undefined are equal when using ==
  2. e.g.
  1. Object and Primitive:
  1. If one operand is an object and the other is a primitive value, the object is converted to a primitive value using the object's valueOf() and toString() methods.
  2. e.g.
  1. Different Objects:
  1. When comparing two different object types, the references are compared, not the contents of the objects.
  2. e.g.

e.g.

console.log([1] == 1) // 1 == 1 = true

console.log([1] == '1') // 1 == 1 = true

console.log(['1'] == '1') // 1 == 1 = true

console.log(['1'] == 1) // 1 == 1 = true

console.log([1] == ['1']) // false as both are arrays (not the same reference)

console.log(new Boolean(true) == 1) // 1 == 1 = true

console.log(new Boolean(true) == new Boolean(true)) // false as both are actually objects

console.log(Boolean(true) == '1') // true == '1' = 1 == 1 = true

console.log(Boolean(false) == [0]) // true == [0] = 0 == 0 = true

console.log(new Boolean(true) == '1') // object true == '1' = 1 == 1 = true

console.log(new Boolean(false) == [0]) // object fasle == [0] = // false as both are actually objects

console.log(null == undefined) // true

+ Operator

+<anyString which is not a number> = NaN,  e.g. ‘biro’

+<anyString which is a number> = that number,  e.g. ‘1’

+<anyNumber> = that number

+{ } = NaN

+[ ] = 0

// In mathematical operations, + works on both numbers and strings (used in string concatenation).

// Hence, if any of the operands is not a number(string), using + converts all operand/s to string and concatenates.

String({ }) = “[object Object]”

String([ ]) = “”

String(123) = “123”

to check if a key present in object

  1. if ('key' in myObj), BUT the in operator matches all object keys, including those in the object's prototype chain.
  2. so best way is to use
    myObj.hasOwnProperty('key')

Sparse Array

In JavaScript, a sparse array is an array in which not all elements have been assigned a value. This means that there are "holes" or empty slots in the array, where no value is stored. These holes are represented by undefined.

Array.flat(depth)

It is used to flatten a array till given depth

Array.prototype.flat() method can be used with sparse arrays. When we apply flat() to a sparse array, it will remove empty slots (undefined elements) and return a new array with only the defined elements.

Array.Sort()

Array.sort expects a compare function that defines the sort order.

If omitted, the array elements are converted to strings, then sorted lexicographically

i.e [999, 1111, 111, 2, 0] will become [0, 111, 1111 ,2, 999]

Function Currying

Currying is a technique in functional programming, in which a function with multiple arguments is converted into several functions in sequence, each taking one or more arguments.

It can be done in 2 ways:

  1. Closure methods
  2. Bind

Memoization

Memoization is a programming technique which is used to increase a function’s performance by caching its previously computed results.

Because JavaScript objects behave like associative arrays, they are ideal candidates to act as caches. Each time a memoized function is called, its parameters are used to index the cache. If the data is present, then it can be returned, without executing the entire function. However, if the data is not cached, then the function is executed, and the result is added to the cache.

Passing by value, Passing by reference & their relation with react memoization

  1. Passing by value: All primitives (number, string, boolean, etc) in JS are passed by value.
    Means when we pass a primitive data type variable as an argument to a function. Any change in that argument inside that function does not affect the original variable.
    e.g.
    const count = 1;
    function increaseCount(count) {

count++;

return count;

}

const increaseCountResp = increaseCount(count);

console.log("count", count); // 1

console.log("increaseCountResp", increaseCountResp); // 2

  1. Passing by reference: objects (object, array, function, etc) in JS are passed by reference. Means when we pass an object data type variable as an argument to a function. Any change in that argument inside that function also affects the original variable.
    e.g.

const obj = { a: "a" };

function addGreet(obj) {

obj.greet = "Hello World";

return obj;

}

const greetResp = addGreet(obj);

console.log("obj", obj); // { a: 'a', greet: 'Hello World' }

console.log("greetResp", greetResp); // { a: 'a', greet: 'Hello World' }

  1. In react, React.memo is used to memoize a component. It prevents re-rendering of the component if its props have not been updated. And in react, props are passed by value. So if the props which are passed to a component does not change, then the memoized component can be used directly which can prevent its re-render.

AbortController

The AbortController is a built-in JavaScript API that allows us to cancel ongoing asynchronous operations like fetch requests, timers, event listeners, etc. It prevents memory leaks and race conditions by stopping unnecessary network traffic if an action is no longer needed.

const controller = new AbortController();

// Pass the signal to the fetch request

fetch('/api/data', { signal: controller.signal })

  .catch(err => {

    if (err.name === 'AbortError') console.log('Request cancelled');

  });

controller.abort(); // Cancel the request

ES Modules (ESM) vs CommonJS (CJS)

ES Modules (ESM) and CommonJS (CJS) are two different systems for organising and importing/exporting code in JavaScript.

  1. Syntax: ES Modules use import and export statements to define dependencies and expose functionality between different modules, while CommonJS uses require() to import modules and module.exports or exports to export functionality.
  2. Async Import: ES Modules support asynchronous loading of modules using the import() function, allowing you to conditionally load modules while CJS only supports synchronous Import.
  3. Named and Default Exports: ESM supports both named exports (exporting specific entities from a module) and default exports (exporting a single entity as the module's default), while CJS supports only named exports.

ECMAScript

ECMAScript is the standardised specification of the rules, syntax, and features of writing code in a scripting language.

JavaScript is a practical implementation of that specification, used for creating dynamic and interactive web applications and supported by various environments and platforms.

Features of ES6

Headless UI

Headless UI refers to a design pattern for frontend components where the component provides all the functionality, state management, and accessibility logic, but zero default styling and we can write our own custom css.

Traditional component libraries (like Material UI or Bootstrap) enforce a specific look and feel out of design of the current system. A headless component library provides only the raw structural engine, leaving the visual presentation completely up to us.

[ Traditional Library ] ─── Forces ───> Logic + Accessibility + Visual Themes/Styles

[ Headless UI Library ] ─── Forces ───> Logic + Accessibility + (Your Custom CSS)

E.g. Radix UI, Headless UI (from tailwindCSS), React Aria (from Adobe)

Interpolation

In JavaScript, interpolation is the process of inserting strings or values into an existing string.

e.g.

t("MNgo Quiz is {{evaluation}}", { evaluation: 'fantastic' })

Idempotent

An operation is known as idempotent if applying it multiple times has the same effect as applying it once.

e.g.

PUT HTTP request is idempotent because it will update the same value multiple time for the same request

POST HTTP request is not idempotent as it will keep creating a new entry for the same request if called multiple times

Topological Order/Sort

A topological sort is a graph traversal algorithm in which a dependent node v is visited only after all its dependencies are visited i.e. In topological order a node u coming before v (u, v) means v is dependent on u.

A topological ordering is possible if and only if the graph has no directed cycles, that is, if it is a directed acyclic graph (DAG).

It is used in spreadsheets for dependency resolution of cells related to each other by some formula. In a spreadsheet, dependencies are parent nodes (cells referenced in formulas), and dependents are child nodes (cells containing formulas), so order will be parent then children.

e.g.

A1(0, 0): 10

B1(0, 1): =A1*2

Topological order: A1, B1

i.e. A1 must be processed before B1 because we can calculate value of B1 only when we know A1’s value

type sheetGraph { [dependencyCell: number]: dependentCells[ ] }

e.g.

{

“0_0”: [“0_1”]

}

i.e. when 0_0 value changes then 0_1’s value must also change

How npm works

npm (Node Package Manager) is a package manager for JavaScript and Node.js which is used to manage and distribute packages (libraries, frameworks, etc.). It allows us to easily install, update, and manage dependencies in our project.

  1. if we do npm install somewhere deep inside a project, it goes up and up till it finds package.json file or node_modules folder (root of the project), for installing dependencies of the project
  2. When we install a package, npm adds an entry for that package along with its versions in the package.json file under the "dependencies" section.
  3. Dependency Resolution: npm creates a package-lock.json file that locks package’s dependency versions and dependency tree (for sub packages), to ensure consistent and reproducible builds and they are compatible with each other.
  4. npm supports scoped packages, which group related packages together under a specific scope (e.g. @organisation/package).

NPM streamlines the process of managing dependencies and packages in JavaScript and Node.js projects

  1. installation
  2. version management
  3. dependency resolution
  4. script execution

Semantic Versioning (SemVer)

Semantic Versioning (SemVer) is a universal, standardized numbering system used in software engineering to communicate the exact nature of changes in a new code release

<major version>.<minor version>.<patch version>

value

desc

version

Must match version exactly

~version

Approximately equivalent, i.e. only accept new patch versions

^version

Compatible with version, i.e. accept new minor and patch versions

>version, >=version, <version, <=version

Version should match the condition

1.2.x

1.2.0, 1.2.1, etc., but not 1.3.0

*

Matches any version

latest

Obtains latest release

JS Tooling

JavaScript Tooling refers to the ecosystem of software applications, libraries, and utilities that developers use to write, build, test, optimize, and deploy JavaScript applications.

Modern JavaScript does not run raw in production anymore; it passes through a sophisticated pipeline designed to maximize developer productivity while ensuring the final code is as fast and compatible as possible.

What is Bundler

A bundler is a tool used in web development to combine multiple separate files, often written in different languages or technologies, into a single file or a smaller number of files.

Here are the key aspects and benefits of using a bundler:

  1. File Dependency Resolution: Bundlers analyse the codebase to determine the dependencies between different files, including JavaScript modules, CSS files, images, and more.
  2. Code Transformation: Bundlers can apply transformations to the code, such as transpiling newer JavaScript syntax into a version compatible with older browsers.
  3. Optimising and Minifying: Bundlers can optimise code by removing whitespace, renaming variables, and performing other operations to reduce file size. This is often referred to as minification.
  4. Module System Compatibility: Bundlers allow you to use modern JavaScript module systems (like ES6 Modules) and bundle them for browsers that may not natively support these features.
  5. Code Splitting: Bundlers support code splitting, which allows parts of the application to be loaded on demand, improving initial load times.
  6. Asset Management: Bundlers can handle assets like images, fonts, and other files, ensuring they are included and processed correctly in the final output.

e.g. Webpack, Rollup, Parcel, Browserify

Webpack

Webpack is a popular open-source module bundler for modern web applications. It's widely used in the JavaScript ecosystem to manage and bundle various assets like JavaScript files, CSS files, images, fonts, and more. Webpack helps optimise the loading and execution of web applications by intelligently combining and serving these assets.

  1. Webpack offers various optimization techniques, including minification, code splitting, and caching, to improve the performance of web applications.
  2. Webpack supports tree shaking, a technique that eliminates dead code (unused exports) from the final bundle. This helps reduce file size.
  3. Webpack is highly configurable through a webpack.config.js file. This file specifies how Webpack should process different types of files and how the final bundle should be generated.
  4. Webpack uses loaders to preprocess files. Loaders transform files from one format to another.
  5. Webpack provides a development server that enables Hot Module Replacement (HMR). This means that when you make changes to your code, the browser is updated in real-time without requiring a full page refresh.

Parcel

Parcel is a zero-configuration module bundler. It's designed to be extremely easy to set up and use, making it a good choice for quick prototyping or smaller projects.

Key Features of Parcel:

  1. Zero Configuration
  2. Built-in Support for Common Technologies: React, vue, Svelte, without extra configuration
  3. Code Splitting
  4. Hot Module Replacement (HMR)

Babel

Babel transpiles modern JavaScript code (ES6+ syntax) into a version of JavaScript that is compatible with older browsers and environments. This allows developers to write code using the latest language features while ensuring it can run on a wider range of platforms.

Babel is commonly used in React projects to transpile JSX syntax into regular JavaScript.

Babel can be configured using a configuration file (usually named .babelrc) or through configuration options in package.json.

Here's how Babel works:

  1. Parsing: Babel starts by parsing the input JavaScript code using a parser like Babel Parser. This step breaks down the code into an Abstract Syntax Tree (AST), which is a hierarchical representation of the code's structure.
  2. Transformation: Once the code is parsed, Babel can apply various plugins to the AST. These plugins can do things like transforming newer syntax into equivalent older syntax or adding polyfills for missing features.
  3. Generation: After the AST has been transformed, Babel then generates code from the modified AST. This code is typically in an older version of JavaScript that is widely supported across different environments.
  4. Output: The transformed code is then outputted, and it can be saved to a file or used directly in a browser or server environment.

Vite

Vite (pronounced veet from the French word for "quick") is a lightning-fast build tool and development server for modern web applications.

[ Webpack ] ── Bundles EVERYTHING first ──> Crawls entire dependency tree ──> Dev Server Ready (Slow)

[ Vite ] ── Starts Server INSTANTLY ──> Transforms only the requested files on-demand via Native ESM ──> Dev Server Ready (Fast)

Feature

Webpack

Vite

Dev Engine

JavaScript (Babel/Terser)

esbuild (Written in Go; 100x faster)

Dev Architecture

Bundle-based server

Native ESM (On-demand translation)

Production Build

Custom Webpack compilation

Rollup (Highly optimized tree-shaking)

Configuration

Complex and verbose

Minimal, zero-config default

Source Maps

Source maps are a way to map a bundled (combined/minified) code back to an unbuilt state. When we build for production, along with minifying and combining our JavaScript files, we generate a source map too which holds information about the original files.

.env variable

An "environment variable," often referred to simply as an "env variable," is a variable outside of a program or application that stores configuration settings, system information, or any data that needs to be available to multiple processes or components within an operating system or application environment. These variables are used to customise the behaviour of software and to manage various aspects of the environment in which programs run.

Prettier

Prettier is an open-source code formatter that is used to format the code to follow a consistent style and layout. It supports various programming languages, including JS, TS, HTML, CSS, JSON, and more. Prettier helps teams maintain a standardised code style without manual formatting efforts or debates over code formatting conventions.

Linter

A linter is a code analysis tool used to check source code for potential errors, bugs, style violations, and adherence to coding standards or style guidelines. It helps identify issues early in the development process, before the code is executed or deployed, reducing the likelihood of bugs and making the codebase more maintainable.

e.g. ESLint (for JavaScript and TypeScript), Pylint (for Python), TSLint (for TypeScript), Stylelint (for CSS and Sass)

Husky

Husky is a popular JavaScript tool used to manage Git hooks. It allows developers to automatically run scripts - like linting, formatting, or testing at specific points in the Git workflow, such as before a commit or a push.

Unit Testing

Unit testing is a software development practice in which individual units or components (like functions, methods, or modules) of a program are tested in isolation from the rest of the application. The goal is to ensure that each unit of code works as expected.

Here are the key aspects of unit testing:

  1. Isolation: Unit tests focus on testing a specific piece of functionality in isolation. This means that external dependencies or interactions are typically replaced with mock objects or stubs.
  2. Fast Execution: Unit tests should be quick to execute. This allows developers to get fast feedback on the correctness of their code.
  3. Deterministic: A unit test should always produce the same result when run under the same conditions. This means that tests should not rely on external factors like the network or system time.
  4. Repeatable: Unit tests should be repeatable in different environments. They should produce the same results whether run on a developer's machine or in a CI/CD pipeline.
  5. Independent: Unit tests should be independent of each other. The success or failure of one test should not impact the results of another test.
  6. Good Test Coverage: The goal of unit testing is to achieve a high level of test coverage, ensuring that most if not all code paths are tested.
  7. Refactoring Safety Net: Unit tests act as a safety net when refactoring code. They help ensure that existing functionality remains intact after making changes.

End to End Testing

The primary goal of E2E testing is to ensure that all components or units work together correctly in a real-world scenario. It helps catch integration issues and ensures that the application functions as expected from the user's perspective. e.g. Selenium, Cypress, Puppeteer, and TestCafe

  1. Scope: E2E testing aims to test the entire flow of an application from the user's perspective. It simulates real user interactions with the system.
  2. Granularity: E2E tests are coarse-grained and cover a broader range of functionalities. They often involve multiple units and can span across different components or modules.
  3. Dependencies: E2E tests do not typically use mocks or stubs. They interact with the actual application and its dependencies as a real user would.
  4. Speed: E2E tests are slower compared to unit tests because they involve interactions with the entire application, including user interfaces, databases, and external systems.

Test Driven Development (TDD)

Test Driven Development (TDD) is a software development practice that focuses on creating unit test cases before writing the actual code.

Git

Revert a commit: git revert <commit id> or git reset –soft <commit id>

Git Rebase: naam pe dhyaan do -> re-base (change base of the feature branch to the latest commit of the main branch)

Ref: https://www.youtube.com/watch?v=0chZFIZLR_0

git merge vs git rebase:

  1. git merge: git merge combines the changes from one branch into another branch, creating a new "merge commit" that has two parent commits. git merge <source-branch>
  2. git rebase: git rebase moves or combines a sequence of commits to a new base commit. It essentially rewrites the commit history. git rebase <target-branch>

Debugging

Steps involved in Debugging

  1. Reproducing the Error: Before you can debug an issue, you need to be able to reproduce it consistently. This involves understanding the conditions or inputs that lead to the problem.
  2. Identify the Actual Error: Identifying an error in a wrong may result in the wastage of time. It is very obvious that the production errors reported by users are hard to interpret, and sometimes the information we receive is misleading. Thus, it is mandatory to identify the actual error.
  3. Find the Error Location: Once the error is correctly discovered, you will be required to thoroughly review the code repeatedly to locate the position of the error. In general, this step focuses on finding the error rather than perceiving it.
  1. Use Console Logs
  2. Set Breakpoints
  3. Step Through the Code
  4. Isolate the Problem: Narrow down the scope of the problem. Identify which part of the code is causing the issue. This may involve commenting out sections of code to see when the behaviour changes.
  1. Analyse the Error: The next step comprises error analysis, a bottom-up approach that starts from the location of the error followed by analysing the code. This step makes it easier to comprehend the errors. Mainly error analysis has two significant goals, i.e., evaluation of errors all over again to find existing bugs and postulating the uncertainty of incoming collateral damage in a fix.
  2. Test Potential Solutions: Implement potential solutions one at a time and test to see if they resolve the issue. Be cautious and make small, reversible changes.
  3. Document Your Findings: Keep track of what you've tried, what worked, and what didn't. This documentation can be valuable for future debugging or for team members.
  4. Retest and Verify: After making changes, ensure that the issue is resolved and that it didn't introduce any new problems.

SOLID Principles in OOP

The SOLID principles are a set of five design principles that are intended to guide software development in order to create maintainable, flexible, and scalable code.

  1. Single Responsibility Principle (SRP): Given class should have only 1 responsibility.
  2. Open-Closed Principle (OCP): Software entities (classes, modules, functions) should be open for extension but closed for modification, meaning new functionality can be added to the system without altering existing code.
  3. Liskov Substitution Principle (LSP): Objects of a subclass should be able to replace Objects of superclass without affecting the correctness of the program.
  4. Interface Segregation Principle (ISP): Breaking large interfaces into smaller, specific interfaces Instead of having monolithic interfaces.
  5. Dependency Inversion Principle (DIP): High-level modules should not depend on low-level modules, but both should depend on abstractions (interfaces or abstract classes).

How would you ensure that your team is adhering to established coding standards to keep consistency in the codebase?

To ensure that our team follows the established coding standards, we can follow these strategies:

Web, Browser & Optimisation

What does <!DOCTYPE html> mean?

<!DOCTYPE html> is a declaration at the beginning of an HTML document that defines the document type and version of HTML being used. By including <!DOCTYPE html> we signal the browser to use the latest standard mode, which helps ensure consistent rendering and behaviour across different browsers.

HTML Semantic

Instead of using divs everywhere, using correct html element to convey the meaning of the element like header, nav, main, section, article, aside, footer, figure, figcaption, details, summary, etc

HTML manifest attribute

The manifest attribute in HTML is used to specify the location of a web application's cache manifest file. The cache manifest file is a text file that lists the resources (like HTML files, CSS, JavaScript, images, etc) that should be cached by the browser when the user visits the site.

e.g.

<html manifest="example.appcache"></html>

HTML Quirks Mode

Quirks mode is a way for browsers to maintain backward compatibility with older web pages that were designed before the widespread adoption of standardised HTML and CSS specifications.

Omitting the <!DOCTYPE html> declaration or using an outdated/incorrect <!DOCTYPE> can trigger quirks mode.

Aria Attributes

ARIA (Accessible Rich Internet Applications) attributes are a set of special HTML attributes designed to make web content and applications more accessible to people with disabilities, particularly those who use screen readers.

ARIA attributes generally fall into three buckets: Roles, States, and Properties.

  1. ARIA Roles (role="...")
  1. Roles tell a screen reader what an element is, when standard HTML doesn't make it clear. It defines the element's purpose, like: role="search", role="tooltip", role="alert", role="tabpanel".
  2. E.g: <div role="button" tabindex="0">Click Me</div>
  1. ARIA States
  1. States define the current condition/state of an element. These are highly dynamic and usually change based on user interaction via JavaScript.
  2. aria-expanded: Tells the user if a dropdown or accordion is open (true) or closed (false).
  3. aria-checked: Indicates whether a custom checkbox or radio button is checked.
  4. aria-disabled: Tells the user that an element is visible but currently unusable.
  1. ARIA Properties
  1. Properties define the nature/relationships of an element. These are usually static and give extra context.
  2. aria-label: Provides a human-readable text label for elements that don't have text content (like an icon-only button).
  3. aria-labelledby: Links an element to another element that acts as its label (using an ID).
  4. aria-describedby: Links an element to a longer description (like a tooltip or error message).
  5. aria-live: Tells screen readers to announce updates to an element automatically (crucial for live chat boxes, alerts, or notification banners).
  6. aria-modal="true": tells screen readers that the underlying windows/content underneath the dialog are temporarily inert, meaning the user's focus should be completely trapped inside the modal overlay until it is closed.
  7. aria-valuemin & aria-valuemax: For a custom role="slider", aria-valuemin and aria-valuemax define the absolute minimum and maximum values allowed for the range, which gives critical context to assistive technologies. (Note: aria-valuenow is also essential to track the current value).

Iframe options

  1. src
  2. width, height
  3. loading - lazy,
  4. sandbox - allow-same-origin allow-scripts
  5. allow - geolocation; microphone; camera
  6. scrolling

Event Delegation

Event delegation is the process of not adding event listeners to all the child elements but only on parent and then handling events from there, using Event Bubbling.

Benefit of Event Delegation

  1. Improved Performance: This reduces the total number of event listeners, which can lead to better performance, especially on large pages or in applications with many interactive elements.
  2. Dynamic Elements: Event delegation works well with dynamically generated or added elements. Since the event listener is attached to a parent element that already exists in the DOM.
  3. Reduced Memory Consumption: Attaching event listeners to individual elements can lead to memory leaks in certain situations, especially if elements are frequently created and destroyed. Event delegation can help in this.
  4. Simplifies Event Management: Event delegation can lead to cleaner and more organised code, as you can handle events for similar elements in a single place.

Event Bubbling: (bubble burst in outwards direction)

When the element and its parent container both have an onClick event handler defined, then on clicking on the child element, the event handler function of the element and its parent both(in sequence from child to parent) gets called/invoked, this phenomenon is k/n as event bubbling.

i.e. in case of event bubbling, the event movement begins from the target to the outermost element in the file.

event.stopPropagation() is used in child’s event handlers to stop/prevent this phenomenon.

Event Capturing

Event capturing is the same as event bubbling, only here an event moves from the outermost element to the target.

Attaching Event Listener to Dynamically created DOM Element

e.g. suppose we created a select element dynamically and attached change event listener to it, then it will not work

const selectEle = document.createElement("select");

selectEle.dataset.selectFieldId = “mine”

// selectEle.addEventListener("change", handleProductChange); // does not works

element.addEventListener("event", handleEvent); does not work for dynamically created elements

we need to attach change event listener to the nearest static parent element of the select element

suppose #content is the static div in our html file, where we are rendering the DOM elements dynamically using javaScript, then we can attach eventListener to #content and use event delegation to handle change in our select element

const contentEle = document.getElementById("content");

contentEle.addEventListener('change', handleChange);

function handleChange(e) {

    const selectFieldId = e?.target?.dataset?.selectFieldId;

    if (selectFieldId === “mine”) {

             // do the work

    }

}

Feature Detection vs User-Agent Sniffing

Feature detection and user agent sniffing are two different approaches used in web development to achieve cross-browser compatibility. They serve as techniques to determine the capabilities of a user's browser.

Feature detection involves checking if a specific feature or capability is supported by the browser before attempting to use it.

if (typeof localStorage !== 'undefined') {

// localStorage is supported, use it

} else {

// Provide an alternative or fallback behaviour

}

User agent sniffing (or browser sniffing) involves examining the User-Agent header sent by the browser in the HTTP request to identify the type and version of the browser being used.

if (navigator.userAgent.indexOf('MSIE') !== -1) {

          // Internet Explorer specific code

} else {

          // Code for other browsers

}

Web APIs

Browser has superpowers that are lent to JS engine to execute some tasks, these are Web APIs such as:

  1. console (console.log())
  2. location (location.href = “”)
  3. DOM API (document.getElementById(“”))
  4. setTimeout
  5. fetch
  6. local storage (localStorage.getItem("”))

Local storage/Session storage/IndexedDB

All of them exist for the same origin and stores data on the client side.

sessionStorage

localStorage

indexedDB

Maximum limit is 5 MB

Maximum limit is 5 MB

Maximum limit depends on  browser

Data in the sessionStorage exists till the browser is open. If we close the browser then our data will also erase automatically from the sessionStorage.

localStorage has no expiration time, Data in the localStorage persist till the user manually delete it

same as localStorage

sessionStorage.setItem(“hi”, “hello”)

localStorage.setItem(“hi”, “hello”)

window.indexedDB.open("MyTestDatabase", 3);

Cookies (Maximum limit is 4 KB)

Cookies are small files which are stored on a user’s computer for a domain. They are used to hold a modest amount of data specific to a particular client and website and can be accessed either by the web server or by the user’s browser.

Only the same website that saves information to a cookie can access it.

Shadow DOM

Shadow DOM is used for building reusable and encapsulated web components, helping to prevent style and script conflicts between different parts of a web page. The Shadow DOM provides a way to encapsulate the structure, style, and behaviour of a component within a scoped and isolated container.

Benefits of using the Shadow DOM:

Web Components

Web Components allow us to create reusable, encapsulated, and framework-agnostic HTML tags. Unlike components built in React, Vue, or Angular, Web Components run natively in the browser without requiring a heavy third-party library bundle or a compilation build step.

PWA (Progressive Web App)

Progressive Web Apps (PWAs) are a type of web application that delivers a native app-like experience to users while being built with web technologies (HTML, CSS, and JavaScript). They offer a range of benefits that combine the best of web and native app experiences.

  1. App like experience
  2. Offline functionality
  3. Faster loading (because of cached resources)
  4. Push notification
  5. Background task
  6. Responsive

Service Worker

A Service Worker is a JavaScript script that runs in the background of a web browser, independent of the web page, and enables advanced features like task execution in background, caching/offline support, push notifications, and background synchronisation.

It is a key component of Progressive Web Apps (PWAs) and can significantly enhance the performance, reliability, and user experience of web applications, especially in offline or low-network conditions.

The service worker life cycle

Difference b/w Service Worker and Web Worker

Service Worker is used for tasks related to network request, caching, offline functionality, while Web Worker is used to parallelize tasks like heavy computation or data processing independently from the main thread.

Difference b/w http cache and service worker cache

HTTP cache is browser-level caching controlled by HTTP headers, while Service Worker cache is a programmable JavaScript cache offering more control, offline capabilities, and persistence for web applications.

WebGL

WebGL (Web Graphics Library) is a low-level JavaScript API that allows browsers to render high-performance, hardware-accelerated 2D and 3D graphics directly inside an HTML <canvas> element without requiring any third-party plugins.

Traditional web elements (HTML/CSS) are rendered by the computer's CPU. WebGL bypasses the CPU and executes code directly on the user's GPU (Graphics Processing Unit), which is specifically designed to calculate millions of pixel coordinates simultaneously.

Different types of cache

Browser Cache: browser-level cache done using http headers, stores static files locally, reducing the need to re-download them on subsequent visits.

Content Delivery Network (CDN) Cache: Distributes static content globally to reduce latency and improve load times.

Database Cache: Stores frequently accessed database query results in memory for faster retrieval.

Object Cache: Caches results of expensive function calls or calculations for reuse.

Memory Cache: Stores data in the server's memory for quick access and improved performance.

Process of Browser Caching

Communication Across Browser Tabs

ref: https://dev.to/weifengnusceg/browser-concepts-the-one-and-only-guide-you-need-3bni

 window.localStorage.setItem("loggedIn", "true");

 window.addEventListener('storage', (event) => {

         if (event.storageArea != localStorage) return;

         if (event.key === 'loggedIn') {

           // Do something with event.newValue

         }

 });

        const channel = new BroadcastChannel('app-data');

        channel.postMessage(data);

        channel.addEventListener ('message', (event) => {

                 console.log(event.data);

        });

DOM

The Document Object Model (DOM) is a hierarchical tree-like representation of the structure and content of a web page.

It allows us to interact with and manipulate the HTML and XML content of a web page dynamically using JS.

How a HTML Page Renders

  1. Start to parse the HTML to build DOM
  2. Fetch the external resources (css, images are fetched in parallel while HTML parsing is happening, for JS depends on async, defer)
  3. Parse the CSS and build the CSSOM
  4. Execute the JavaScript
  5. Merge DOM and CSSOM to construct the Render Tree
  6. Calculate the layout and paint
  1. Layout (Reflow): Computes the exact geometry, size, and position of every visible element on the viewport.
  2. Paint (Repaint): Fills in pixels on the screen (colors, backgrounds, text styles, shadows).

When the browser encounters an external resource (like an image or video) while parsing HTML, it typically starts fetching that resource in parallel with continuing to parse and render the HTML. This is k/n as asynchronous loading.

  1. JS: Block Parsing unless defer or async
  2. CSS: Block Rendering
  3. Other resources: No blocking

In a web page, when multiple synchronous scripts are encountered, they are typically executed in the order in which they appear in the HTML

ref: https://starkie.dev/blog/how-a-browser-renders-a-web-page

Reflow vs Repaint

Reflow (Heavy)

Repaint (Lighter)

The Golden Rule: A Reflow always forces a Repaint (because moving an element means it has to be redrawn in the new spot). A Repaint can happen completely on its own.

Bad Practice (Triggers Reflow/Repaint)

Good Practice (Optimized Approach)

Changing individual styles one-by-one via JS (el.style.width = '10px'; el.style.margin = '5px';)

Change the className or use el.style.cssText to trigger exactly one combined reflow.

Making live DOM updates inside a loops

Make changes offline using a DocumentFragment, then append it to the live DOM once.

Using top / left for layout-based animations

Use CSS transform: translate() and opacity. These bypass both reflow and repaint, offloading execution directly to the GPU via the Compositing layer.

Constantly querying layout metrics in loops (causing layout thrashing)

Cache the layout values in a local JavaScript variable before running the loop.

CSS animation performance: why transform is preferred

Using transform promotes the element to its own isolated GPU layer. To animate it, the GPU just shifts or fades that pre-rendered layer on top of the page without disturbing the rest of the layout, guaranteeing a smooth 60+ FPS animation.

  1. The Core Reason: CPU vs. GPU
  1. Layout Properties (top, left, width): Run entirely on the CPU. The browser must recalculate the geometry (Reflow) and redraw the pixels (Repaint) on every single frame, causing stuttering (jank).
  2. Transform Properties (translate, scale, rotate): Run entirely on the GPU. The browser skips (reflow) layout and (repaint)paint entirely, handling the animation as a simple image manipulation.
  1. The Rendering Lifecycle Shortcut
  1. Slowest: top / left → Reflow → Repaint → Composite
  2. Slow: color / box-shadow → Repaint → Composite
  3. Fastest: transform / opacity → Composite Only

Async vs Defer

Critical Rendering Path (CRP)

The Critical Rendering Path is the sequence of steps the browser goes through, to convert the HTML, CSS, and JavaScript into pixels on the screen. Optimising the critical render path improves render performance.

The document object model (DOM) is created as the HTML is parsed. The HTML may request JavaScript, which may, in turn, alter the DOM. The HTML includes or makes requests for styles, which in turn builds the CSS object model (CSSOM). The browser engine combines the two to create the Render Tree. Layout determines the size and location of everything on the page. Once layout is determined, pixels are painted to the screen.

Optimising for CRP

Improve page load speed by prioritising which resources get loaded, controlling the order in which they are loaded, and reducing the file sizes of those resources. Performance tips include:

  1. Minimising the number of critical resources by deferring non-critical ones' download, marking them as async, or eliminating them altogether
  2. Optimising the size of critical resources of each request
  3. Prioritising the critical resources: optimising the order in which critical resources are loaded by prioritising the downloading of critical assets, thereby shortening the critical path length.

Critical Section of a page

The critical section of a web page refers to the part of the page that is immediately visible to the user upon the initial page load, without requiring additional scrolling or interaction.

Way to optimise critical section

  1. Displaying perception: loader, error, etc
  2. Optimise CRP
  1. Identify & Prioritise critical resources
  2. Minimise number of critical resources
  3. Decrease/optimise size of resources
  1. Optimise/compress resources
  2. Decrease network calls
  3. Load non critical/important resources later
  4. SSR

Code Obfuscation

Code obfuscation is a technique used to make source code more difficult to understand or reverse engineer. It involves transforming the code in a way that it remains functional but becomes harder for humans to read and comprehend. This is often done to protect intellectual property, prevent unauthorised modifications, or enhance security.

Some common obfuscation techniques: variable & function renaming, string encryption, code jumbling, dummy code insertion (inserting meaningless or redundant code), code splitting, minification, etc.

Tree Shaking

ref: https://benestudio.co/building-a-tree-shaking-friendly-javascript-package/

Tree shaking is a dead-code (unused code) elimination process. It relies on the static structure of ES modules (import and export). So CommonJS modules (require) can’t be shaken off directly because of their dynamic nature. So the bundlers we are using, like Webpack and Rollup, automatically tree shake our code for us.

To achieve this, we need to mark your library as side-effect-free in our library’s “package.json”. Add:

"sideEffects": false

To build a tree shaking friendly package we need to

window.onload vs DOMContentLoad

  1. window.onload
  1. It get triggered when the complete web page is loaded
  2. It is used when we need to execute JS code that relies on the complete availability of the web page
  1. DOMContentLoad
  1. It gets triggered when the browser is done loading & parsing the html document into DOM.
  2. It is often used when we want to execute JavaScript code as soon as the DOM hierarchy is ready

Hydration

Hydration in JavaScript, is the process of attaching event handlers to the DOM elements.

Hydration or Rehydration is a technique in which client-side JavaScript converts a static HTML web page (delivered either through static hosting or server-side rendering), into a dynamic web page by attaching event handlers to the HTML elements, sets up interactivity and make the page fully functional.

This helps improve the initial loading performance and user experience of web applications.

Selective Hydration

Selective/Partial hydration, is a concept in web development that refers to the practice of dynamically loading and rendering specific parts of a web page based on user interaction or other conditions.

The goal of selective hydration is to optimise the initial page load by sending only the essential HTML, CSS, and JavaScript needed to render the initial view. Then, as the user interacts with the page, additional content and functionality are loaded and rendered dynamically.

Selective hydration can be achieved using techniques like:

  1. Conditional Rendering
  2. Client-side Routing
  3. Lazy loading component
  4. Using Suspense in react
  5. Data-fetching on demand

Progressive Rendering

Progressive rendering is a method designed to enhance user experience and perceived speed by showing content as quickly as possible, even before every asset has finished downloading. Rather than delaying display until the full page is ready, it incrementally prioritizes and renders content.

Associated strategies for progressive rendering include:

Web Vitals

Web Vitals are a set of metrics used to measure and improve speed, performance and interactivity of a web page.

  1. First Contentful Paint (FCP):
  1. First Contentful Paint (FCP) measures the time taken to render the first piece of content of a web page
  2. It indicates how quickly users perceive that the page is loading and becoming usable.
  3. To Reduce FCP:
  1. We need to decrease resources size (images, css, js, etc) or minimise render blocking resources (lazy loading for images, async loading for non-critical script)
  2. Prioritise Critical Resources: Use the <link rel="preload"> and <link rel="prefetch"> tags to instruct the browser to fetch critical resources early and similarly for critical JS.
  3. Minimise Network Requests & Server Response Time: fast CDN & caching can be used
  1. A good FCP score is typically under 1 second.
  1. Largest Contentful Paint (LCP):
  1. Largest Contentful Paint, measures the time taken to render the largest visible element (such as an image or text block) of a web page.
  2. LCP is an important metric for understanding when the main content of a page becomes visible to users.
  3. A good LCP score is typically under 2.5 seconds.
  1. Total Blocking Time (TBT):
  1. Total Blocking Time (TBT) measures the amount of time during which the main thread of a web page is blocked and unable to respond to user input.
  2. TBT is concerned with how quickly a page can start responding to user input (first interaction)
  3. To reduce TBT, we can optimise JS execution and minimise the impact of long tasks using web-worker
  4. A good TBT score is typically under 300 milliseconds.
  1. Time to Interactive (TTI):
  1. Time to Interactive (TTI) measures the time it takes for a web page to become fully interactive for users.
  2. TTI is concerned with when the page becomes fully interactive.
  3. To improve TTI, we need to optimise critical resources (load critical js, responsible for interactivity, first).
  4. A good TTI score is typically under 5 seconds.
  1. Cumulative Layout Shift (CLS):
  1. CLS, measures the visual stability of a page by tracking unexpected layout shifts in the loading process.
  2. CLS helps ensure that page content doesn't unexpectedly shift while users are interacting with it.
  3. To reduce CLS:
  1. Give width, height attribute to image/video elements
  2. Ensure new content/element does not shift any existing content/element
  3. Allocate space for content which will be loaded asynchronously.
  1. A good CLS score is typically under 0.1.
  1. Speed Index (SI):
  1. Time taken to load entire web-page
  2. It measures how quickly the content of a web page is visually displayed during the entire loading process.
  3. It takes into account the progression of content loading over time and provides a score based on the visual completeness of the page at different points during loading.
  4. A good Speed Index score is typically under 3.4 seconds.

Core Web Vitals

3 core web vitals are:

  1. Largest Contentful Paint (LCP) — Loading
  1. What it is: The time it takes for the largest visible element (e.g., a hero banner image or main heading text) to fully render on the screen.
  2. Target: ≤ 2.5 seconds
  3. Quick Fix: Compress images into modern formats (WebP/AVIF), set up an aggressive CDN to serve assets closer to users, and eliminate render-blocking scripts.
  1. Interaction to Next Paint (INP) — Interactivity
  1. What it is: The latency of all user interactions (clicks, key presses, taps) across the entire page lifecycle, measuring how long it takes for the browser to paint the next visual frame after an action.
  2. Target: ≤ 200 milliseconds
  3. Quick Fix: Break up heavy, monolithic JavaScript functions ("long tasks") using code splitting, and optimize framework rendering loops.
  1. Cumulative Layout Shift (CLS) — Visual Stability
  1. What it is: A score quantifying how much layout elements unexpectedly shift position while loading, which can cause users to accidentally click the wrong button.
  2. Target: ≤ 0.1 (structural score, not time)
  3. Quick Fix: Always declare explicit width and height dimensions on image tags and video containers, and reserve static placeholder heights for late-loading dynamic advertisements.

How CLS is calculated

Cumulative Layout Shift (CLS) is a structural score calculated by multiplying how much screen area unexpectedly moved (Impact Fraction) by how far it traveled (Distance Fraction).

CLS Score = Impact Fraction × Distance Fraction

Final Score for this shift = 0.55 × 0.15 = 0.0825

Waterfall

Waterfall refers to a graphical representation of the various resources (like HTML, CSS, JavaScript, images, etc.) that a web page loads, and the order in which they are loaded. It's a tool used for visualising the loading process of a web page.

The waterfall chart displays a timeline on the x-axis and the different resources on the y-axis. Each resource is represented as a horizontal bar, with the position on the y-axis indicating when it starts to load and the length of the bar representing how long it takes to load. The resources are loaded in the order they are requested by the browser.

When debugging a memory leak in a SPA, what are the most common JavaScript-level causes you look for

When you implement route-based code splitting, what metrics would you track to ensure you didn't just shift the cost into a slower navigation (e.g., after a user clicks)?

[ User Clicks Link ] ───> [ Download Chunk ] ───> [ Parse & Execute JS ] ───> [ Screen Paints New Route ]    

      └──────────────────────── Route Transition Latency ────────────────────────┘

       Monitored via INP & LoAF

  1. Interaction to Next Paint (INP): Measures the time between a user clicking a navigation link and the browser actually painting the next frame. If compiling the new JS chunk chokes the main thread, your INP will spike above the healthy 200ms threshold.
  2. Route Transition Latency (Custom Metric): Tracks the raw duration of a navigation event. You can log this manually using the User Timing API in your router's navigation hooks:
  1. router.onTransitionStart(() => performance.mark('nav-start'));
  2. router.onTransitionEnd(() => {
  3.   performance.mark('nav-end');
  4.   performance.measure('RouteTransition', 'nav-start', 'nav-end');
  5. });
  1. Long Animation Frames (LoAF): It identifies exactly which split script file (e.g., chunk.Dashboard.js) caused the main thread to stutter during a route change.
  2. Network Fetch Latency & Cache Hit Rate: Monitors whether navigation lag is caused by a slow network download or JS execution. If your CDN cache hit rate for chunk files is low, users are constantly waiting on raw data transfers mid-session.

Does font affect rendering and layout phases? If yes, how to minimise the effect?

  1. Rendering Phase: The time it takes to download a font can impact the rendering phase. If a font is large or takes a long time to download, it may delay the rendering of the associated text content.
  2. Layout Phase: The browser needs to measure the size of text elements for layout calculations. The size of the text is influenced by the font metrics, including the font size, line height, and character spacing.
  3. For minimising its impact
  1. Use optimised/compressed font file type like WOFF2
  2. Lazy loading fonts: For non-critical fonts, load them asynchronously after the critical section is loaded
  3. Use fallback fonts
  4. Preloading: Preload fonts using the <link rel="preload"> tag to initiate the font download earlier in the page loading process.

System Design

Things to consider in Frontend System Design interviews

Tutorial: https://www.youtube.com/watch?v=44mOnnt5pic&list=WL&index=1&t=2s

RADIO - Requirements, Architecture, Data Models, Implementation, Optimisation

  1. Requirements - gather all requirements
  1. Functional requirements
  1. Module Wise
  1. Authentication & User Management
  2. Product Listing
  3. Pricing & Subscription
  4. Payment Gateway
  5. Cart
  6. Account Management
  1. Feature Wise
  1. Search
  2. Filter
  3. Details
  4. Product Review
  5. Add/remove items to cart
  6. Video Streaming - Dash.js, Shaka Player
  1. Non-Functional Requirements
  1. Devices - Mobile/Desktop/Tablets
  2. Responsive/Adaptive
  3. Accessibility - support for disabled people, international people in local language
  4. Assets Optimisation
  5. Performance - Web Vitals, CSR/SSR
  6. Security
  7. Caching
  8. Offline Support
  9. Logging & monitoring
  10. Testing
  1. Scope - Prioritisation - what least to build, Minimum Viable Product (MVP)
  2. Tech Choices
  1. Library/Framework
  2. State Management
  3. Caching & Storage
  4. Offline Support / PWA
  5. Design System / theming / Design Tokens
  6. Components: Material UI, Prime React, Headless UI
  7. Folder Structure
  8. Packages
  9. Build Tools - webpack, rollup, turbopack
  1. Component Architecture
  1. Routes / Component Diagram
  2. Component Hierarchy / Component API
  3. Dependency Tree / Data Sharing
  1. Data Models, API, Component API/State/Props
  1. Data Models / State Management
  2. Backend API
  3. Component API
  1. state/props
  2. event handling
  3. customisation - theming
  4. Reusability
  5. Data source
  1. Optimisation & Performance
  2. Accessibility
  3. Availability
  4. Security

Common HLD Components

  1. Architectural Patterns
  1. Monolith Frontend
  2. Micro Frontend
  1. The idea behind Micro Frontends is to think about a website or web app as a composition of features which are owned by independent teams. Each team has a distinct area of business or mission it cares about and specialises in.
  2. iframe: window.postMessage(), window.addEventListener(‘message’, function(){ })
  3. shadowDOM - web components (html+css+js encapsulated which can be used anywhere, any js library)
  4. npm package (can be used in particularly that js library)
  5. Module Federation: It is an architectural pattern and a powerful feature introduced in Webpack 5 that allows a JavaScript application to dynamically load code from another separate application at runtime. Before Module Federation, sharing code between independent frontend teams required bundling shared code into npm packages, which meant forcing a full application rebuild and redeployment every time a shared component was updated. With Module Federation, applications can share code instantly without redeploying the host container.
  1. Communication Protocols
  1. HTTP Request - client makes the request and server sends the response
  2. HTTP Long Polling - client keep asking to server until all demands are met
  3. Web-Socket - server sends response whenever any update/new data is available
  4. Server Sent Events(SSE) - client need to make request only once, server keep sending data
  1. Availability
  1. Offline support - service worker (PWA - Progressive Web App)
  2. Responsive - Device support
  3. internationalisation
  1. Accessibility
  1. Add keyboard accessibility - use tabIndex html attribute, programmatically using JS
  2. Html5 semantics: using correct html element to convey the meaning of the element, header, nav, article, section, figure, figcaption, main, footer, etc
  3. aria html attributes
  4. alt html attribute for image
  5. Adding form field labels
  1. Consistency: should have same behaviour / look on all browsers
  1. js polyfills
  2. design system (material ui, atlassian design system, etc) / theming
  1. Credibility & Trust
  1. SEO (Search Engine Optimisation)
  1. On Page
  1. title, description, meta, content
  2. Semantic HTML
  1. Off Page
  1. backlinks
  2. ads
  1. Logging & Monitoring
  1. Error Logging: sentry, datadog, posthog
  2. User Monitoring/Tracking: RUM(Real User Monitoring): user tracking (types of users, how much time they spend), Posthog
  3. Performance Monitoring: Application Performance Monitoring (APM): datadog
  4. Application Monitoring: capacity/traffic monitoring (Google Analytics, Posthog)
  1. Storage & Database
  1. Caching - HTTP Caching, In Memory Caching, API Caching
  2. State Management - Redux, React Context, Jotai
  3. Local Storage, Session Storage, IndexedDB, Cookies
  1. Performance & Optimisation
  1. Network Performance
  1. Caching resources
  2. Compress resources (gzip, brotli)
  3. Debouncing/Throttling
  4. Widget/Component Result Cache (Memoization)
  1. Assets Performance
  1. Assets resources caching
  2. webP Images (smaller in size, can be compressed without losing quality, support animation like gifs)
  3. compress/resize media
  4. image smaller in size, use src-set:  https://html.com/attributes/img-srcset/#ixzz7cyKsZPJ5
  1. The src-set attribute is an HTML attribute used in the <img> element to provide multiple sources (and optionally their sizes) for an image. This helps browsers to choose the most appropriate version of an image based on the device's screen size and resolution. It's particularly useful for responsive web design to optimise for various devices and network conditions.
  2. <img
            src="image-800.jpg"
            srcset="
                    image-400.jpg 400w,
                    image-800.jpg 800w,
                    image-1200.jpg 1200w,
                    image-1600.jpg 1600w
            "
            sizes="
                    (max-width: 400px) 400px,
                    (max-width: 800px) 800px,
                    (max-width: 1200px) 1200px, 1600px
            "
            alt="Description of the image"
    />
  1. Lazy Loading assets
  1. JS Performance
  1. Write Optimal Code (best time complexity)
  2. Minify code
  3. remove unused/repeated code (tree-shaking)
  4. Loading JS Asynchronously (defer)
  5. Event Delegation
  6. Use Web Worker for high weight tasks
  7. Bundle Splitting
  8. Memoization
  1. Rendering Performance
  1. Prevent un-necessary re-renders: React.memo, useCallback, useMemo
  2. Delivery Option: Pagination, Infinite Scroll, Virtualisation
  3. Debounce/Throttle: Optimizing high-frequency events like search inputs (debouncing) or window resizing/scrolling (throttling) to reduce CPU load.
  4. Load Non-critical stuff later (analytics script, non-critical styles) - by using defer for non-critical scripts or loading scripts asynchronously using JS, by using media=”print” for non-critical css or loading css asynchronously using JS
    <link rel="stylesheet" href="non-critical.css" media="print">
    document.addEventListener('DOMContentLoaded', function() {
            var link = document.createElement('link');
            link.rel = 'stylesheet';
            link.href = 'non-critical.css';
            document.head.appendChild(link);
    });
  5. Perception - Loader, Skeleton, Placeholder
  6. SSR
  7. Prefer CSS animation
  1. Web Vitals - FCP, LCP, TBT, TTI, CLS, INP
  2. Keep a connection state in the app, so that you will not make network request knowing that it will fail
  1. Security
  1. .env variables
  2. Authentication & Authorisation
  3. Content Security Policy (CSP)
  4. CORS
  5. CSRF
  6. XSS (Cross-Site Scripting)
  1. Testing
  1. Unit Testing (Individual Testing), e.g Jest, React Testing Library, Chai
  2. Integration Testing
  3. End-to-end Testing (E2E Testing), e.g. Selenium, Cypress, Playwright

https://www.youtube.com/watch?v=9JDlZxR8gVw

How can we make a web app scalable

  1. Optimise Frontend Performance:
  1. Caching:
  1. Load Balancing:
  1. Horizontal Scaling:
  1. Content Delivery Network (CDN):
  1. Asynchronous Processing:
  1. Optimise Code and Assets:
  1. Responsive Design:
  1. Monitoring and Analytics:
  1. Security Best Practices:
  1. Micro Service Architecture:
  1. Content and Database Caching:

What are the security measures which should be considered during development?

  1. Using .env variable for secret keys and credentials
  2. Input validation
  3. CSP Policy
  4. CORS
  5. CSRF: csrf token, same-site
  6. Proper error handling
  7. Authentication - auth token
  8. Access Control
  9. Storing encrypted data in browser storage
  10. Encrypted password
  11. Encrypted url
  12. Code obfuscation - minification, uglification
  13. Logging & Monitoring

How can we ensure components support multi-themeing (dark mode, client-specific branding) and are extensible

  1. Design Token Layer: Never hardcode style values in the components. Instead, map identical semantic design tokens to completely different raw values using HTML data attributes.
  2. Compound Components: Instead of configuring components using a fragile array of boolean properties (hasIcon={true} title="..."), use Compound Components. Break a massive layout down into modular sub-components that share state implicitly via React Context.

What is WCAG Compliance

WCAG (Web Content Accessibility Guidelines) is the gold-standard global benchmark for digital accessibility. It outlines how to make websites, apps, and digital documents usable for everyone, including individuals with visual, auditory, motor, or cognitive disabilities. It is divided into three conformance levels: A (Minimum), AA (The standard legal requirement for most mid-to-large businesses), and AAA (The highest, most stringent standard).

All WCAG rules are built on four core principles, known by the acronym P.O.U.R.:

How to make element keyboard accessible

To make a User Interface (UI) keyboard accessible, you must ensure a user can navigate and operate every feature using only a keyboard (Tab, Shift + Tab, Enter, Spacebar, and Arrow Keys).

  1. The Core Rules of Keyboard Accessibility
  1. Maintain a Logical Tab Order
  1. Focus must move predictably: top-to-bottom, left-to-right.
  2. Use native semantic HTML (<button>, <a>, <input>) because they have built-in keyboard support.
  3. Never use positive indexes (tabindex="1"). It forces an artificial order that breaks layouts. Use tabindex="0" to make a custom element focusable naturally, or tabindex="-1" to make it focusable only via JavaScript.
  1. Never Hide the Focus Indicator
  1. Avoid global resets like outline: none; which leave keyboard users blind.
  2. Use the CSS modern pseudo-class :focus-visible to style a high-contrast focus ring that appears only when a user is navigating via keyboard.
  1. button:focus-visible {
  2.   outline: 3px solid #3b82f6;
  3.   outline-offset: 2px;
  4. }
  1. The Rule of Two (Key Events)
  1. Any custom element that triggers an action on a mouse click must also trigger that exact action using Enter and the Spacebar via a keydown listener.
  1. Advanced Component Layouts
  1. Modal Focus Traps
  1. When an overlay dialog box opens, you must implement a Focus Trap
  2. Pressing Tab must cycle focus only within the active elements inside the modal.
  3. The cursor must never escape "underneath" into the background page.
  4. Pressing Escape must instantly close the modal and return focus to the button that originally opened it.

ref: check react/srcKeyboardAccessibility of coreJs Repo

What are the A11y issues? How do you handle it?

A11y is short form of accessibility

Some common accessibility issues are:

  1. Lack of keyboard navigation/shortcuts
  2. Missing alt attribute for image
  3. Colour contrast issues
  4. Missing label of form elements
  5. Unstructured content
  6. Not following semantic html

To handle it we can do:

  1. Use semantic html
  2. Keyboard accessibility
  3. alt attribute for images
  4. Use accessibility testing tools like pagespeed.web.dev, lighthouse

Diff b/w Virtualisation and Windowing

Windowing calculates which items are inside the visible viewport and as we scroll, items that leave the viewport are completely destroyed (unmounted from the DOM), and items entering the viewport are created from scratch (mounted into the DOM).

But in virtualization, it creates a fixed number of DOM rows (just enough to fill the viewport plus a tiny buffer) and never destroys them. As we scroll, instead of deleting an off-screen row, a row that leaves the top of the screen is physically repositioned to the bottom, and its contents are instantly swapped out with the next dataset item.

Design Patterns

ref: https://www.youtube.com/watch?v=tv-_1er1mWI

Difference between Controller (of MVC) and ViewModel (of MVVM)

While both the Controller and ViewModel play roles in connecting the Model and View, the Controller often handles the overall application flow and user input, while the ViewModel is more focused on organising and presenting data in a way that is suitable for the user interface.

Agile Methodology

  1. Customer-Centric Approach: Agile places a strong emphasis on understanding and meeting the needs of the customer. Continuous customer feedback is actively sought and incorporated into the development process.
  2. Iterative Development: Instead of trying to deliver the entire product at once, Agile breaks the project into smaller increments called iterations. Each iteration typically lasts 2-4 weeks and results in a potentially shippable product increment.
  3. Cross-Functional Teams: Agile teams are typically small, cross-functional groups that include developers, testers, designers, and other necessary roles.
  4. Empowered Teams: Agile teams are self-organising and have the authority to make decisions about how to accomplish their work. They are empowered to choose the best approach to meet their goals.
  5. Adaptability: Agile teams are responsive to changing requirements and priorities. They can adapt quickly to new information or feedback from stakeholders.
  6. Continuous Delivery and Integration: Agile encourages continuous integration of code into a shared repository and frequent delivery of working software.
  7. Sprint Planning: At the beginning of each iteration (commonly referred to as a "sprint"), the team collaborates to define the scope of work for that sprint. They select user stories or tasks from the backlog to work on.
  8. Daily Stand-Ups: The team holds daily stand-up meetings to discuss progress, challenges, and plans. This helps ensure everyone is aware of the project's status and any potential roadblocks.
  9. Retrospectives: At the end of each sprint, the team conducts a retrospective meeting to reflect on what went well, what could be improved, and how to implement those improvements in the next sprint.
  10. Backlog Prioritisation: The product backlog is a prioritised list of features, user stories, or tasks that need to be addressed. The highest priority items are worked on first.
  11. Working Software as the Primary Measure of Progress: The ultimate goal of each sprint is to produce working, potentially shippable software. This ensures that progress is tangible and provides value to the customer.

Google Docs

googleDocs folder of machineCoding repo

https://www.youtube.com/watch?v=9JKBlkwg0yM&list=PLg-m8NS3FSbf37gYvFj8maUk7XQeZ4iFU

https://www.youtube.com/watch?v=GYVLp0ekHdM&list=PLg-m8NS3FSbf37gYvFj8maUk7XQeZ4iFU&index=2

https://www.youtube.com/watch?v=uOKrTc3Q0D0&list=PLg-m8NS3FSbf37gYvFj8maUk7XQeZ4iFU&index=4

Google Sheets

googleSheets folder of machineCoding repo

https://www.youtube.com/watch?v=fmIiDLbLc_s&list=PLg-m8NS3FSbf37gYvFj8maUk7XQeZ4iFU&index=3

Google Calendar

https://www.youtube.com/watch?v=leo1FZ6vu1I

Networking & Security

What happens when you type a URL in the web browser and hit enter?

  1. Parse URL
  2. Look for IP address of the domain in DNS
  3. Establish TCP connection with the server
  4. Make HTTP request
  5. Severs send the HTTP response
  6. Browser receives the response, parse the response and display the content
  1. Parsing the URL: Browser parses the URL to extract various components such as the protocol (HTTP, HTTPS), domain name (e.g., www.example.com), path, query parameters, and other fragments.
  2. The browser looks for the IP address of the domain name (locate the server hosting that website) in the DNS (Domain Name System)
  1. DNS is a list of URLs and their corresponding IP address (like a telephone book). We can access the website directly by typing the IP address but imagine remembering a group of numbers to visit any site.
  2. The DNS checks at the following places for the IP address.
  1. Check Browser Cache: The browser maintains a cache of the DNS records for some fixed amount of time.
  2. Check OS Cache: If the browser doesn't contain the cache then it requests to the Operating System as the OS also maintains a cache of the DNS records.
  3. Router Cache: If your computer doesn't have the cache, then it searches in the router cache of the DNS records.
  4. ISP (Internet Service Provider) Cache: If the IP address is not found till this then it is searched at the cache that ISP maintains of the DNS records. If not found here also, then ISP’s DNS recursive search is done. In "DNS recursive search", a DNS server initiates a DNS query that communicates with several other DNS servers to find the IP address.
  1. The Browser initiates a TCP connection with the server.
  1. When the browser receives the IP address, it will build a connection between the browser and the server using the internet protocol. The most common protocol used is TCP protocol. The connection is established using a three-way handshake.
  1. The browser sends an HTTP request to the server.
  1. Once the TCP connection is established with the server, actual request i.e GET | http://www.google.com is sent.
  1. The server handles the incoming request and sends an HTTP response.
  1. The server processes the request and prepares the response in client requested format i.e. HTML, JSON, XML, etc & adds other details like response status, etc. Once the server is ready with the response, it will be sent back to the client over the established TCP connection.
  1. Browser’s Receiving and Parsing the Response and then Rendering and Displaying Content:
  1. Once the client receives the response from the server, the browser checks the response status code i.e 2xx(success response), 3xx(redirecting request),4xx(client error) 5xx(server error), etc.
  2. The browser will try to translate the response based on the received response type. In our case it is HTML, so it will display the html page. If the response is cacheable is will store the response in the browser cache.

Crawls, indexing and its ways

Crawlers, also known as spiders or bots, are automated programs used by search engines to discover and index web pages on the internet. The process of crawling involves systematically visiting web pages, following links, and extracting information for further processing.

Crawlers follow these steps

  1. Discovery: Crawlers start by visiting a list of known URLs, often provided by a sitemap or seed list
  2. Fetching: After getting a URL, the crawler sends a request to the server hosting the page to retrieve its content.
  3. Parsing: Once the content is retrieved, the crawler analyses the HTML, CSS, and JavaScript to extract relevant information like text content, links, metadata, and more.
  4. Indexing: The extracted information is then processed and stored in a searchable index. This index is like a database that allows search engines to quickly retrieve relevant results when a user performs a search.

How can we do 200 network request (download 200 images) in a web page

We can batch them in chunks (suppose the size of the chunk/no of items in a chunk is 10) and process chunks one-by-one, and in a chunk all items will run concurrently / parallelly. So at any moment, at most the size of the chunk network requests are going on.

  1. If we have domain sharding (for http 1.1, new http has no request limitation) implemented then we can decide any size for a chunk.
  2. No domain sharding: then we can have maximum of 6 items in a chunk, so the total no of chunks will be 200/6 = 34

WebSocket Protocol

WebSocket is a network protocol that enables continuous, two-way (bi-directional), real-time communication between a client (like a web browser) and a server over a single, long-lived connection.

WebSocket operates on its own dedicated protocol, called the WebSocket Protocol (WS).

Just like HTTP, the WebSocket protocol has an unencrypted and an encrypted version, running on the exact same ports:

Webhook

A webhook is a mechanism that allows one server to send real-time data to another server as soon as a specific event occurs. Webhooks are commonly used in web development and integration scenarios to enable communication between different applications or services. Webhooks operate asynchronously, allowing near-real-time communication between systems. The sender does not wait for a response from the receiver, making it a lightweight and efficient way to notify other systems about events.

Difference between Server Sent Events(SSE) and Webhook

In summary, Webhooks are event-driven notifications triggered by specific events, whereas SSE is a continuous stream of updates sent from the server to the client without the need for the client to repeatedly request information.

Compare Different Communication Protocols

Feature / Metric

Short Polling

Long Polling

WebSockets (ws://)

Server-Sent Events (SSE)

Communication Direction

One-way (Client $\rightarrow$Server)

One-way (Client $\rightarrow$Server)

Bi-directional (Full-Duplex)

Uni-directional(Server $\rightarrow$Client)

Protocol Foundation

Standard HTTP

Standard HTTP

HTTP Upgrade $\rightarrow$ Custom TCP Sockets

Standard HTTP (over HTTP/2 or 3)

Connection Lifetime

Ephemeral (Closes instantly)

Near-persistent (Closes on data delivery)

Persistent (Stays open infinitely)

Persistent (Stays open infinitely)

Data Format Support

Any (JSON, XML, Binary)

Any (JSON, XML, Binary)

Any (Text or Raw Binary Data)

Text-only (UTF-8 / JSON streams)

Header Overhead

Extremely High(Sent with every poll interval)

High (Sent with every data cycle)

Minimal (Sent only during initial handshake)

Low (Sent only during connection setup)

Real-Time Latency

High (Bounded by poll interval delay)

Low (Delivered immediately upon data arrival)

Near-Zero (Sub-millisecond)

Near-Zero (Sub-millisecond)

Automatic Reconnection

N/A (Handled by new interval loop)

N/A (Handled by subsequent request loop)

No (Must be manually coded in JS)

Yes (Built-in browser feature)

Firewall / Proxy Friendly

Yes (Standard Web Traffic)

Yes (Standard Web Traffic)

Can be blocked by strict corporate networks

Yes (Runs over standard HTTP paths)

Scalability Bottleneck

High CPU stress from handling frequent requests

Connection pool exhaustion on thread-based servers

Memory limits (Maintaining thousands of open TCP sockets)

Max 6 connections per domain if restricted to HTTP/1.1

Ideal Use Case

Checking status of rare background jobs

Basic notification fallbacks for legacy systems

Multi-user collaboration platforms and real-time games

Generative AI token streaming (ChatGPT) and live tickers

What is pub/sub (publish/subscribe)

Imagine you have a bunch of friends who love getting updates about your life. Instead of calling each friend individually every time you have something new to share, you decide to put a notice on a community board. Now, whenever you have something interesting to tell, you just put a note on that board.

Your friends know to check the board regularly. When they see a new note, they read it and get the latest news about you. They don't have to wait for your call or be available at the same time you're sharing news.

In this analogy:

Pub/Sub is a messaging system used to facilitate communication between different services of a software.

It is an asynchronous and scalable messaging system that decouples services producing messages from services consuming those messages.

Instead of communicating directly, publishers send their messages to a central broker (pub-sub system). This broker is responsible for managing the subscriptions and distributing the messages to the appropriate subscribers.

What is CI/CD

Code Commit -> build -> test -> deploy

CI/CD automates the process of integrating and deploying code, leading to faster development cycles.

CI: Continuous Integration

CD: Continuous Deployment/Delivery

E.g. Jenkins, github actions, gitlab ci/cd

What is load balancer

A load balancer is a networking device or software application used in distributing incoming network traffic across multiple servers or computing resources in a way that optimises resource utilisation, ensures high availability, and enhances the performance of applications or websites.

What is domain sharding

It is a legacy technique used during HTTP/1.1, current http versions HTTP/2, HTTP/3 does not have these limitations. There was a limit of the maximum number of concurrent requests (HTTP/1.1) a browser can make to a single origin.

  1. Chrome, Firefox, Edge: 6
  2. Safari: 10

So to overcome this, domain sharding come into picture

Domain sharding is a technique used to overcome the limitations on the number of concurrent connections a web browser can make to a single domain. It involves spreading resources (like images, scripts, stylesheets, etc) across multiple subdomains of the main domain.

What is CDN

CDN stands for Content Delivery Network. It is a distributed network of servers present at multiple locations which is responsible for serving web resources (like images, videos, scripts, stylesheets, etc.) to clients in a faster and more reliable manner. e.g. Akamai Technologies, cloudflare, Amazon CloudFront, Google Cloud CDN, etc.

Here's how a CDN works:

Normalisation

Normalization is the process of organizing a database to reduce data redundancy (duplication) and prevent data anomalies (bugs when inserting, updating, or deleting records).

The goal is to divide large, messy tables into smaller ones and connect them using relationships, ensuring every piece of data is stored in exactly one place.

Middleware

Middleware is a software component that acts as a bridge between different parts of a software application. It enables communication, data processing, and interactions between different modules or components in a system.

What is next() in Node.js

In Node.js web frameworks like Express, next() is a control-flow function passed as the third argument to middleware handlers. It acts as a signaling mechanism that tells the server: "I am done processing my part of this request; hand it off to the next middleware function in line."

[ HTTP Request ] ───> [ Middleware 1 ] ─── next() ───> [ Middleware 2 ] ─── next() ───> [ Controller Route ]  ───>  [ HTTP Response ]

three-way handshake of TCP

TCP (Transmission Control Protocol), is a protocol used by devices/systems to communicate over a network.

The three-way handshake is a fundamental process in the establishment of a TCP (Transmission Control Protocol) connection between two devices, typically a client (initiating device) and a server (responding device). This process ensures that both devices are ready to send and receive data in a reliable manner.

  1. SYN (Synchronise) - Client to Server:
  1. The client sends a TCP segment with the SYN flag (SYN = 1, ACK = 0) set to the server.
  2. This segment indicates that the client wants to establish a connection and is ready to synchronise sequence numbers.
  1. SYN-ACK (Synchronise, Acknowledge) - Server to Client:
  1. The server receives the SYN segment from the client and acknowledges it by sending a TCP segment with both the SYN and ACK flags set (SYN = 1, ACK = 1).
  2. The server also generates its own initial sequence number (ISN) for this connection.
  3. This segment confirms the client's request to establish a connection and also synchronises the server's sequence numbers.
  1. ACK (Acknowledge) - Client to Server:
  1. Upon receiving the SYN-ACK segment from the server, the client acknowledges it by sending a TCP segment with the ACK flag set and its own initial sequence number (ISN) incremented by one.
  2. This segment acknowledges the server's readiness to establish the connection and confirms synchronisation of sequence numbers.

Browser Same Origin Policy

The Same-Origin Policy (SOP) is a foundational security mechanism built into all modern web browsers. Its primary job is to isolate code loaded from one website, preventing it from interacting with or stealing sensitive data from another website.

It essentially stops a malicious site you visit in one browser tab from reading your bank account or email data open in another tab.

CORS

CORS (Cross-Origin Resource Sharing) is a mechanism by which data or any other resource from different origins (different domain/subdomain/port/protocol) could be shared intentionally.

A preflight request is a specific type of HTTP request that is sent by a web browser to a server as part of the Cross-Origin Resource Sharing (CORS) mechanism.

  1. do preflight/options request, in order to check that the server will permit the actual request. In that preflight request, the browser sends headers that indicate the HTTP method and headers of the actual request.
  2. responding to preflight, the server sends back additional headers (would contain the Allowed methods, Allowed origin details about the target site), which is then together with the original header used in the actual request.
  3. After deciding whether the target site could return the requested information based on this response, the actual request is sent by the browser.

HTTP Methods

http 1/2 and httpS

Http 1.1: HTTP/1.1 processes requests and responses sequentially. This means that if a browser wants to load multiple resources (like HTML, CSS, JavaScript, images) from a server, it sends one request at a time and waits for each response before sending the next request.

Http 2: One of the biggest improvements in HTTP/2 is the ability to send multiple requests and receive multiple responses over a single TCP connection. This eliminates the problem of head-of-line blocking.

HTTP - A protocol used by clients (e.g. web browsers) to request resources from servers (e.g. web servers).

HTTPS - A way of encrypting HTTP. It basically wraps HTTP messages up in an encrypted format using SSL/TLS.

XMLHttpRequest (XHR)

XHR objects are used to interact with servers. Using XHR we can retrieve data from a URL without having to do a full page refresh. This enables a web page to update just part of a page without disrupting what the user is doing.

XMLHttpRequest is used heavily in AJAX programming.

AJAX (Asynchronous JavaScript and XML)

using AJAX web applications can do API calls and are able to make quick, incremental updates to the user interface without reloading the entire browser page. This makes the application faster and more responsive to user actions.

JWT (JSON Web Token)

JWT is a standard for securely transmitting information between parties as a JSON object. It is commonly used for authentication in web applications. In simple terms, a JWT is like a secret message in a special envelope that only certain people can open because they have the right key. It's a secure way to share information over the internet.

Here's how authentication works with JWT:

In the digital world, JWTs are used for authentication. After we log in to a website, the server sends a JWT. This JWT contains our user ID (payload) and is locked with a secret key (signature). In our subsequent requests to the server, this JWT is included in the request. The server can then read the user ID from the JWT and know who we are without asking for our password every time.

Cross Site Scripting (XSS)

Cross Site Scripting (XSS) is a vulnerability in a web application that allows a third party to execute a script in the user’s browser on behalf of the web application. It allows the attacker to compromise the interactions that users have with a vulnerable app. It usually happens because the website does not filter malicious code, it is mixed with normal code, and the browser has no way to distinguish which scripts are trustworthy, which leads to the execution of malicious code.

Three types of XSS attack:

  1. Reflected XSS: It arises when an application receives data in an HTTP request and includes that data within the immediate response in an unsafe way.
  2. DOM XSS: DOM-based XSS arises when an application contains some client-side JavaScript that processes data from an untrusted source in an unsafe way, usually by writing the data back to the DOM.
  3. Stored XSS: Stored XSS (also known as persistent or second-order XSS) arises when an application receives data from an untrusted source and includes that data within its later HTTP responses in an unsafe way.

Preventing XSS vulnerabilities is likely to involve a combination of the following measures:

Content Security Policy

CSP allows us to define origins from where resources are allowed to be loaded and executed in our web page.

Content Security Policy (CSP) is a security feature implemented by web browsers to mitigate the risks of cross-site scripting (XSS) attacks, data injection, and other code injection attacks that can compromise the security of a web app.

To enable CSP, we need to configure our web server to return the Content-Security-Policy HTTP header.

Alternatively, the <meta> element can be used to configure a policy, for example:

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';"  />

Directives

Cross Site Request Forgery (CSRF)

It is a type of security vulnerability that occurs when a malicious website tricks a user's browser into making an unintended request to a different site on which the user is authenticated. This can lead to actions being taken on the user's behalf without their consent.

e.g.

  1. Let's say you are logged into your online banking account in one browser tab.
  2. While still logged in, you visit a malicious website in another tab.
  3. The malicious website contains a hidden form that submits a request to your bank's website, using your authenticated session without your knowledge.
  4. This request might perform actions like transferring funds or changing account settings, since it's being made in the context of your authenticated session.

To prevent CSRF attacks, web applications can implement measures like:

  1. CSRF Tokens: Generating a unique token for each user session and requiring this token to be submitted with each request. This token is checked on the server side to verify the legitimacy of the request.
  2. Same-Site Cookies: Setting cookies with the SameSite attribute to restrict when they are sent along with a request. This helps prevent cookies from being sent in cross-origin requests.

Man In The Middle Attack (MITM)

MITM refers to the fact that the attacker establishes a unique connection with both ends of the communication, and exchanges the data they receive, so that both ends of the communication think they are passing through a private connection and a direct conversation with each other, but in fact the entire conversation is completely controlled by the attacker. In a MITM attack, the attacker can intercept the two-way communication and insert new content.

Subresource Integrity (SRI)

Subresource Integrity (SRI) is a security feature that enables browsers to verify that resources they fetch (for example, from a CDN) are delivered without unexpected manipulation. It works by allowing you to provide a cryptographic hash that a fetched resource must match.

Tokenisation

Tokenisation is the process of replacing sensitive data or real-world assets with unique, non-sensitive digital identifiers (called "tokens"). It is widely used across three main fields: digital payments for security, blockchain technology for asset ownership, and artificial intelligence/machine learning for language processing.

Generate Random Unique ID in Web

const uniqueId = window.crypto.randomUUID();

console.log(uniqueId);

Smart Contracts

Smart contracts are self-executing contracts with the terms and conditions written directly into code. They run on blockchain platforms, ensuring that the terms of the contract are automatically enforced, executed, and recorded without the need for intermediaries like banks or legal systems (external 3rd party). Smart contracts are often used to create and manage tokens or cryptocurrencies.

Decentralised: Smart contracts operate on blockchain networks, which are decentralised and distributed across many nodes (computers). This decentralisation ensures that no single entity has control over the contract.

Transparency: All transactions and operations performed by a smart contract are recorded on the blockchain and are transparent and publicly viewable.

Digital Signature

Memento Architecture

The Memento Design Pattern is a behavioral design pattern that allows us to capture and save the current internal state of an object so that it can be restored to this exact state later— all without violating encapsulation (keeping the object's private data secure).

It is the foundational pattern used to build Undo/Redo mechanisms, transaction rollbacks, or game save-state systems.

Next.js

SSG - Static Site Generation

Entire application is fully built and compiled into flat, static HTML, CSS, and JS files at build time (during the CI/CD deployment pipeline). These static files are pushed directly to a globally distributed Content Delivery Network (CDN). When a visitor hits your URL, the CDN instantly serves the pre-built files from the edge node nearest to them.

SSR - Server-Side Rendering

In (SSR), a web page is rendered on the server (where the web app is deployed) and then sends the fully rendered HTML page to the client (browser), then client hydrates it (attach event listeners)

Advantages of Server-Side Rendering:

CSR - Client-Side Rendering

In CSR, the server sends a basic HTML file along with a JavaScript bundle. The JavaScript code is responsible for generating the content and rendering it in the browser (client).

Advantages of Client-Side Rendering:

In practice, many applications use a combination of both Server-Side Rendering and Client-Side Rendering (CSR) for the best of both worlds. This is sometimes referred to as "Hybrid Rendering" or "Universal Rendering".

SSR is often used for the initial page load, while client-side rendering takes over for subsequent interactions, providing a more interactive experience.

How will you select between CSR, SSR & SSG for your application? What parameters will you consider?

1. When to use CSR (Client-Side Rendering)

Use CSR when your application is highly interactive, locked behind a login screen, and does not require search engine visibility.

2. When to use SSR (Server-Side Rendering)

Use SSR when your data changes constantly, needs to be updated in real-time on every click, but must be indexed perfectly by search engines.

3. When to use SSG (Static Site Generation)

Use SSG when the content is public, rarely changes, and your primary goals are maximum speed and top-tier SEO.

Metric / Feature

CSR

SSR

SSG

Initial Page Load Speed

Slow (Waiting for JS to download & run)

Fast (HTML arrives completed)

Blazing Fast (Served instantly via edge CDN)

Data Freshness

Real-time (Fetched on demand)

Real-time (Computed on every request)

Delayed (Fixed at build time unless using ISR)

Server Cost

Very Low (Static files served via storage)

High (Requires continuous active node server)

Very Low (Static asset hosting only)

SEO

Poor (Bots struggle to execute heavy client JS)

Excellent

Excellent

What is hydration in Next.js and when can it cause UI mismatches?

Hydration is the process where React takes the static HTML pre-rendered by Next.js on the server and injects JavaScript event listeners (onClick, onChange, etc) into it once it reaches the browser, making the page fully interactive.

What is a Hydration Mismatch?

When the HTML generated on the server does not perfectly match the initial HTML React calculates in the browser. When they don't align, React loses track of the DOM and throws an error.

  1. Using Browser-Only APIs During Render: Referencing window, document, or localStorage directly in your component markup (the server evaluates it as undefined, while the client evaluates the real value).
  2. Dynamic / Non-Deterministic Data: Using new Date() or Math.random(). The server generates one timestamp during the build/request, but the client generates a slightly later timestamp during hydration.
  3. Invalid HTML Structure: Nesting a <div> inside a <p> tag. The browser's native parser automatically forces a fix to the broken HTML structure before React can hydrate it, destroying the expected layout matching template.

Fix: Use a useEffect Guard: Keep browser-dependent stuffs inside useEffect so it only triggers after the initial client hydration is complete.

React Server Components

React Server Components (RSC) is an architectural paradigm/pattern that splits React components into two categories based on where they execute: Server Components and Client Components.

Instead of sending heavy JavaScript libraries to the browser, RSC renders static parts on the backend and sends a lightweight UI description (the RSC payload) to the client, which seamlessly merges with interactive components.

Feature

Server-Side Rendering (SSR)

React Server Components (RSC)

Primary Goal

Fast Initial Load: Instantly converts the initial React tree into raw HTML so the user doesn't see a blank screen while JS downloads.

Zero Bundle Size & Direct Backend Access: Decouples components so heavy backend logic stays off the client.

Where the code lives

The code for every component is still bundled and sent to the browser so React can hook up interactivity (hydration).

The code for Server Components never leaves the server. Only the final UI layout structure is sent.

Interactivity

Components can use state, hooks, and browser events (after hydration is complete).

Server components cannot use state or browser hooks. Interactivity must be passed to Client Components.

Data Fetching

Happens at the top page level before rendering (e.g., getServerSideProps or router-level fetches).

Can happen dynamically at the individual component level using standard async/await.

State Preservation

Refetching or navigating via full SSR will wipe out existing client state (like text typed into an input box).

Refetching data from the server updates the UI dynamically without losing the user's current client-side state.

Features of Next.js

 TL;DR:

  1. SSR/RSC/SSG/ISR
  2. Automatic Code Splitting
  3. Default Routing support (without using any external library)
  4. Image Optimisation: Images are automatically optimised for different screen sizes and devices, improving website performance and user experience. Next.js uses techniques like lazy loading, responsive images, and format detection to deliver the most efficient image experience.
  5. API Proxying
  6. Built in Internationalisation (i18n) Support (without using any external library)
  7. Many options of Data Fetching (getStaticProps, getServerSideProps)
  8. Built-in Typescript support
  9. Turbopack Bundler (based in Rust, introduced in Next 14)
  10. Server Actions (introduced in Next 14, allows us to run server-side code directly from React components, simplifying data mutations and user interactions without API routes in specific scenarios.)

  1. Rendering Strategies

  1. React Server Components (RSC): Default in App Router. Renders on server; minimizes client-side JS bundle size.
  2. SSR (Server-Side Rendering): HTML generated on every request. Best for dynamic, personalized data.
  3. SSG (Static Site Generation): HTML generated at build time. Extremely fast, ideal for SEO and static content (blogs, marketing).
  4. ISR (Incremental Static Regeneration): Revalidates and updates static pages in the background after deployment without full rebuilds.
  1. Routing (App Router)

  1. File-Based Routing: Directory structure defines URL paths (`app/dashboard/page.js` -> `/dashboard`).
  2. Special Files:

  • `layout.js`: Shared UI across sub-routes; preserves state on navigation.

  • `loading.js`: Automatic loading UI using React Suspense.

  • `error.js`: Isolated error boundaries for graceful degradation.

  1. Dynamic Routes: Folders named with brackets ( `[id]` ) capture dynamic path parameters.
  1. Data Fetching & Full-Stack

  1. Server Actions: Direct execution of asynchronous server-side code from client forms/events without manual API endpoints.
  2. Route Handlers: Custom backend endpoints (`GET`, `POST`, etc.) built inside `route.js` using standard Web APIs.
  3. Middleware: Code executed before a request is completed; ideal for auth, redirects, and geo-targeting.
  1. Built-In Optimizations

  1. next/image: Automatic resizing, modern formats (WebP), lazy-loading, prevents Layout Shift (CLS).
  2. next/font: Self-hosts fonts automatically; eliminates external network requests and avoids font flicker (FOUT).
  3. Code Splitting: Splitting bundles per page so users only download necessary JS.
  1. Tooling & DX

  1. SWC / Turbopack: Rust-based compiler replacing Babel/Webpack for fast build times.
  2. Fast Refresh: Instant live-editing while preserving component state.
  3. TypeScript: Out-of-the-box support with automatic type generation for routes.

Next.js Streaming UI

Next.js Streaming allows a server to break down a webpage's HTML into chunks and send them to the browser piece-by-piece over a single HTTP connection as soon as they are ready.

Instead of waiting for slow database queries to load the entire page, Next.js streams the static layout (headers, sidebars) instantly, leaving loading placeholders where the slow data will eventually slide in.

It can be done in 2 ways

  1. Component Level Streaming: By wrapping a slow React Server Component in a <Suspense> boundary, we isolate its loading state from the rest of the layout.
  2. Page Level Streaming: We can use Next.js's file convention. By creating a loading.tsx file inside a route folder, Next.js automatically wraps the entire page.tsx in a fallback boundary.

For more details check nextjs-streaming of coreJs Repo

Server Actions

Server Actions are asynchronous server-side functions that we can invoke directly from our React components (both Client and Server components) without manually writing an API route. They provide a seamless bridge between our frontend UI and backend logic.

Instead of capturing a form submission, writing a manual fetch('/api/endpoint') call, and setting up a separate API route file to handle the request, you define a function with the "use server" directive. Next.js automatically handles the network communication behind the scenes.

Basic Example:

// A Server Component

export default function OrderForm() {

 

  // The Server Action runs entirely on the server

  async function createOrder(formData) {

    "use server";

    const itemId = formData.get("itemId");

    // Securely run database operations directly here

    await db.order.create({ data: { itemId } });

  }

  return (

    <form action={createOrder}>

      <input type="text" name="itemId" />

      <button type="submit">Place Order</button>

    </form>

  );

}

getStaticPaths, getStaticProps, getServerSideProps, getInitialProps (Legacy Stuffs)

  1. getStaticPaths (Before Next 14)
  1. getStaticPaths is a method in Next.js that allows us to specify which paths should be pre-rendered at build time when using Static Site Generation (SSG).
  1. getStaticProps (SSG - Before Next 14):
  1. Usage: Used in a page component to fetch data at build time for Static Site Generation - SSG
  2. Execution: Runs at build time, not in the client-side JavaScript bundle.
  3. revalidate in getStaticProps: revalidate in getStaticProps, specifies how often Next.js should re-generate the static page.
  1. getServerSideProps (SSR - Before Next 14):
  1. Usage: Used in a page component to fetch data on each request, on the server-side (Server Side Rendering).
  2. Execution: Runs on the server every time a request is made to the page.
    export async function getServerSideProps / geStaticProps() {
            const response = await fetch('https://api.example.com/data'); // Fetch data from an API or database
            const data = await response.json();
            return {
                    props: { data, }
            };
    }
    The
    getStaticProps & getServerSideProps function must return an object with a props key. The value of props will be passed as props to the page component.
  1. getInitialProps (obsolete - the ancient way - not used anymore):
  1. Usage: Used in both page components and regular React components.
  2. Execution:
  1. In page components, it can run on both the server and the client. On the server, it runs during the initial request. On the client, it runs when navigating to the page using the client-side router.
  2. In regular React components, it only runs on the client.
  1. Return Value: Should return an object with the data that will be merged into the component's props.
    MyComponent.getInitialProps = async () => {         
            const response = await fetch('https://api.example.com/data'); // Fetch data from an API
            const data = await response.json();
            return { data };
    };

Feature Strategy

Pages Router (Old)

App Router (Modern)

Static Generation (SSG)

getStaticProps

fetch(url) (Default behavior)

Server-Side Rendering (SSR)

getServerSideProps

fetch(url, { cache: 'no-store' })

Incremental Static (ISR)

getStaticProps + revalidate

fetch(url, { next: { revalidate: 60 } })

Dynamic Path Generation

getStaticPaths

generateStaticParams()

React.js

React

React.js is an open-source JavaScript library used for building user interfaces or UI components for web applications. React is particularly popular for creating interactive and dynamic user interfaces because it allows developers to efficiently update and render components as data changes. React is often described as a declarative library for building user interfaces. In a declarative approach, you specify what you want to achieve, and the library (React in this case) takes care of updating the DOM to match the desired state.

Key features and concepts of React include:

  1. Component-Based Architecture: React applications are built using components, which are self-contained, reusable modules that encapsulate a specific piece of UI
  2. Reusable Components: Components in React are designed to be reusable, which promotes code modularity and maintainability.
  3. Component Lifecycle: React components go through various lifecycle stages (mounting, updating, un-mounting), and one can hook into these stages to perform actions at specific points in a component's life.
  4. Virtual DOM: React maintains a lightweight representation of the DOM, known as the virtual DOM. When data changes, React compares the new virtual DOM with the previous one to identify the minimal set of updates needed to reflect the changes in the actual DOM. This makes React very efficient in managing UI updates.
  5. JSX (JavaScript XML): React uses JSX, a syntax extension for JavaScript, which allows developers to write HTML-like code directly within their JavaScript files. JSX makes it easier to visualise and build component hierarchies.
  6. Unidirectional Data Flow: React follows a unidirectional data flow, which means that data in an application flows in a single direction. This makes it easier to track changes and debug code.

Challenges with React

  1. SEO
  2. Props drilling: passing props to deep down the component
  3. State Management
  4. Unnecessary Re-renders: can be prevented using React.memo

Virtual DOM

It is a key concept in React that helps to efficiently update the actual DOM, making React highly performant.

Here's a breakdown of how it works:

Reconciliation

Reconciliation is the algorithm React uses to differentiate one virtual dom tree with another to differentiate which parts need to be changed.

Advantages of the Virtual DOM

React Fiber Architecture

React Fiber is a job-scheduling architecture that manages how, when, and in what priority the differences found in the Virtual DOM are calculated and pushed to the real DOM.

  1. Reconciliation (The Whole System)
  1. What it is: The umbrella term for the entire process React uses to keep the user interface in sync with the underlying application state. It is the full backstage operation.
  1. Virtual DOM (The Blueprint)
  1. What it is: A lightweight, in-memory JavaScript representation of the user interface.
  2. Why it matters: It acts as a fast, cheap scratchpad. React can create and tear down these virtual objects instantly without touching the heavy, slow browser DOM.
  1. Diffing (The Logic)
  1. What it is: The mathematical algorithm that compares the previous Virtual DOM tree with the newly generated one.
  2. Why it matters: It handles the intelligence. It pinpoints the exact elements that changed so React knows the absolute minimum number of mutations required to update the screen.
  1. React Fiber (The Job Scheduler)
  1. What it is: The underlying scheduling engine and linked-list data structure that hosts and executes the reconciliation process.
  2. Why it matters: It handles the mechanics. It breaks the heavy diffing work down into tiny, bite-sized tasks. If a user interacts with the page (like typing or clicking) midway through a render, Fiber pauses the background calculation, handles the urgent user action, and then smoothly resumes updating the screen.

Analogy

Think of a kitchen renovation project managed by a company called Reconciliation Inc

Key Benefits of React Fiber

React Fiber’s job-scheduling architecture brings three major upgrades to how web applications run:

React 18 Core Features

React 18 introduces structural changes to the core rendering engine. At its heart is the concept of Concurrent Rendering, which allows React to pause, resume, or abandon updates in the background to ensure the main UI thread stays responsive.

Key changes introduced in React 18:

e.g. Automatic batching inside an asynchronous fetch call:

// React 18 triggers exactly ONE re-render for both the state updates

fetch('/api/user').then(() => {

  setIsLoading(false);

  setUserData(data);

});

e.g. Managing heavy filtering tasks with useTransition:

const [isPending, startTransition] = useTransition();

const [search, setSearch] = useState('');

const handleSearch = (e) => {

  setSearch(e.target.value); // Urgent: Shows typed letters instantly

 

  startTransition(() => {

    setFilterQuery(e.target.value); // Non-urgent: Filters thousands of items in the background

  });

};

What is 'flushSync' in React 18, and what's a realistic scenario where you'd wrap a state update with it?

flushSync is a utility (from react-dom package) that forces React to complete a state update synchronously and instantly mutate the real browser DOM.

It acts as an escape hatch to bypass React 18's automatic batching. The exact millisecond the callback code inside flushSync finishes running, React forces a visual layout update before moving to the next line of JavaScript.

The Problem Without flushSync

Standard React state updates are asynchronous. If you add a new message to a list state (setMessages) and then immediately calculate the container's height on the next line to scroll down, the browser DOM hasn't actually updated yet. Your code will read the old height, causing the scroll anchor to miss the new message.

The Solution With flushSync

By wrapping the state update in flushSync, you force the new message to render to the screen instantly. On the very next line of code, your scroll calculation reads the correct new height, snapping the container perfectly to the bottom.

Use it when: You must read or modify DOM properties (like .getBoundingClientRect(), .focus(), or .scrollTop) immediately following a state modification.

useSyncExternalStore

useSyncExternalStore is a built-in React Hook introduced in React 18 that allows components to safely subscribe to external data stores (any state management container outside of React's built-in useState or useReducer systems).

The hook takes three arguments and returns the current snapshot of the external store's data:

  1. subscribe: A function that registers a callback with the external store. React calls this to listen for data changes.
  2. getSnapshot: A function that returns the current value of the store. It must return a cached/stable value if the data hasn't changed.
  3. getServerSnapshot (Optional): A function that returns the initial snapshot during Server-Side Rendering (SSR) and hydration.

How It Works Internally (The Mechanics)

When a component mounts with useSyncExternalStore, React initializes a hidden internal subscription.

  1. The Handshake: React passes an internal updater function (the callback) to your subscribe function.
  2. The Watcher: The external store saves this callback. Whenever the data inside that store mutates, it must execute this callback.
  3. The Trigger: Calling the callback alerts React that an external change just happened.
  4. The Consistency Check: React immediately runs your getSnapshot() function. It compares the new snapshot value against the previous snapshot value using a strict reference check (Object.is). If the value or reference is different, React forces a synchronous re-render to keep the UI perfectly synced.

The Golden Rule of getSnapshot ⚠️

The most critical aspect of useSyncExternalStore is that getSnapshot must return a cached or immutable reference if the data hasn't changed.

For more details check react-optimisation of coreJs repo

React 19 Core Updates

React 19 focuses on Data Mutation and Form Automation, introducing native features to eliminate standard loading, error, and optimistic UI state boilerplate.

  1. Form Actions (Async Transitions): Pass async functions directly to the HTML <form> action. React automatically handles the pending lifecycle, errors, and form resets.
  2. New Built-in Hooks
  1. useActionState: Tracks the result, errors, and pending loading state of an async Action wrapper.
  2. useFormStatus: Allows nested child components to read parent form metadata without prop-drilling. Note: Must be nested inside a <form> element.
  3. useOptimistic: Instantly updates a display state to a predicted "successful outcome" while an async action runs, rolling back to source data on failure.
  4. The use API: Can be called conditionally to unpack Promises or read Context streams mid-render.
  1. Quality of Life Structural Upgrades
  1. No More forwardRef: ref is now a standard, regular prop; no need to wrap components in forwardRef().
  2. Context as a Provider: Simplified context usage: use the context variable directly (<Context>) as a wrapper instead of <Context.Provider>.
  3. First-Class Asset Management: Metadata tags placed anywhere in a component are automatically hoisted to the HTML <head>.

Key in React

In React, the key prop is a special attribute that is included when creating a list of elements. It helps React identify which items have changed, been added, or been removed.

Uniqueness: The key prop should be unique among siblings of the same parent. It helps React differentiate between components and efficiently update the virtual DOM.

Stability: The key should be stable across re-renders. Avoid using indexes as keys as if the list changes, it can lead to unexpected behaviour. E.g. If you insert an item at the beginning of a long list, every subsequent item's index changes (0→1, 1→2, etc.). React assumes the entire list has fundamentally changed and destroys/re-creates every single DOM node instead of just inserting the new one.

Performance: Providing a key helps React optimise the rendering process. Without keys, React might need to recreate the entire list when there are changes, which can be less efficient.

JSX (JavaScript XML)

JSX is a syntax extension for JavaScript often used with React. It allows us to write HTML-like code directly within JavaScript files. JSX provides a more readable and concise way to describe the structure of user interfaces.

React Component

A React component is a self-contained, reusable module that encapsulates a specific piece of UI. Components in React are designed to be reusable, which promotes code modularity and maintainability.

In React, components can be thought of as JavaScript functions or classes that return elements, specifying what should be rendered on the screen. They can also maintain their own internal state and receive data through "props".

There are two main ways to define a React component: 1. Functional Components, 2. Class Components

Component State and Props

Props (Properties):

Props are inputs to a React component. They are passed down from a parent component to a child component. Props are read-only and cannot be modified within the component itself. They allow us to customise the behaviour or appearance of a component.

State:

State is a way for a component to keep track of information that may change over time. It represents the current state of the component and determines how it should render. Unlike props, state is managed within the component itself.

React.CreateElement

React.createElement is a method provided by the React library for creating React elements, which are the building blocks of React applications.

const element = React.createElement(elementType, props, children);

e.g.

const element = React.createElement('div', { className: 'my-class' }, 'Hello, World!');

i.e.

const element = <div className="my-class">Hello, World!</div>;

Functional vs Class components

Functional

Class

Functional components are just JavaScript functions that take props as an argument and return React elements.

Class components are ES6 classes that extend React.Component.

With Hooks, functional components can use useState and other hooks to manage state.

Class components can manage their own local state using this.state and this.setState().

useEffect for life cycle methods

Class components have access to lifecycle methods like componentDidMount, componentDidUpdate, and componentWillUnmount

Functional components are typically faster and more lightweight than class components. This is because they don't have the overhead associated with class instantiation and lifecycle methods.

React Life Cycle Methods

React lifecycle methods are special functions that get called at different stages in a component's life. They allow us to hook into these different points to perform actions, such as setting up a component, updating it, or cleaning it up. As of React 16.3, some lifecycle methods have been deprecated in favour of safer alternatives called "lifecycle hooks" which are used with functional components and React Hooks.

Class Component: componentDidMount, componentDidUpdate, and componentWillUnmount

Functional Component: useState, useEffect, useRef, useContext, useReducer

React Hooks

React Hooks are functions that allow us to use state and other React features in functional components. They were

e.g. useState, useEffect, useRef, useContext, useReducer

Custom Hooks

A custom hook in React is a JavaScript function that uses one or more built-in hooks. It helps us to encapsulate, separate complex logic and reuse stateful logic across multiple components.

Custom hooks must always start with the word "use”, it helps React to recognise them as hooks.

Features of custom hooks:

  1. Reusing Logic: Custom hooks allows us to extract and reuse stateful logic that may be used in multiple components. This can include state management, side effects, subscriptions, and more.
  2. Separation of Concerns: Custom hooks promote separation of concerns. They allow us to keep our components focused on rendering and user interactions, while moving complex logic into hooks.
  3. Composable: Custom hooks can be composed together. Multiple custom hooks can be used in a single component, allowing us to reuse different pieces of logic independently.
  4. Manage State and Side Effects: Custom hooks can use built-in hooks like useState, useEffect, etc. This enables us to manage component state and perform side effects within the custom hook.

Stateful Logic

Any program's logic that depends on or interacts with the current state of the component. It involves managing and updating the state of the component based on user interactions, data changes, or other events.

Examples of stateful logic in a React application might include:

Side Effect

In React, a side effect refers to any code that affects something outside the scope of the current component. Side effects are often asynchronous operations that don't happen immediately.

Common examples of side effects in React components include:

The primary way to manage side effects in functional components is through the useEffect hook.

useEffect(() => {

          // Side effect code here

        return () => {

                    // Cleanup code (optional)

        };

}, [/* dependencies */]);

Synthetic Events

Synthetic events are a cross-browser wrapper around the native browser events. They provide a consistent interface for handling events across different browsers.

Event Pooling: React reuses synthetic event objects. This means that the event object is nullified after the event callback has been called. If you need to access event properties asynchronously, you should call event.persist().

function handleClick(event) {
    event.persist(); // This allows you to access event properties later

    setTimeout(() => {
        console.log(event.type); // This will work now // without event.persist(), event will be null here
    }, 100);
}

Error Handling in React Application

  1. Try Catch Statement: We can use try catch at places/code logic (react side-effects) where we think that something might fail, and display meaningful messages to users when code goes to catch.
  2. then/catch for promises: We can also display meaningful messages to user when code goes to catch while using then/catch for promises
  3. React Error Boundary: It is used to gracefully handle errors in React Apps. It helps us prevent the full app crash and let us define a fallback UI when an error is detected in our Application. It is generally wrapped at the top level of the app so that errors at any of the children components can be detected by our error boundary.

What triggers a react functional component re-render and what is happening after the rerender?

  1. A react functional component can re-render if:
  1. Its state changes
  2. Its props changes
  3. Its parents component re-renders
  4. context changes (if the component consumes any context and any value in the context get changed)

After rerendering, React compares the new Virtual DOM with the previous one, and finds the most optimised way (minimum number of operations - add/delete/modify) to update the actual DOM.

How can we diagnose unnecessary re-renders and fix them with clarity?

  1. Component Composition (Move State Down): Instead of forcing a heavy parent component to hold high-frequency state (like inputs or scroll tracking), split that state out into its own dedicated child component.
  2. React.memo + Stable References (useMemo and useCallback): If a child component must live inside a shifting parent, wrap the child in React.memo so it only updates if its props change. If we pass an object, array, or function as a prop, JavaScript recreates their memory references on every render, which breaks React.memo. so must lock those references down using useMemo and useCallback.
  3. Creating Store & useSyncExternalStore: Create a external store for a state, and subscribe to it using useSyncExternalStore in the components where that state is used. Or we can also use any state management libraries like redux, zustand, jotai

ref: react-optimisation of coreJs Repo

How was your experience in optimizing the performance of React applications. What tools, techniques, or strategies have you employed to enhance rendering speed and reduce load times?

I target a LCP under 2.5s and INP under 200ms, I profile bottlenecks using pagespeed, Bundle Analyzer.

Techniques I use:

  1. State Localization: Moved state down to the leaf nodes, preventing minor data updates from triggering app-wide re-render chains.
  2. Automated Memoization: Leveraged the React Compiler to automate reference equality, eliminating manual useMemo/useCallback boilerplate.
  3. Intelligent Caching: Swapped unoptimized useEffect fetch blocks for React Query to gain instant server-state caching and kill duplicate API calls.
  4. Bundle Splitting & Deferred Loading: Used React.lazy() and Suspense to split code at route boundaries, while deferring non-critical telemetry scripts until after initial paint to maintain a 95+ Web Vital score.
  5. List Virtualization: Implemented DOM windowing for heavy data tables to render only viewport-visible elements, keeping paint times sub-millisecond.

HOC

Higher-Order Component is basically a component in react that takes a component as an argument and returns a new component with additional props or functionality.

It is used to enhance the functionality of a component by wrapping it with another component. HOCs are a way to reuse code logic and add additional features to a component without modifying the component itself.

e.g. HOCs are commonly used for tasks like authentication and authorization. They can check if a user is authenticated and conditionally render components based on their authentication status.

Suspense

React Suspense is a feature that allows components to "suspend" rendering while they wait for some data to load. This is especially useful for handling asynchronous operations like dynamic import of components using lazy loading.

const LandingPage = lazy(() => import('./pages/LandingPage'));

<Suspense fallback={<div>Loading...</div>}>

<LandingPage />

</Suspense>

Routing in React

Routing in React is the process of managing the navigation of a web app by dynamically rendering different components based on the URL. This allows users to move between different parts of the app without the need for a full page refresh.

To implement routing in React, we typically use a library called react-router.

npm install react-router-dom

React Pure Component (React.memo in functional comps)

PureComponent is similar to Component but it skips re-renders for the same props and state. To skip re-rendering a class component for the same props and state, extend PureComponent instead of Component.

React.PureComponent provides a default implementation of the shouldComponentUpdate method that performs a shallow comparison of the current and next props and state. It helps to optimise the rendering performance by preventing unnecessary re-renders when the props and state are unchanged.

Stateless Component

Stateless components typically refer to components in a user interface that do not manage their own state.

Stateless components receive data through their props (properties) and render it in a certain way, but they don't have their own internal state. This means that their behaviour is purely based on the data they receive, and they don't have the ability to modify that data directly.

e.g. button, loader, etc

Controlled vs. Uncontrolled Components

Controlled Component: The form data is handled directly by a React component's state.

Uncontrolled Component: The form data is handled directly by the browser DOM itself.

function ControlledForm() {

  const [username, setUsername] = useState('');

  const handleSubmit = (e) => {

    e.preventDefault();

    console.log(`Submitting Username: ${username}`); // Data is already in state

  };

  return (

    <form onSubmit={handleSubmit}>

      <label>Username:</label>

      <input

        type="text"

        value={username}

        onChange={(e) => setUsername(e.target.value)}

      />

      <button type="submit">Submit</button>

    </form>

  );

}

function UncontrolledForm() {

  const inputRef = useRef(null);

  const handleSubmit = (e) => {

    e.preventDefault();

    // Reaching out directly to the DOM node to read its current value

    console.log(`Submitting Username: ${inputRef.current.value}`);

  };

  return (

    <form onSubmit={handleSubmit}>

      <label>Username:</label>

      <input type="text" ref={inputRef} />

      <button type="submit">Submit</button>

    </form>

  );

}

If we add defaultValue attribute to the input field of a controlled component, then it will throw a warning, that it contains both defaultValue and value. Input elements must be either controlled or uncontrolled (specify either the value prop, or the defaultValue prop, but not both)

React Memo

React memo is used to stop re-rendering of a component if its props have not changed.

import { memo } from "react";

const Todos = ({ todos }) => {

  console.log("child render");

  return (

    <>

      <h2>My Todos</h2>

      {todos.map((todo, index) => {

        return <p key={index}>{todo}</p>;

      })}

    </>

  );

};

export default memo(Todos);

There are two ways to prevent child components from re-rendering.

    - wrapping them in `memo`

    - passing them as `children` prop of a parent component

useReducer

It is an alternative to using useState when the state logic involves multiple sub-values or when the next state depends on the previous one.

The useReducer hook takes two arguments: a reducer function and an initial state. It returns an array with two elements: the current state and a dispatch function. The reducer function is responsible for specifying how the state should change in response to dispatched actions.

e.g.

import React, { useReducer } from 'react';

// Reducer function

const reducer = (state, action) => {

  switch (action.type) {

    case 'INCREMENT':

      return { count: state.count + 1 };

    case 'DECREMENT':

      return { count: state.count - 1 };

    default:

      return state;

  }

};

// Component using useReducer

const Counter = () => {

  // Initial state

  const initialState = { count: 0 };

  // useReducer returns [state, dispatch]

  const [state, dispatch] = useReducer(reducer, initialState);

  return (

    <div>

      <p>Count: {state.count}</p>

      <button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button>

      <button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button>

    </div>

  );

};

useLayoutEffect

We can use useLayoutEffect when we need an immediate DOM update before the screen paints.

Rule of thumb: Default to useEffect. Only use useLayoutEffect if you are measuring DOM nodes (like tooltips, popups, or animations) and need to prevent a visual flash.

import { useState, useLayoutEffect, useRef } from 'react';

function Tooltip({ targetRef, children }) {

  const [tooltipHeight, setTooltipHeight] = useState(0);

  const tooltipRef = useRef(null);

  useLayoutEffect(() => {

    if (tooltipRef.current) {

      // 1. Measure the real DOM element before the browser paints

      const { height } = tooltipRef.current.getBoundingClientRect();

      // 2. Set state immediately to adjust position

      setTooltipHeight(height);

    }

  }, []); // Run once on mount

  // If we used useEffect here, the user would see the tooltip flash at top: 0px for a split second before jumping to its real position.

  return (

    <div

      ref={tooltipRef}

      style={{ position: 'absolute', top: `-${tooltipHeight}px` }}

    >

      {children}

    </div>

  );

}

useMemo vs useCallback

useMemo:

The useMemo hook is used to memoize a value, such as a computed result or an object. It takes a function and an array of dependencies. The value returned by the function will only be recomputed if one of the dependencies has changed.

e.g.

const MyComponent = ({ data }) => {

const memoizedValue = useMemo(() => {

return computeExpensiveValue(data)

}, [data]);

 

return <div>{memoizedValue}</div>;

};

useCallback:

useCallback hook memoizes/caches a function definition between re-renders. The dependency array (the second argument) acts as the cache-validation mechanism. It tells React when to preserve the existing function reference and when to discard it and create a new one.

  1. If a variable used inside the function is left out of the dependency array, it creates a stale closure.
  1. The Problem: The function locks into the values from the render when it was first created.
  2. The Result: Even if the state updates in the component, the function will continue reading the old, outdated value forever.
  1. If you include variables that aren't used inside the function, or pass objects that regenerate on every render, you trigger unnecessary cache clears.
  1. The Problem: The function is forced to rebuild from scratch far too often.
  2. The Result: The function gets a brand new memory reference on almost every render. This completely defeats the purpose of useCallback, forcing any downstream components wrapped in React.memo to break cache and re-render needlessly.

Every time ParentComponent renders, a new handleClick function is created. This can lead to unnecessary re-renders of ChildComponent because it receives a new function reference each time, even if the function logic is the same, which can be optimised using useCallback

e.g.

const MyComponent = ({ onClick }) => {

const memoizedCallback = useCallback(() => {

// callback logic

}, [/* dependencies */]);

return <childComponent onClick={memoizedCallback}>Click me</childComponent>;

};

Adding Style in React

  1. Inline Styles: style={}
  2. External Stylesheets: import './MyComponent.css';
  3. CSS Modules: import styles from './MyComponent.module.css'; CSS Modules are a way to locally scope your CSS by default. Each component gets its own CSS file, and the class names are scoped to that component.
  4. Styled Components (Library):
  1. npm install styled-components
  2. import styled from 'styled-components';
  3. const StyledDiv = styled.div`
        color: blue;
        font-size: 20px;
    `;

Data Flow (Flux Architecture)

Flux is an architectural pattern that helps manage the flow of data in a React application.

Flux follows unidirectional data flow which increases predictability of application code.

The Flux pattern consists of the following parts:

React Context

The React Context API is a way to pass data deep down the nested comp without having to manually pass props through every level of comp (prop drilling). It provides a way to share data between components that are not directly connected.

It is mainly used to pass global data, such as theme information, user authentication, or language preferences, etc.

Here's an overview of how the Context API works:

How would you prevent widespread re-renders when using Context for frequently changing state?

  1. Split the Context: Separate your State and Dispatch (Updater) into two distinct contexts. Components that only trigger actions won't re-render when state changes.
  2. Colocate & Minimize: Break massive global contexts into small, isolated domain-specific contexts (e.g., separate CartContext from a high-frequency FormContext).
  3. Memoize Consumers: Extract the heavy UI into a child wrapped in React.memo, then pass the specific context values into it as props from a lightweight wrapper.
  4. Stabilize the Provider Value: Always wrap your context object in useMemo so it maintains reference stability across parent renders.
  5. Switch to an External Store: For high-frequency state (e.g., animations, forms, real-time data), swap Context for selector-based libraries like Redux,  Zustand, Jotai, or Signals which bypass React's top-down render chain completely.

State Management Libraries (Redux, Zustand, Jotai) vs React Context

In react context, when value in the context updates then the complete component tree re-renders because state lives in the parent component (where we wrapped with Context.Provider) and as the state updates parent re-renders hence the complete component tree (under the parent) re-renders.

But in state management libraries state exists outside React component (in a plain JavaScript store).

  1. React Context: For static, low-frequency, global architectural data (e.g., UI themes, user authentication sessions, app language settings).
  2. Atomic State (Zustand): For medium-to-high frequency updates shared across decoupled components (e.g., interactive dashboards, shopping carts, checkout funnels).
  3. Unidirectional Flux (Redux Toolkit): For enterprise-scale applications with complex background pipelines (e.g., multi-team systems requiring strict action logging, offline sync, or infinite undo/redo).

Feature

React Context / Parent State

Zustand / Jotai / Redux

State Location

Inside React Component State

Outside React Component Tree

Parent Component

Re-renders on every update

Does not re-render on updates

Render Cascade

Starts at the top and goes down

Never starts

Need React.memo?

Yes, required on parents to block selection updates

No, not needed for selection updates

Updates

Propagates down from Provider

Directly updates subscribing nodes

Render Efficiency

O(N) renders by default (unless heavily optimized with memo)

O(1) renders out-of-the-box (only subscribing components re-render)

Business Logic Location

Coupled inside React hooks and UI components

Decoupled into pure JS/TS store files

Testing Complexity

High; requires mounting React component trees & DOM simulators

Low; test transitions via simple pure function assertions

State Persistence

Manual; requires custom hooks and handling browser hydration lifecycle

Automatic; comes with built-in persist middleware options

Debugging & Tooling

Limited to standard React DevTools component tree inspection

Rich tools (e.g. Redux DevTools) with time-travel and action logs

Middleware Support

No; requires writing custom abstraction layers

Yes; native pipelines for logging, sync, and async side-

Redux

Redux is a popular library that implements the Flux architecture with some modifications. Redux provides a predictable and manageable way to handle the state of a React application. Redux simplifies the flow of data by unifying the state management in a single, immutable store.

Redux has the following key components:

Data Flow in Redux:

In Redux, how do you decide what belongs in the global store versus staying as local component state?

Default to local state unless you are needed to lift it up.

Put it in Redux if:

Keep it in Local State if:

In Redux Toolkit, how do you model and manage async server state (loading/error/data) without mixing it into unrelated Ul state?

  1. RTK Query (Automatic Isolation)
  1. RTK Query manages server state automatically in a separate, internal cache reducer. It completely removes the need to write manual loaders or error actions.
  2. const { data, isLoading, error } = useGet<apiname>Query();
  1. Redux Saga (Event-driven Lifecycle)
  1. Sagas use ES6 Generator functions (yield) to intercept a trigger action, execute the async side effect via a try/catch block, and dispatch specific success or failure actions back to the reducer.

E.g.

function* fetchDataSaga() {

  try {

    yield put({ type: 'products/fetchLoading' });

    const response = yield call(apiFetch, '/products');

    yield put({ type: 'products/fetchSuccess', payload: response.data });

  } catch (err) {

    yield put({ type: 'products/fetchError', payload: err.message });

  }

}

Reducer

Reducers are a pure function that is used to modify redux store. it takes the previous state and the action being dispatched as arguments and -> returns the new state.

Redux Side Effect Manager

The normal Redux flow is: action dispatched, then some state is changed. However, a Redux app won’t be very useful if the only function is changing state.

Actions like talking to the server, accessing local storage, and recording analytics events require reaching out to the outside world. Anything that occurs outside of the Redux normal flow is considered a Redux side-effect.

Redux Saga

Redux-saga is a Redux side effect manager library that aims to manage application side effects (i.e. asynchronous things like data fetching and impure things like accessing the browser cache) easier to manage, more efficient to execute, easy to test, and better at handling failures.

How can we fetch API data without using the useEffect

  1. useState
  2. Custom useFetch hook
  3. useQuery hook of react-query 3rd party library
  4. HOC

What are the different ways to improve React Applications?

  1. Lazy loading components/routes
  2. Avoid unnecessary re-renders: React memo
  3. Creating Reusable components
  4. Writing modular code
  5. Memoization - useMemo, useCallback
  6. Avoid nesting too many components
  7. Bundle Splitting
  8. Code Obfuscation - code minifying
  9. Debounce/throttle
  10. Perception - use Loader, error
  11. Pagination / Infinite Scrolling / Virtualisation
  12. Optimising Web Vitals
  13. src-set for images, loading images lazily

Jest & React Testing library

Jest and React Testing Library are popular JavaScript testing libraries used in the React ecosystem for writing unit tests.

React Testing Library and Jest are often used together for testing React comps. Jest provides the testing framework and the ability to run tests, while React Testing Library provides utilities for rendering components and interacting with them.

Jest

Jest is a zero-config, all-in-one testing framework for JavaScript. It's widely used for testing JavaScript code, including React applications. Key features of Jest include:

React Testing Library

React Testing Library is a set of utility functions designed to make testing React components more intuitive and user-centric. It encourages writing tests that closely resemble how users interact with your application.

Key features of React Testing Library include:

TypeScript

What is TypeScript?

TypeScript adds syntax on top of JavaScript, allowing developers to add types. TypeScript allows specifying the types of data being passed around within the code, and has the ability to report errors when the types don't match.

Compiling TypeScript: tsc yourFileName.ts -> it compile the TS file into a JS file

Running the JavaScript: node yourFileName.js

Using TypeScript with a Project: For larger projects, it's common to use a tsconfig.json file to configure TypeScript settings. This file specifies compiler options, file globs, and other settings for the project.

Implicit & Explicit Typing

Implicit Typing: TypeScript will "guess" the type, based on the assigned value.

We can disable implicit variable type assignment by enabling the compiler option: noImplicitAny.

We can enable 'undefined' & 'null' types to be accounted for by enabling the compiler option: strictNullChecks

e.g.

let myString = "Hello"; // TypeScript infers the type as string

let myNumber = 42; // TypeScript infers the type as number

function add(x, y) {

return x + y; // TypeScript infers x and y as any

}

Explicit Typing: Explicit typing involves explicitly stating the type of a variable, function parameter, return type, or any other part of your code where you want to be specific about the type.

e.g.

let myString: string = "Hello";

let myNumber: number = 42;

function add(x: number, y: number): number {

return x + y;

}

typeScript Interfaces

Interfaces are used to define the structure of an object.

interface Person {

name: string;

age: number;

 }

let person: Person = { name: "John Doe", age: 30 };

Generics

Generics in TypeScript provide a way to create reusable components and functions that can work with a variety of data types. They allow us to write code without committing to a specific type, making our code more flexible and reusable.

Generics can be assigned default values which apply if no other value is specified or inferred.

e.g.

function echo<T>(arg: T): T {

return arg;

}

let myString: string = echo("Hello, TypeScript!");

let myNumber: number = echo(42);

interface Pair<K, V> {

key: K;

value: V;

}

const pair1: Pair<number, string> = { key: 1, value: "first" };

const pair2: Pair<string, boolean> = { key: "second", value: true };

any & unknown

They are often used in situations where the type of a value is not known at compile time or when interacting with dynamic or external data.

The unknown type is a safer alternative to any. When a value is of type unknown, typeScript expects that we will perform some kind of type-check before we try to use it.

e.g.

let userInput: unknown = "Hello";

// 'unknown' requires a type check before use

if (typeof userInput === "string") {

let myString: string = userInput; // Valid because we checked the type

}

When to use any:

  1. Transitioning from JS
  2. Interacting with Dynamic or Unstructured Data
  3. Working with External Libraries

When to use unknown:

  1. Interacting with Dynamic Data Safely
  2. Library or API design

Never

In TypeScript, the never type represents a value that will never occur or a state that is impossible to reach.

It is fundamentally different from void (which means a function returns nothing useful) and any (which can be anything). never means the execution path finishes or terminates before a value can even be returned.

void: "I will finish, but I have nothing to say."

never: "I will literally never reach the finish line."

E.g.

// This function never returns a value because it crashes the execution

function throwError(message: string): never {

  throw new Error(message);

}

// This function never returns because the loop runs forever

function infiniteLoop(): never {

  while (true) {

    console.log("Running...");

  }

}

Utility Types

Utility types in TypeScript are a set of predefined generic types that provide commonly used functionalities to work with and manipulate types. They can help simplify and improve the readability of complex type definitions. These utility types are built into TypeScript and can be used out of the box.

Tuple

A tuple is a typed array with a predefined length and types for each index.

They allow each element in the array to be of known type and also they can be of different types.

Unlike arrays, which allow elements of the same type, tuples can contain elements of different types at specific indices.

let arr: string[] = [“yo”,”bro”,”nyc”,”pik”];

let tuple: [string, boolean, number] = [“yo bro”, false, 123];

readonly

readonly is used to make a variable immutable. Readonly variable is there simply to be read from and not modified.

e.g.

let readOnlyTuple: readonly [string, number] = ["Hello", 42];

readOnlyTuple[0] = "Hi"; // Error: Cannot assign to '0' because it is a read-only property

Type aliases

Type aliases allow us to create a custom name for a type, making it easier to refer to complex or frequently-used types. Interfaces are similar to type aliases, but they are only for object types.

e.g.

type MyString = string;

type MyNumber = number;

type Coordinate = {

x: number;

y: number;

};

let myString: MyString = "Hello";

let myNumber: MyNumber = 42;

let point: Coordinate = { x: 10, y: 20 };

Union

Union types are used when a value can be of more than a single type.

function printStatusCode(code: string | number) {

  console.log(`My status code is ${code}.`)

}

printStatusCode(404);

printStatusCode('404');

Enums

An enum represents a group of constants (unchangeable variables). It comes in two flavours, string and numeric. By default, enums will initialise the first value to 0 and add 1 to each additional value.

enum CardinalDirections {

  North, // 0

  East, // 1

  South, // 2

  West // 3

}

let currentDirection = CardinalDirections.North; // 0

enum CardinalDirections {

  North = 'North',

  East = "East",

  South = "South",

  West = "West"

}

let currentDirection = CardinalDirections.North; // North

keyof

keyof is a TypeScript keyword that is used to create a union type of all the keys (property names) in a given type. It allows us to extract the keys from an object or interface.

e.g.

interface Person {

name: string;

age: number;

email: string;

}

type PersonKeys = keyof Person; // "name" | "age" | "email"

function getProperty(obj: Person, key: PersonKeys) {

return obj[key];

}

let personName = getProperty(person, "name"); // "John Doe"

let personAge = getProperty(person, "age"); // 30

Modeling Function

Using type (most common, most readable):

type Add = (a: number, b: number) => number;

const add: Add = (a, b) => a + b;

Using interface (call signature on an object):

interface Add {

   (a: number, b: number): number;

}

const add: Add = (a, b) => a + b;

Modeling Union Types

Using type — straightforward:

type Status = "idle" | "loading" | "success" | "error";

Using interface — not possible.

An interface can only declare an object shape.

Declaration Merging

If we declare two interfaces with the exact same name in the same scope, TypeScript automatically merges their properties into a single type.

interface User { name: string; }

interface User { age: number; }

// Resulting type requires both:

const player: User = { name: "Aditya", age: 26 };

Module Augmentation

Module augmentation in TypeScript is a powerful feature that allows you to extend, modify, or add types to existing modules (including third-party libraries) without changing the original source code. It works by using the declare module syntax combined with TypeScript's declaration merging

// Inside your-types.d.ts or any augmented file

import { OriginalClass } from 'third-party-library';

declare module 'third-party-library' {

  interface OriginalClass {

    newMethod(): void;

    extraProperty: string;

  }

}

Definitely Typed

Definitely Typed is a community-driven repository that provides TypeScript type definitions for popular JavaScript libraries and frameworks that were originally written in JS. It enables TS developers to use these libraries in a type-safe manner.

npm install --save-dev @types/<library name>

TypeScript Casting

There are times when working with types where it's necessary to override the type of a variable, such as when incorrect types are provided by a library. Casting is the process of overriding a type.

let x: unknown = 'hello';

console.log((x as string).length);

console.log((<string>x).length)

Static Method of Class

Static methods of a class can be accessed directly without creating an object of that class.

static methods of a class are accessed, like <Class Name>.<static function name>

instead of <Object of that Class>.<static function name>

Access Modifier

Access modifiers are keywords that specify the level of access to class members (properties and methods) and constructors. They control how these members can be accessed from outside the class.

e.g.

class Person {

  public isMale: boolean = true;

  protected age: number; = 25;

  private readonly name: string;

  public constructor(name: string) {

    // name cannot be changed after this initial definition, which has to be either at its declaration or in the constructor.

    this.name = name;

  }

  public getName(): string {

    return this.name;

  }

 public getAge(): number {

    return this.age;

  }

}

const person = new Person("Jane");

console.log(person.isMale); // true

console.log(person.age); // Error, as age is protected and can only be accessed within the class and subclasses

console.log(person.name); // Error, as age is private and can only be accessed within the class

console.log(person.getName());

OOPS

OOP (Object-Oriented Programming) is a way of organizing code by bundling related DATA (state) and BEHAVIOR (functions that operate on that data) into single units called OBJECTS.

Instead of having scattered functions that work on loose data:

      let name = "Fido"; let age = 3;

      function bark(name) { ... }

 We package them together:

      class Dog { name; age; bark() {...} }

Class: Class is a template/blueprint for creating objects

Object: Object is a instance of a class. A concrete thing created using the blueprint with its own data

4 Pillar of Oops

Prototypal vs classical inheritance

Prototypal Inheritance:

Classical Inheritance: below stuffs

Inheritance: implements

Interfaces can be used to define the type a class must follow through the implements keyword.

interface Shape {

  getArea: () => number;

}

class Rectangle implements Shape {

  public constructor(protected readonly width: number, protected readonly height: number) {}

  public getArea(): number {

    return this.width * this.height;

  }

}

const myRect = new Rectangle(10, 20);

console.log(myRect.getArea()); // 200

Inheritance: extends

Classes can extend each other through the extends keyword. A class can only extend one other class.

interface Shape {

  getArea: () => number;

}

     

class Rectangle implements Shape {

  public constructor(protected readonly width: number, protected readonly height: number) {}

  public getArea(): number {

    return this.width * this.height;

  }

}

     

class Square extends Rectangle {

  public constructor(width: number) {

    super(width, width);

  }

  // getArea gets inherited from Rectangle

}

const mySq = new Square(20);

console.log(mySq.getArea()); // 400

Override

When a class extends another class, it can replace the members of the parent class with the same name.

Newer versions of TypeScript allow explicitly marking this with the override keyword.

Abstract Classes

Abstract classes serve as a template or blueprint that defines a common structure and behaviour for a group of related classes

Classes can be written in a way that allows them to be used as a base class for other classes without having to implement all the members. This is done by using the abstract keyword.

Abstract classes can contain abstract methods, which are methods that are declared but not implemented in the abstract class. Subclasses that inherit from an abstract class must provide concrete implementations for the abstract methods.

e.g.

abstract class Polygon {

  public abstract getArea(): number;

  public toString(): string {

    return `Polygon[area=${this.getArea()}]`;

  }

}

class Rectangle extends Polygon {

  public constructor(protected readonly width: number, protected readonly height: number) {

    super();

  }

  public getArea(): number {

    return this.width * this.height;

  }

}

const myRect = new Rectangle(10,20);

console.log(myRect.getArea());

TypeScript Decorator

TypeScript decorators are a way to modify or enhance the behaviour of a class or its members (properties or methods).

Here's a breakdown of how decorators work in TypeScript:

  1. Declaration: A decorator is essentially a function that is prefixed with an @ symbol and placed immediately before the declaration of a class, or its members.
  2. Metadata Injection: Decorators are called at runtime with information about the decorated declaration. They have the ability to observe, modify, or replace the decorated declaration.

Decorators are like stickers we can put on different parts of our code, like classes or its members. These stickers give instructions to our code on how to do special things with those parts.

For example, if you have a recipe book, you might put a sticker on a recipe that says "make it spicy." Then, whenever you use that recipe, you know to add some extra spice.

In coding, decorators are like those stickers. They help customise how our code works, making it do special things in certain situations. They're like little notes that tell our code to behave in specific ways.

CSS

Design Tokens

Design Tokens are the absolute smallest building blocks of a design system. They are agnostic, single-value variables - like hex codes for color, pixel values for spacing, or font names for typography. They are stored in a centralized format.

e.g. ref: design-system of machineCoding repo

token.css

:root {

   /* PRIMITIVES TOKENS */

   --color-off: antiquewhite;

   --color-red: red;

   --color-green: green;

}

[data-theme="dark"] {

   /* PRIMITIVES TOKENS for dark theme */

   --color-off: darkslategrey;

}

:root,

[data-theme="dark"] {

   /* SEMANTIC TOKENS */

   --bg-color-main: var(--color-off);

   /* COMPONENT TOKENS */

   --body-bg-main: var(--bg-color-main);

}

style.css

body {

background-color: var(--body-bg-main);

}

Responsive vs Adaptive Design

Responsive design is making a page automatically adjust the layout and content of a web app based on the screen size and device characteristics (using flexible grids and layouts, along with media queries in CSS) while Adaptive design involves creating multiple versions of a website or application, each optimised for specific device types or screen sizes.

CSS Position: absolute vs fixed vs relative

  1. absolute: An element with position: absolute is positioned relative to its nearest non-static ancestor (an ancestor that is not position: static, i.e it can be relative, absolute, fixed). If no positioned ancestor is found, the element is positioned relative to the initial containing block (usually the viewport).
  2. fixed: An element with position: fixed is positioned relative to the viewport (the browser window) and stays in the same place even when the page is scrolled
  3. relative: It positions an element relative to its normal position on the web page. When we use position: relative; we can then use the top, right, bottom, and left properties to offset the element from its normal position.

CSS Padding vs Margin

css margin can be negative, but padding can’t be negative

CSS letter-spacing vs word-spacing

word-spacing is used to configure the amount of space between words, while letter-spacing is for letters.

CSS box-sizing

box-sizing is a CSS property that defines how the total width and height of an element are calculated. It determines whether the specified width and height include padding and borders or not.

The property can take two values: content-box (the default) and border-box.

Border-box: In the border-box model, the specified width and height include padding and border. This is particularly useful when we want to set a specific width or height for an element and include padding and border within that space.

Content-box: In the default content-box model, the specified width and height of an element do not include the padding and border. They only apply to the content area.

ch unit

The ch unit stands for "character". It represents the width of the zero ('0') character of the current font

CSS clamp()

clamp(min, val, max)  e.g. clamp(16px, calc(100vw / 12.5), 32px)

clamp() for font sizes, allows us to set a font-size that grows with the size of the viewport, but doesn't go below a minimum font-size or above a maximum font-size.

CSS Pseudo Classes

A pseudo-class is used to define a special state of an element.

For example, it can be used to:

e.g. :hover, :visited, :focus, :first-child, :nth-child(n)

CSS Pseudo Elements

A CSS pseudo-element is used to style specified parts of an element.

For example, it can be used to:

e.g. ::after, ::before, ::first-letter, ::first-line, ::marker,  ::selection

CSS Symbols

diff b/w display: none; visibility: hidden’ and opacity: 0 in terms of layout, interactivity, and accessibility?

Property

Takes up Layout Space?

Clickable / Interactive

Read by Screen Readers?

Animatable?

display: none

❌ No

❌ No

❌ No (Ignored)

❌ No

visibility: hidden

Yes

❌ No

❌ No (Ignored)

Yes (Step-transition)

opacity: 0

Yes

Yes

Yes (Read out loud)

Yes (Smooth fade)

CSS Box Model

CSS Flexbox vs Grid

Grid

  1. Grid is used to create structured layouts in web pages.
  2. It divides the web pages into rows and columns.
  3. It is used to handle 2 Dimensional layouts in HTML, while flexbox can handle only 1D.
  4. It takes a basis on layout, i.e. it does not get affected by content.

Flexbox

  1. Flexbox is made for one-dimensional(1D) layouts, and the Grid is made for two-dimensional(2D) layouts.
  2. It means flexbox can work on either rows or columns at a time, but Grids can work on both.
  3. Flexbox takes a basis in the content while Grid takes a basis in the layout.

We should consider using grid layout when:

We should consider using flexbox when:

Grid

To have N number of items in a row, and M number of items in a column

.grid {

        display: grid;

grid-template-rows: repeat(N, 1fr);

grid-template-columns: repeat(M, 1fr);

}

1fr stands for one fractional unit. It represents a flexible share of the leftover space inside the grid container after all fixed-size items (like those set in pixels, percentages, or rems) have been accounted for.

Flex-shrink: 0

flex-shrink: 0; // don't let the height/width to go below the defined in any situation

Flex-grow: 0

flex-grow: 0 (default); // don't let the height/width to go below the defined in any situation

CSS Media Queries

/* On screens whose size is up to 800px (800px or less) */

@media screen and (max-width: 800px) { }

CSS Pre-Processor

CSS is primitive and incomplete. Building a function, reusing a definition or inheritance are hard to achieve. For bigger projects, or complex systems, maintenance is a very big problem. This is where CSS Preprocessors come to the rescue.

A CSS Preprocessor is a tool used to extend the basic functionality of default vanilla CSS through its own scripting language. It helps us to use complex logical syntax like – variables, functions, mixins, code nesting, and inheritance, etc. By using CSS Preprocessors, we can seamlessly automate menial tasks, build reusable code snippets, avoid code repetition and bloating and write nested code blocks that are well organised and easy to read.

e.g SCSS (Sassy CSS), LESS (Leaner Style Sheets), Stylus

CSS Mixins

CSS mixins are a way to group together a set of CSS declarations and reuse them throughout the stylesheet. They're similar to functions, allowing us to define a block of styles and then include or "mix in" those styles wherever needed.

e.g.

/* Define a mixin */

@mixin border-radius($radius) {

  -webkit-border-radius: $radius;

  -moz-border-radius: $radius;

  -ms-border-radius: $radius;

  border-radius: $radius;

}

/* Use the mixin */

.button {

  @include border-radius(5px);

  background-color: #3498db;

  color: #ffffff;

  padding: 10px 20px;

  border: none;

  cursor: pointer;

}

.box {

  @include border-radius(10px);

  border: 2px solid #ccc;

  padding: 20px;

}

CSS BEM

The Block, Element, Modifier methodology (BEM) is a popular naming convention for classes in HTML and CSS. Its goal is to help developers better understand the relationship between the HTML and CSS in a given project.

E.g.

/* Block component */

.btn {}

/* Element that depends upon the block */ 

.btn__price {}

/* Modifier that changes the style of the block */

.btn--orange {}

.btn--big {}

CSS @import rule

The @import rule allows us to import a style sheet into another style sheet.

The @import rule must be at the top of the document (but after any @charset declaration).

The @import rule also supports media queries, so we can allow the import to be media-dependent.

e.g.

@import "navigation.css";   /* Using a string */

or

@import url("navigation.css");   /* Using a url */

On responsive design, how do you choose breakpoints and layout strategy so components adapt smoothly across devices without relying on many device-specific media queries?

  1. The Core Strategy: Mobile-First
  1. Write your base CSS for small screens without media queries. Use min-width media queries to layer on complexity only as space increases. It is much easier to scale a simple layout up than to strip a complex desktop layout down.
  1. Choose Content-Driven Breakpoints
  1. Forget specific device sizes. Start with your mobile layout and slowly widen your browser. The exact point where the design looks awkward or lines of text get too long—that is your breakpoint.
  2. Define breakpoints in em or rem so they adapt if a user zooms in or changes their browser's default font size.
  1. Use Intrinsic (Fluid) Layout
  1. Let components size themselves based on their available space instead of hardcoding widths at fixed breakpoints.
  2. CSS Grid auto-fit: Automatically calculates how many columns fit.
  1. grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  1. Flexbox Wrapping: Items sit side-by-side if there is room, but cleanly drop to a new line and expand when space shrinks.
  1. flex-wrap: wrap;
  2. flex: 1 1 300px; /* grow, shrink, ideal basis */
  1. Fluid Typography: Use clamp() to smoothly scale typography between a defined minimum and maximum without media query jumps.
  1. font-size: clamp(1.5rem, 4vw + 1rem, 3rem);
  1. Leverage Container Queries
  1. Instead of asking "How wide is the screen?" (Media Queries), use Container Queries to ask "How much space does my parent element have?" This allows a single component to automatically look like a desktop version when placed in a wide main content area, or a mobile version when dropped into a narrow sidebar—with zero global media queries.

Web Tech Trends

React Server Components (RSC) & Server-First UI

Edge Compute Rendering

Partial Prerendering (PPR)

Performance-First Native Tooling

Micro-Frontends & Isomorphic Frameworks

Astro (Deep Dive Update)

AI Tooling

Agentic AI

Multi-Agent Systems

Mixture of Experts (MoE)

Advanced Multimodal Interfaces

Next-Gen AI Workspace Assistants

ES2022 (EcmaScript 2022)

  1. Private Class Fields: variable/function starts with # e.g #name = “aditya”
  2. Static Class Fields
  3. Top Level await: await can be used directly without having inside a async function
  4. Array.at() function: can be use with negative index to access element from back of the array
  5. Object.hasOwn() Function
  6. RegExp Match Indices

LightWight Frameworks

e.g. svelte, solid.js, Remix, Astro, qwik

Remix: A JS framework

almost 0KB size, very less javascript

Astro: A JS framework

Component Islands: A new web architecture for building faster websites in which component js is fetched individually from the network when needed. Components only hydrate when they scroll into view. If you don't see it, Astro won't load it.

Zero JS, by default: No JavaScript runtime overhead to slow you down.

UI-agnostic: Supports React, Preact, Svelte, Vue, Solid, Lit and more.

qwik: A JS Framework

syntax similar to react, No hydration, auto lazy-loading

javascript get loaded when user interact with the UI

preact signal

What makes Signals unique is that state changes automatically update components and UI in the most efficient way possible

Headless UI(from tailwind css)

When we use UI components from any library like materialUI, or any other, it comes with its own look and feel and do not match with our project’s theme, then we need to write our css overrides to make it feel as per our project’s theme.

Headless UI is a set of completely un-styled, fully accessible UI components for React and Vue

Tauri

Electron.js

Satori

Vercel rolled out an awesome tool: Satori, an engine which converts your HTML to SVG!

Workerd

new Javascript Runtime(like node.js, deno) developed by cloudflare

Web Assembly (wasm)

Build Application for Web in languages other than JS.

DSA

DSA Patterns

Topic

Difficulty to Learn

Return on Investment

Two Pointers

Easy

High

Sliding Window

Easy

High

Breadth-First Search

Easy

High

Depth-First Search

Medium

High

Backtracking

High

High

Heap

Medium

Medium

Binary Search

Easy

Medium

Dynamic Programming

High

Medium

Divide and Conquer

Medium

Low

Trie

Medium

Low

Union Find

Medium

Low

Greedy

High

Low

  1. Sliding Window - used to analyze specific sub-section of a Array / String
  1. window
  2. sub-array / substring / sub-sequence (meet some condition like max, min, target)
  3. METHODS:
  1. Expands or contracts the window to meet specific conditions
  1. Two Pointers  - used to efficiently analyze specific segments of a Array / String
  1. Palindrome / Pair / Reverse
  2. METHODS:
  1. Same direction: used for scanning data in a single pass (e.g., fast and slow pointers to detect cycles or find middle elements).
  2. Opposite directions: used for finding pairs (e.g., sum of two numbers in a sorted array).
  1. Binary Search
  1. sorted stuffs (meet some condition like find, divide)
  1. BFS/DFS
  1. almost all graph (including tree) can be solved using them
  2. DFS: Dives deep into one path before exploring others
  3. BFS: Explores nodes level by level
  1. Priority Queue (Heap)
  1. kth largest / smallest / frequent / closest element
  2. top n largest / smallest / frequent / closest elements
  3. select something based on some priority
  1. Backtracking - extension of DFS - used to explore all possible paths
  1. go into depth looking for best optimized solution if the current is not optimized then go back and check at that point
  2. Builds the solution dynamically by making decisions and backtracking on invalid paths
  1. Dynamic Programming
  1. where ever recursion is used -> it can be optimized using DP
  2. Optimizes solutions by breaking problems into overlapping subproblems - solution of overlapping problems can be saved/memoized by pre-computing
  3. METHODS
  1. Top-down: recursive with memoization to store results.
  2. Bottom-up: solves smaller subproblems iteratively using a table.
  1. Greedy Algo
  1. pick best option at the point and move to next sub-problem
  2. min cost
  3. shortest path
  1. Divide & Conquer
  1. Divide problem in to non-overlapping sub-problems

Binary Search

  1. Implementation
  2. Closest To Target in Sorted Array: https://www.geeksforgeeks.org/problems/find-the-closest-number5513/1
  3. First Bad Version: https://leetcode.com/problems/first-bad-version/description/
  4. Peak Finder: https://leetcode.com/problems/find-peak-element/
  5. Search for a Range: https://leetcode.com/explore/interview/card/top-interview-questions-medium/110/sorting-and-searching/802/
  6. Search in Matrix: https://leetcode.com/problems/search-a-2d-matrix/description/
  7. Search in Rotated Sorted Array: https://leetcode.com/problems/search-in-rotated-sorted-array/description/
  8. Eating Banana: https://leetcode.com/problems/koko-eating-bananas/description/

Solution: https://github.com/adityasuman2025/CP/tree/master/JS/binarySearch

Sorting Algos

  1. Bubble Sort
  2. Selection Sort
  3. Merge Sort
  4. Quick Sort
  5. Insertion Sort
  6. Heap Sort
  7. Merge 2 Sorted Array: https://leetcode.com/problems/merge-sorted-array/description/
  8. Merge N Sorted Array: https://bigfrontend.dev/problem/merge-sorted-arrays
  9. Top K Frequent Elements: https://leetcode.com/problems/top-k-frequent-elements/description/

Solution: https://github.com/adityasuman2025/CP/tree/master/JS/sorting

Array

  1. Set Matrix Zeros: https://leetcode.com/problems/set-matrix-zeroes/description/
  2. Remove Duplicates from Sorted Array: https://leetcode.com/problems/remove-duplicates-from-sorted-array/description/
  3. Rotate Array: https://leetcode.com/problems/rotate-array/description/
  4. Rotate Matrix: https://leetcode.com/problems/rotate-image/description/
  5. Single Number: https://leetcode.com/problems/single-number/
  6. Find the Duplicate Number: https://leetcode.com/problems/find-the-duplicate-number/
  7. 2 Sum in Unsorted Array: https://leetcode.com/problems/two-sum/description/
  8. 4 Sum: https://leetcode.com/problems/4sum/description
  9. Maximum Sum Subarray: https://leetcode.com/problems/maximum-subarray/
  10. Best Time to Buy Sell Stock 2: https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/
  11. Best Time to Buy Sell Stock 1: https://leetcode.com/problems/best-time-to-buy-and-sell-stock
  12. Next Permutation: https://leetcode.com/problems/next-permutation/description/

Solution: https://github.com/adityasuman2025/CP/tree/master/JS/array

Two Pointers

  1. Move Zeros: https://leetcode.com/problems/move-zeroes/description/
  2. 2 Sum in Sorted Array: https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/
  3. 3 Sum: https://leetcode.com/problems/3sum/description/
  4. K Sum Pairs: https://leetcode.com/problems/max-number-of-k-sum-pairs/description
  5. Reverse Vowels of a String: https://leetcode.com/problems/reverse-vowels-of-a-string/description/
  6. Square of Sorted Array: https://leetcode.com/problems/squares-of-a-sorted-array/
  7. Container With Most Water: https://leetcode.com/problems/container-with-most-water/description/
  8. Trapping Rain Water: https://leetcode.com/problems/trapping-rain-water/

Solution: https://github.com/adityasuman2025/CP/tree/master/JS/twoPointers

Sliding Window

  1. Maximum No of Vowels In Substring: https://leetcode.com/problems/maximum-number-of-vowels-in-a-substring-of-given-length/description/
  2. Maximum Average Subarray: https://leetcode.com/problems/maximum-average-subarray-i/description/
  3. Maximum Consecutive Ones: https://leetcode.com/problems/max-consecutive-ones-iii/description/
  4. Longest SubString Without Repeating Characters: https://leetcode.com/problems/longest-substring-without-repeating-characters/
  5. str.indexOf: https://leetcode.com/problems/find-the-index-of-the-first-occurrence-in-a-string/description/
  6. Minimum Window Substring: https://leetcode.com/problems/minimum-window-substring/description/

Solution: https://github.com/adityasuman2025/CP/tree/master/JS/slidingWindow

Tree

  1. BST (Binary Search Tree) Implementation
  2. Sorted Array to Binary Search Tree: https://leetcode.com/problems/convert-sorted-array-to-binary-search-tree/description/
  3. Kth Smallest Element in Binary Search Tree: https://leetcode.com/problems/kth-smallest-element-in-a-bst/description/
  4. Lowest Common Ancestor: https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/description/
  5. Root To Leaf Path Target Sum: https://leetcode.com/problems/path-sum/description/
  6. Count Good Node: https://leetcode.com/problems/count-good-nodes-in-binary-tree/description
  7. Binary Tree Maximum Path Sum: https://leetcode.com/problems/binary-tree-maximum-path-sum/description/
  8. Longest Zig Zag Path in Binary Tree: https://leetcode.com/problems/longest-zigzag-path-in-a-binary-tree/description/
  9. Count Path Sum in Binary Tree: https://leetcode.com/problems/path-sum-iii/description/
  10. Dirty Tree

Solution: https://github.com/adityasuman2025/CP/tree/master/JS/tree

Heap

  1. Min Heap Implementation
  2. Priority Queue Implementation
  3. Kth Largest Element in an Array: https://leetcode.com/problems/kth-largest-element-in-an-array/description/
  4. Kth Largest Element in Stream: https://leetcode.com/problems/kth-largest-element-in-a-stream/description/
  5. Minimum Rope Cost: https://practice.geeksforgeeks.org/problems/minimum-cost-of-ropes-1587115620/1
  6. 'K' Closest Points to Origin: https://leetcode.com/problems/k-closest-points-to-origin/description/
  7. Task Scheduler: https://leetcode.com/problems/task-scheduler/description/

Solution: https://github.com/adityasuman2025/CP/tree/master/JS/heap

Graph

  1. Implementation
  2. Number of Connected Components in an Undirected Graph: https://neetcode.io/problems/count-connected-components/question
  3. Can Visit All Rooms: https://leetcode.com/problems/keys-and-rooms/
  4. Number of Provinces: https://leetcode.com/problems/number-of-provinces/description/
  5. Topological Sort (Used in Google Sheets for cell dependency resolution): https://www.geeksforgeeks.org/problems/topological-sort/1
  6. Dijkstra Algorithm: https://practice.geeksforgeeks.org/problems/implementing-dijkstra-set-1-adjacency-matrix/1
  7. Nearest Exit From Maze: https://leetcode.com/problems/nearest-exit-from-entrance-in-maze/description/
  8. Biggest Island: https://leetcode.com/problems/max-area-of-island/
  9. Flood Fill: https://leetcode.com/problems/flood-fill
  10. Rotten Oranges: https://leetcode.com/problems/rotting-oranges/
  11. Number of Islands: https://leetcode.com/problems/number-of-islands/

Solution: https://github.com/adityasuman2025/CP/tree/master/JS/graph

Stack

  1. Implementation
  2. Removing Star From String: https://leetcode.com/problems/removing-stars-from-a-string/description/
  3. Valid Parentheses: https://leetcode.com/problems/valid-parentheses/description/
  4. Longest Valid Parentheses: https://leetcode.com/problems/longest-valid-parentheses/
  5. Asteroid Collision: https://leetcode.com/problems/asteroid-collision/description/
  6. Next Greater Element: https://practice.geeksforgeeks.org/problems/next-larger-element-1587115620/1
  7. Next Greater Element 2: https://leetcode.com/problems/next-greater-element-ii/
  8. Maximum Area in Matrix (Maximum Rectangle): https://leetcode.com/problems/maximal-rectangle/
  9. Infix To Postfix: https://leetcode.com/problems/basic-calculator/description/

Solution: https://github.com/adityasuman2025/CP/tree/master/JS/stack

Queue

  1. Implementation

Solution: https://github.com/adityasuman2025/CP/blob/master/JS/Queue.js

Linked List

  1. Implementation
  2. Palindrome linked list: https://leetcode.com/problems/palindrome-linked-list/
  3. Merge sorted linked list: https://leetcode.com/problems/merge-two-sorted-lists/submissions/
  4. Rotate List by k: https://leetcode.com/problems/rotate-list/description/
  5. Reverse in k Group: https://leetcode.com/problems/reverse-nodes-in-k-group/description/
  6. Odd/Even linked list: https://leetcode.com/problems/odd-even-linked-list/description/
  7. Killing in Circular Table: https://leetcode.com/problems/find-the-winner-of-the-circular-game/
  8. Merge N Sorted linked list: https://practice.geeksforgeeks.org/problems/flattening-a-linked-list/1

Solution: https://github.com/adityasuman2025/CP/tree/master/JS/linkedList

Backtracking

  1. Subsets: https://leetcode.com/problems/subsets/description/
  2. Combination Sum 1: https://leetcode.com/problems/combination-sum/
  3. Combination Sum 2: https://leetcode.com/problems/combination-sum-ii/
  4. Combination Sum 3: https://leetcode.com/problems/combination-sum-iii/
  5. Permutations: https://leetcode.com/problems/permutations/description/
  6. Letter Combinations: https://leetcode.com/problems/letter-combinations-of-a-phone-number/description
  7. Generate Valid Parenthesis: https://leetcode.com/problems/generate-parentheses/description/
  8. Word Search: https://leetcode.com/problems/word-search/description/

Solution: https://github.com/adityasuman2025/CP/tree/master/JS/backtracking

Greedy

  1. Fractional Knapsack: https://practice.geeksforgeeks.org/problems/fractional-knapsack-1587115620/1
  2. Activity Selection: https://practice.geeksforgeeks.org/problems/activity-selection-1587115620/1
  3. Coin Change: https://leetcode.com/problems/coin-change-ii/description/,
  4. Job Sequencing https://practice.geeksforgeeks.org/problems/job-sequencing-problem-1587115620/1
  5. Non-overlapping Intervals: https://leetcode.com/problems/non-overlapping-intervals
  6. Meeting Room II: https://neetcode.io/problems/meeting-schedule-ii/question
  7. Lamp Light
  8. Minimum Jumps to Reach End (Jump Game II): https://leetcode.com/problems/jump-game-ii/description
  9. Jump Game 1: https://leetcode.com/problems/jump-game/
  10. Minimum Coin Change: https://leetcode.com/problems/coin-change/description/
  11. Minimum Platform: https://practice.geeksforgeeks.org/problems/minimum-platforms-1587115620/1
  12. Max Guests In Party: https://practice.geeksforgeeks.org/problems/maximum-intervals-overlap5708/1
  13. Minimum Number of Arrows to Burst Balloons: https://leetcode.com/problems/minimum-number-of-arrows-to-burst-balloons

Solution: https://github.com/adityasuman2025/CP/tree/master/JS/greedy

Dynamic Programming (DP)

  1. Min Cost Climbing Stairs: https://leetcode.com/problems/min-cost-climbing-stairs
  2. House Robber: https://leetcode.com/problems/house-robber/description
  3. Longest Increasing Subsequence: https://leetcode.com/problems/longest-increasing-subsequence/description/
  4. Unique Paths: https://leetcode.com/problems/unique-paths/description/
  5. Longest Common SubSequence: https://leetcode.com/problems/longest-common-subsequence/

Solution: https://github.com/adityasuman2025/CP/tree/master/JS/dp

Maths

  1. Maths Formulas
  2. Decimal to Binary & Vice Versa
  3. Math.Pow(x, n): https://leetcode.com/problems/powx-n/description
  4. Math.sqrt(n): https://leetcode.com/problems/sqrtx/description/
  5. Excel Sheet Column Number: https://leetcode.com/problems/excel-sheet-column-number/description/

Solution: https://github.com/adityasuman2025/CP/tree/master/JS/maths

LRU Cache

https://leetcode.com/problems/lru-cache/

Solution: https://github.com/adityasuman2025/CP/blob/master/JS/LRUCache.js

Largest Subarray with 0 Sum

https://practice.geeksforgeeks.org/problems/largest-subarray-with-0-sum/1

Solution: https://github.com/adityasuman2025/CP/blob/master/JS/largestSubArrWith0Sum.js

All SubSequence of a String

Solution: https://github.com/adityasuman2025/CP/blob/master/JS/allSubSeqOfString.js

All SubStrings of a String

Solution https://github.com/adityasuman2025/CP/blob/master/JS/allSubStrOfString.js

Longest Consecutive Sequence

https://leetcode.com/problems/longest-consecutive-sequence/

Solution: https://github.com/adityasuman2025/CP/blob/master/JS/longestConsecSeqInArr.js

Group Anagrams

https://leetcode.com/problems/group-anagrams/description

Solution: https://github.com/adityasuman2025/CP/blob/master/JS/groupAnagrams.js

Overlapping Rectangle

https://leetcode.com/problems/rectangle-overlap/

Solution: https://github.com/adityasuman2025/CP/blob/master/JS/overlappingRect.js

Reverse Array

Solution: https://github.com/adityasuman2025/CP/blob/master/JS/reverseArray.js

3Sum Closest

https://leetcode.com/problems/3sum-closest/

Solution: https://github.com/adityasuman2025/CP/blob/master/JS/threeSumClosest.js

Valid Anagram

https://leetcode.com/problems/valid-anagram/

Solution: https://github.com/adityasuman2025/CP/blob/master/JS/validAnagram.js

Valid Palindrome

https://leetcode.com/problems/valid-palindrome/

Solution: https://github.com/adityasuman2025/CP/blob/master/JS/validPalindrome.js