- 1. Core JavaScript
- 2. Asynchronous JavaScript
- 3. Browser Internals
- 4. HTML
- 5. CSS
- 6. React
- 7. Next.js & Meta-Frameworks
- 8. State Management
- 9. Testing
- 10. Frontend System Design
- 11. Security
- 12. Performance & Optimization
- 13. Build Tools & Ecosystem
- 14. TypeScript
- 15. DSA for Frontend
- 16. Web APIs & Browser Features
- 17. GraphQL
- 18. Progressive Web Apps (PWA)
- 19. Web Performance Budget
- 20. Micro-frontends
- 21. Internationalization (i18n)
- 22. Animation & Motion
- 23. Web Components
- 24. Debugging & Dev Tools
- 25. Design Systems
- 26. Behavioral & System Design Interview Tips
let/const/var- Block scope vs function scope
- Temporal Dead Zone (TDZ)
- Hoisting behavior
- When to use each
constwith objects/arrays (reference immutability)
-
Primitive Types
string,number,boolean,null,undefined,symbol,bigint- Immutability of primitives
- Symbol use cases (unique keys, well-known symbols)
- BigInt for large integers
-
Reference Types
- Objects, Arrays, Functions
- Shallow vs deep copy
- Pass-by-value vs pass-by-reference
-
Equality Operators
==vs===Object.is()differences- Implicit coercion rules
- Truthy/Falsy values
-
Special Values
NaN(Not-a-Number)nullvsundefinedtypeofoperator quirksisNaN()vsNumber.isNaN()
-
Logical operators
- Short-circuit evaluation
- Nullish coalescing (
??) - Optional chaining (
?.) - Logical assignment (
&&=,||=,??=)
-
Spread & Rest
- Array/object spreading
- Rest parameters
- Destructuring with rest
- Stack frames
- Stack overflow
- Recursion limits
- Call stack visualization
-
Phases
- Call stack
- Web APIs / Node APIs
- Callback queue
- Microtask queue
- Rendering
-
Task Queues
- Macrotasks (setTimeout, setInterval, I/O)
- Microtasks (Promises, queueMicrotask, MutationObserver)
- Priority: Microtasks execute before macrotasks
process.nextTick()(Node.js)
-
Common Pitfalls
- Starvation (infinite microtasks)
- Blocking the event loop
- Long-running synchronous operations
- Variable hoisting (var vs let/const)
- Function hoisting (declarations vs expressions)
- Class hoisting
- Temporal Dead Zone with let/const
-
Function Declarations
function foo() {}
-
Function Expressions
const foo = function() {}
-
Arrow Functions
const foo = () => {}
thisbinding differences- No
argumentsobject in arrow functions - Cannot be used as constructors
- Implicit return for single expressions
- No
prototypeproperty
-
Four binding rules
- Default binding (global/undefined in strict mode)
- Implicit binding (method invocation)
- Explicit binding (call/apply/bind)
newbinding (constructor)
-
Precedence: new > explicit > implicit > default
-
Arrow function behavior: Lexical
this
call(thisArg, ...args): Invoke immediatelyapply(thisArg, [args]): Invoke with array of argsbind(thisArg, ...args): Return new function with boundthis
- Transform
f(a, b, c)→f(a)(b)(c) - Partial application
- Use cases: Configuration, composition
- Implementation:
function curry(fn) { return function curried(...args) { if (args.length >= fn.length) { return fn.apply(this, args); } return (...nextArgs) => curried(...args, ...nextArgs); }; }
- Pre-fill some arguments
- Different from currying
- Use cases: Reusable utilities
-
Definition: Function + lexical environment
-
Use cases
- Data privacy
- Function factories
- Callbacks maintaining state
- Memoization
- Module pattern
-
Memory implications
-
Common interview questions
- Loop with closures
- Private variables
- Counter implementations
-
Creation
- Object literals
Object.create()- Constructor functions
- Classes
-
Property access
- Dot notation vs bracket notation
- Dynamic property names
- Computed property names
-
Methods
Object.keys()/Object.values()/Object.entries()Object.assign()(shallow copy)Object.freeze()(immutable, shallow)Object.seal()(prevent add/delete)Object.preventExtensions()Object.hasOwn()vshasOwnProperty()Object.getOwnPropertyDescriptors()
-
Shallow copy
- Spread operator:
{ ...obj } Object.assign()Array.slice()
- Spread operator:
-
Deep copy
structuredClone()(modern, recommended)- JSON.parse/stringify (limitations)
- Recursive manual implementation
- Libraries: lodash
cloneDeep
-
Map
- Key-value pairs (any type as key)
- Maintains insertion order
- Size property
- Methods: set, get, has, delete, clear
-
Set
- Unique values
- Methods: add, has, delete, clear
- Use cases: Deduplication, membership checks
-
WeakMap
- Keys must be objects
- No enumeration
- Garbage collection friendly
- Use case: Private data
-
WeakSet
- Objects only
- No enumeration
- Use case: Tracking object references
-
Objects
const { name, age, city = 'Unknown' } = person; const { name: fullName } = person; // Rename
-
Arrays
const [first, second, ...rest] = array; const [, , third] = array; // Skip
-
Nested destructuring
-
Function parameters
-
Spread (
...)- Arrays:
[...arr1, ...arr2] - Objects:
{ ...obj1, ...obj2 } - Function arguments:
fn(...args)
- Arrays:
-
Rest (
...)- Function parameters:
function fn(...args) {} - Destructuring:
const [first, ...rest] = arr
- Function parameters:
- Purpose: Delay execution until after wait time with no calls
- Use cases: Search input, window resize
- Implementation:
function debounce(fn, delay) { let timeoutId; return function(...args) { clearTimeout(timeoutId); timeoutId = setTimeout(() => fn.apply(this, args), delay); }; }
- Leading vs trailing edge
- Purpose: Execute at most once per time period
- Use cases: Scroll events, button clicks
- Implementation:
function throttle(fn, limit) { let inThrottle; return function(...args) { if (!inThrottle) { fn.apply(this, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; }
- Leading vs trailing edge
-
Must know:
Array.prototype.mapArray.prototype.filterArray.prototype.reduceFunction.prototype.bindPromisePromise.allPromise.allSettledArray.prototype.flatObject.create
-
Implementation approach
- Check if method exists
- Add to prototype
- Handle edge cases
-
Common causes
- Global variables
- Forgotten timers/intervals
- Event listeners not removed
- Closures holding references
- Detached DOM nodes
- Console.log in production
-
Detection
- Chrome DevTools Memory profiler
- Heap snapshots
- Allocation timeline
-
Prevention
- Clean up event listeners
- Clear timers/intervals
- WeakMap/WeakSet for caching
- Proper component unmounting (React)
- Mark-and-sweep algorithm
- Reference counting
- Generational collection
- When GC runs (unpredictable)
- Performance impact
- Every object has
[[Prototype]] __proto__vsprototypeObject.getPrototypeOf()Object.setPrototypeOf()- Prototype chain lookup
-
Prototypal inheritance
const parent = { greet() { console.log('Hi'); } }; const child = Object.create(parent);
-
Constructor functions
function Person(name) { this.name = name; } Person.prototype.greet = function() {};
-
ES6 Classes
class Person extends Animal { constructor(name) { super(); this.name = name; } }
-
Class features
- Private fields (
#field) - Static methods/properties
- Getters/setters
superkeyword
- Private fields (
- Pending (initial)
- Fulfilled (resolved)
- Rejected (error)
- Settled (fulfilled or rejected)
-
Constructor
new Promise((resolve, reject) => { // async operation })
-
Methods
.then(onFulfilled, onRejected).catch(onRejected).finally(onFinally)
- Return values propagate
- Return promise for async chaining
- Error propagation through chain
- Implicit try-catch in
.then
.catch()at end of chain.then(null, errorHandler)- Uncaught promise rejections
window.addEventListener('unhandledrejection')
async function fetchData() {
try {
const response = await fetch(url);
const data = await response.json();
return data;
} catch (error) {
console.error(error);
}
}asyncfunction always returns Promiseawaitpauses execution- Can only use
awaitinsideasyncfunctions (or top-level modules) - Error handling with try-catch
- Top-level await (ES2022)
- Cleaner syntax
- Better error handling
- Easier debugging
- Sequential vs parallel execution
- Waits for all to resolve
- Fails fast (rejects if any rejects)
- Returns array of results (same order)
- Use case: Parallel independent requests
- Waits for all to settle (resolve or reject)
- Never rejects
- Returns array of
{status, value/reason} - Use case: When you need all results regardless
- Resolves when first promise resolves
- Rejects only if all reject (AggregateError)
- Use case: Racing multiple sources, first success wins
- Settles with first promise that settles
- Can resolve or reject
- Use case: Timeout implementations
async function retry(fn, retries = 3, delay = 1000) {
for (let i = 0; i < retries; i++) {
try {
return await fn();
} catch (error) {
if (i === retries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}-
AbortController
const controller = new AbortController(); fetch(url, { signal: controller.signal }); controller.abort();
-
Promise cancellation patterns
-
Race with timeout
- Sequential execution of async tasks
- Rate limiting
- Concurrency control
- Implementation with promises
- Limit concurrent operations
- Promise pool pattern
p-limitlibrary approach
- Synchronous error handling
- Works with async/await
- Catch specific error types
.catch()method- Error propagation
- Global handlers
- Always handle rejections
- Specific error types
- Logging and monitoring
- User-friendly messages
- Retry strategies
- HTML Parsing → DOM Tree
- CSS Parsing → CSSOM Tree
- Render Tree = DOM + CSSOM
- Layout (Reflow) - Calculate positions
- Paint - Fill pixels
- Composite - Layer composition
- Incremental parsing
- Tokenization
- Tree construction
- Script blocking
- CSS parsing
- Cascade resolution
- Specificity calculation
- Inheritance
- Only visible nodes
- Excludes
display: none - Combines DOM and styles
- Pseudo-elements included
-
Triggers
- Geometry changes (width, height, position)
- Font changes
- Content changes
- Window resize
- DOM manipulation
-
Performance impact
- Expensive operation
- Affects descendants
- Batch DOM updates
- Use
transformover layout properties
-
Triggers
- Color changes
- Background changes
- Visibility changes
- Shadows, borders
-
Paint order
- Background
- Borders
- Content
- Outlines
-
Layers
- Promoted elements (will-change, transform, opacity)
- GPU acceleration
- Layer creation cost
-
Compositing triggers
transformopacityfilterwill-change
- Reflow: Geometry changes (expensive)
- Repaint: Visual changes (less expensive)
- Neither: Compositing only (cheapest)
- Use
transformandopacityfor animations - Batch DOM reads and writes
requestAnimationFramefor animations- Debounce scroll/resize handlers
- Virtual scrolling for large lists
- Code splitting
- Lazy loading
- Tree shaking
- Minimize critical resources
- Minimize critical bytes
- Minimize critical path length
- Defer non-critical CSS
- Inline critical CSS
- Preload key resources
<link rel="preconnect"><link rel="dns-prefetch"><link rel="preload"><link rel="prefetch"><link rel="modulepreload">
-
Use cases
- Lazy loading images
- Infinite scroll
- Ad viewability tracking
- Animations on scroll
-
API
const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { // Element is visible } }); }, { threshold: 0.5, rootMargin: '0px' }); observer.observe(element);
- Monitor element size changes
- Better than window resize
- Use cases: Responsive components, charts
- Watch DOM changes
- Configuration: attributes, childList, subtree
- Performance considerations
- Monitor performance entries
- Types: navigation, resource, measure, paint
- Core Web Vitals monitoring
- Purpose: Run JavaScript in background thread
- Types: Dedicated, Shared
- Communication: postMessage / onmessage
- Limitations: No DOM access
- Use cases: Heavy computation, data processing
-
Purpose: Network proxy, offline functionality
-
Lifecycle: install → activate → fetch
-
Capabilities
- Cache API
- Background sync
- Push notifications
- Offline support
-
Scope: Based on registration path
-
Update mechanism
- Synchronous
- 5-10MB limit
- String-only storage
- Same-origin policy
- Persists across sessions
- Similar to localStorage
- Cleared when tab closes
- Per-tab isolation
- Asynchronous
- Large storage (50MB+)
- NoSQL database
- Structured data
- Transactions and indexes
- Service Worker cache
- Request/Response pairs
- Cache strategies
- 4KB limit per cookie
- Sent with every request
- Domain/path scoping
- Expiration options
- Secure/HttpOnly flags
- Performance metrics
- DNS, TCP, SSL times
- Request/response times
- DOM processing times
- Individual resource metrics
- Size, duration, caching
- States: active, passive, hidden, frozen, terminated
- Events: visibilitychange, freeze, resume
- Battery/memory optimization
<header>,<nav>,<main>,<aside>,<footer><article>,<section><figure>,<figcaption><details>,<summary><time>,<address>
- Accessibility
- SEO
- Maintainability
- Screen reader support
- Div soup
- Incorrect nesting
- Missing landmarks
- Improper heading hierarchy
- Landmark roles: navigation, main, complementary, contentinfo
- Widget roles: button, checkbox, tab, dialog
- Document roles: article, document, note
- Live regions: alert, status, log
aria-label,aria-labelledbyaria-describedbyaria-hiddenaria-live,aria-atomicaria-expanded,aria-selectedaria-disabled,aria-readonly
- Focus management
- Tab order
- Skip links
- Focus trap in modals
- Keyboard shortcuts
tabindexusage
- Alt text for images
- Form labels
- Button text
- Heading structure
- Link text
- Error messages
- Visible focus indicators
- Skip to content links
- Focus restoration
- Auto-focus considerations
- Input types: text, email, number, tel, url, date, color, file
<select>,<textarea><datalist>for autocomplete<fieldset>and<legend>
required,pattern,min,max,minlength,maxlengthtypeattribute validation- Custom validity with
setCustomValidity() :valid,:invalidpseudo-classes
<label>association- Error messages with
aria-describedby aria-invalidattribute- Group related inputs
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="...">
<meta name="keywords" content="...">
<meta name="author" content="...">
<meta name="robots" content="index, follow"><meta property="og:title" content="...">
<meta property="og:description" content="...">
<meta property="og:image" content="...">
<meta property="og:url" content="...">
<meta property="og:type" content="website"><meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="...">
<meta name="twitter:description" content="...">
<meta name="twitter:image" content="...">- Canonical:
<link rel="canonical" href="..."> - Alternate languages:
<link rel="alternate" hreflang="..."> - Icon:
<link rel="icon" href="..."> - Preconnect/prefetch (covered earlier)
- 2D drawing context
- Animations
- Image manipulation
- Chart libraries
draggableattribute- Events: dragstart, drag, dragend, dragover, drop
- dataTransfer object
pushState(),replaceState()popstateevent- SPA navigation
navigator.geolocation.getCurrentPosition()- Watch position
- Privacy concerns
- Content, Padding, Border, Margin
box-sizing: border-boxvscontent-box- Collapsing margins
- Inline vs block vs inline-block
- static (default)
- relative: Relative to normal position
- absolute: Relative to nearest positioned ancestor
- fixed: Relative to viewport
- sticky: Hybrid of relative and fixed
- block, inline, inline-block
- flex, inline-flex
- grid, inline-grid
- none, contents
- table, table-row, table-cell
- Inline styles (1,0,0,0)
- IDs (0,1,0,0)
- Classes, attributes, pseudo-classes (0,0,1,0)
- Elements, pseudo-elements (0,0,0,1)
!importantoverride (avoid)
- Inherited properties: color, font-family, font-size, etc.
- Non-inherited: margin, padding, border, display
inherit,initial,unset,revertkeywords
- Origin: User agent → User → Author
- Importance: normal → !important
- Specificity
- Order of appearance
:root {
--primary-color: #3498db;
--spacing: 1rem;
}
.button {
background: var(--primary-color);
padding: var(--spacing);
}width: calc(100% - 2rem);
font-size: clamp(1rem, 2vw, 2rem);
width: min(90%, 1200px);.card:has(img) {
/* Style card if it contains an image */
}
form:has(:invalid) {
/* Style form with invalid inputs */
}:is(h1, h2, h3) { /* ... */ }
:where(.class1, .class2) { /* ... (0 specificity) */ }
:not(.excluded) { /* ... */ }@container (min-width: 700px) {
.card {
display: grid;
}
}@layer base, components, utilities;
@layer base {
h1 { font-size: 2rem; }
}margin-inline-startinstead ofmargin-leftpadding-blockinstead ofpadding-top + padding-bottom- Better for internationalization
-
Container properties
display: flex | inline-flexflex-direction: row, columnjustify-content: center, space-between, space-around, space-evenlyalign-items: stretch, center, flex-start, flex-endflex-wrap: nowrap, wrapgap: spacing between items
-
Item properties
flex-grow,flex-shrink,flex-basisflexshorthandalign-selforder
-
Use cases: Navigation, button groups, simple layouts
-
Container properties
display: grid | inline-gridgrid-template-columns,grid-template-rowsgrid-template-areasgap,column-gap,row-gapjustify-items,align-itemsjustify-content,align-content
-
Item properties
grid-column,grid-rowgrid-areajustify-self,align-self
-
Functions
repeat(),minmax(),auto-fit,auto-fillfrunit
-
Use cases: Page layouts, complex grids, dashboards
column-count,column-widthcolumn-gap,column-rule- Use case: Newspaper-style layouts
/* Base styles for mobile */
.container { padding: 1rem; }
/* Tablet and up */
@media (min-width: 768px) {
.container { padding: 2rem; }
}
/* Desktop and up */
@media (min-width: 1024px) {
.container { padding: 3rem; }
}- Common: 640px (sm), 768px (md), 1024px (lg), 1280px (xl)
- Use em/rem for accessibility
%: Relative to parentvw,vh: Viewport width/heightvmin,vmax: Smaller/larger of vw or vhrem: Root element font sizeem: Parent element font sizech: Width of "0" character
srcsetandsizesattributes<picture>element- Art direction with multiple sources
-
BEM (Block Element Modifier)
.card { } .card__title { } .card--featured { }
-
OOCSS (Object-Oriented CSS)
-
SMACSS (Scalable and Modular Architecture)
-
Atomic CSS / Utility-first (Tailwind)
- Component-based
- Utility-first
- CSS Modules
- CSS-in-JS
- File structure
- Import order
- Specificity management
- Avoiding conflicts
-
CSS advantages
- GPU acceleration
- Off main thread
- Better performance
-
Use CSS for: opacity, transform
-
Avoid animating: width, height, margin, padding
.animated {
will-change: transform, opacity;
}- Creates new layer
- Use sparingly (memory cost)
- Remove after animation
transformandopacityare GPU-accelerated- Use
transform: translateZ(0)to force layer - Monitor layer creation (DevTools)
- Inline above-the-fold styles
- Defer non-critical CSS
- Use tools: critical, critters
:hover,:focus,:active:nth-child(),:nth-of-type():first-child,:last-child:not(),:is(),:where():has()(parent selector):focus-visible,:focus-within:checked,:disabled,:invalid
::before,::after::first-line,::first-letter::placeholder::selection::marker(list markers)
- JavaScript XML syntax
- Expressions in curly braces
- Self-closing tags
- className vs class
- Style as object
- Comments:
{/* */}
- Function components (modern approach)
- Class components (legacy)
- PascalCase naming
- Props as function arguments
- Return JSX (or null, arrays, fragments)
- Props: Read-only data from parent
- State: Component's own mutable data
- Props flow down (unidirectional)
- State triggers re-renders
- Controlled: Form values controlled by React state
- Uncontrolled: Use refs to access DOM values
- Controlled preferred for form validation
- Share state between components
- Move state to closest common ancestor
- Pass state and updaters as props
childrenprop- Render props pattern
- Higher-Order Components (HOC)
- Compound components
const [state, setState] = useState(initialValue);- State updates are batched
- Functional updates:
setState(prev => prev + 1) - Lazy initialization:
useState(() => expensiveComputation())
useEffect(() => {
// Side effect
return () => {
// Cleanup
};
}, [dependencies]);- Runs after render
- Cleanup function
- Dependency array
- Common uses: data fetching, subscriptions, timers
const ref = useRef(initialValue);- Mutable object that persists across renders
.currentproperty- Use cases: DOM access, storing mutable values
- Doesn't trigger re-render on change
const memoizedValue = useMemo(() => expensiveCalculation(), [deps]);- Memoize expensive computations
- Only recalculates when dependencies change
- Don't overuse (has overhead)
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);- Memoize function reference
- Useful for passing callbacks to optimized children
- Prevents unnecessary re-renders
const value = useContext(MyContext);- Access context without nesting
- Alternative to prop drilling
- Re-renders when context value changes
const [state, dispatch] = useReducer(reducer, initialState);- Alternative to useState for complex state
- Similar to Redux pattern
- Good for state with multiple sub-values
- Similar to useEffect
- Runs synchronously after DOM mutations
- Blocks visual updates
- Use for DOM measurements
- Customize ref value exposed to parent
- Used with
forwardRef - Rarely needed
- Generate unique IDs for accessibility
- SSR-safe
- Use for form labels, aria attributes
- Extract reusable logic
- Must start with "use"
- Can use other hooks
- Examples: useLocalStorage, useFetch, useDebounce
- Process of updating DOM
- Virtual DOM diffing
- Key prop importance
- Same-level comparison
- Reconciliation engine (React 16+)
- Incremental rendering
- Pause/resume/abort work
- Priority scheduling
- Lightweight representation of real DOM
- Diff algorithm
- Batching updates
- Performance benefits
- React 18: Automatic batching everywhere
- React 17: Only in event handlers
flushSync()to opt-out
- Double-invokes effects in dev
- Highlights potential problems
- Deprecation warnings
- Does not affect production
const MemoizedComponent = React.memo(Component, arePropsEqual);- Prevents re-renders if props unchanged
- Shallow comparison by default
- Custom comparison function
- Memoize values and functions
- Reduce unnecessary calculations
- Optimize child component re-renders
React.lazy(() => import('./Component'))- Dynamic imports
- Route-based splitting
- Component-based splitting
<Suspense fallback={<Loading />}>
<LazyComponent />
</Suspense>- Handle loading states
- Works with React.lazy
- Future: data fetching
- Stable identity for elements
- Avoid using index as key
- Use unique IDs
- Improves reconciliation performance
- Move state down
- Split large components
- Use composition (children prop)
- Memoization
- Proper key usage
- Measure component render performance
<Profiler id="..." onRender={callback}>- Chrome DevTools Profiler
<DataProvider render={data => <Display data={data} />} />const EnhancedComponent = withFeature(Component);- Multiple components work together
- Share implicit state
- Example: Tabs component
- Allow parent to control component state
- Both controlled and uncontrolled modes
- Give users control over state updates
- Pass custom reducer
ReactDOM.createPortal(child, container);- Render outside parent DOM hierarchy
- Use cases: Modals, tooltips
class ErrorBoundary extends React.Component {
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
logErrorToService(error, errorInfo);
}
render() {
if (this.state.hasError) {
return <FallbackUI />;
}
return this.props.children;
}
}- Catch errors in child components
- Only class components can be error boundaries
- Don't catch: event handlers, async code, SSR, errors in boundary itself
- Place at route level
- Granular boundaries for critical sections
- Logging integration
- Reset functionality
import { startTransition } from 'react';
startTransition(() => {
setState(newState); // Mark as non-urgent
});const [isPending, startTransition] = useTransition();const deferredValue = useDeferredValue(value);- Defer updating non-urgent parts
- Useful for debouncing expensive renders
- Non-blocking rendering
- Prioritize urgent updates
- Improves perceived performance
- Server Components: Run on server, no client JS
- Client Components: Traditional React components
'use client'directive
- Reduced bundle size
- Direct database/API access
- Better security (secrets stay on server)
- Automatic code splitting
- Send HTML in chunks
- Progressive hydration
- Faster time-to-interactive
pages/directory- File-based routing
getServerSideProps,getStaticProps- Client-side routing with
next/link
app/directory- Nested layouts
- Server Components by default
- Streaming and Suspense
- Better developer experience
- HTML generated per request
- Fresh data
- Slower TTFB
- Good for: Dynamic content, personalization
- HTML generated at build time
- Fast delivery via CDN
- Stale data until rebuild
- Good for: Marketing pages, blogs
- Static with periodic updates
- Revalidate at intervals or on-demand
- Best of SSG and SSR
- Traditional SPA behavior
- Data fetching on client
- Good for: Dashboards, authenticated pages
- Server Components: async/await directly
- Client Components: use client-side fetching
fetchwith caching options
getServerSideProps: SSRgetStaticProps: SSGgetStaticPaths: Dynamic routesgetInitialProps: Legacy
fetchwithcacheoption- Revalidation strategies
- Route segment config
- Folder = route segment
page.tsx= route UIlayout.tsx= shared UIloading.tsx= loading UIerror.tsx= error UI
[id]= single segment[...slug]= catch-all[[...slug]]= optional catch-all
(folder)= group without affecting URL- Organize routes logically
@folder= parallel route- Load multiple pages in same layout
(.)= intercept same level- Useful for modals
import Image from 'next/image'
<Image src="..." alt="..." width={500} height={300} />- Automatic optimization
- Lazy loading
- Modern formats (WebP, AVIF)
- Responsive images
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] })- Automatic font optimization
- No layout shift
- Self-hosting
import Script from 'next/script'
<Script src="..." strategy="lazyOnload" />- Deferred loading
- Strategy options
- SEO optimization
- Dynamic metadata
- OpenGraph, Twitter cards
export function middleware(request) {
// Run before request is completed
}
export const config = {
matcher: '/about/:path*',
}- Authentication
- Redirects
- Header manipulation
- A/B testing
- Lightweight runtime
- Fast cold starts
- Deploy globally
- Limited Node.js APIs
- Nested routing
- Progressive enhancement
- Form handling
- Error boundaries
- GraphQL data layer
- Plugin ecosystem
- Image optimization
- SSG focus
- Multi-framework support
- Partial hydration
- Islands architecture
- Content-focused
- Component-specific
- useState, useReducer
- Doesn't need sharing
- Examples: Form inputs, toggle states
- Shared across components
- Needs state management solution
- Examples: User auth, theme, cart
- Move state to common ancestor
- Pass as props
- Simple solution for related components
const ThemeContext = createContext(defaultValue);<ThemeContext.Provider value={theme}>
{children}
</ThemeContext.Provider>const theme = useContext(ThemeContext);- Performance: All consumers re-render on value change
- Solution: Split contexts, memoize values
- Anti-pattern: Using for all state
const value = useMemo(() => ({
state,
dispatch
}), [state]);import { configureStore } from '@reduxjs/toolkit'
export const store = configureStore({
reducer: {
counter: counterReducer,
},
})import { createSlice } from '@reduxjs/toolkit'
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: state => { state.value += 1 },
decrement: state => { state.value -= 1 },
},
})import { createAsyncThunk } from '@reduxjs/toolkit'
const fetchUser = createAsyncThunk(
'users/fetch',
async (userId) => {
const response = await fetch(`/api/user/${userId}`)
return response.json()
}
)useSelector(selector)useDispatch()- Memoized selectors with Reselect
import create from 'zustand'
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}))- Simple API
- No boilerplate
- Minimal re-renders
import { atom, useAtom } from 'jotai'
const countAtom = atom(0)
function Counter() {
const [count, setCount] = useAtom(countAtom)
}- Atomic state
- Bottom-up approach
- TypeScript-first
- Atom-based
- Derived state (selectors)
- Async queries
- Proxy-based
- Mutable API
- Auto-tracking
import { useQuery } from '@tanstack/react-query'
function Users() {
const { data, isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
})
}- Caching
- Background refetching
- Stale data handling
- Pagination
- Infinite queries
- Mutations
- Optimistic updates
import useSWR from 'swr'
function Profile() {
const { data, error, isLoading } = useSWR('/api/user', fetcher)
}useSearchParams(Next.js)URLSearchParamsAPI- Sync state with URL
- Shareable links
- Browser history
- Bookmark-friendly
import { useForm } from 'react-hook-form'
function Form() {
const { register, handleSubmit, formState: { errors } } = useForm()
}- Form state management
- Validation
- Error handling
- Test individual functions/components
- Fast execution
- High coverage
- Mock dependencies
- Test component interactions
- Multiple units together
- More realistic scenarios
- Test entire application flow
- User perspective
- Slower, more brittle
- Critical paths only
- Detect UI changes
- Screenshot comparison
- Prevent visual bugs
describe('sum', () => {
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
});- Test runner
- Assertion library
- Mocking utilities
- Code coverage
jest.mock('./api')
jest.fn()
jest.spyOn(object, 'method')import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
test('button click', async () => {
render(<Button />)
const button = screen.getByRole('button')
await userEvent.click(button)
expect(screen.getByText('Clicked')).toBeInTheDocument()
})- Test behavior, not implementation
- Query by accessibility
- Avoid testing state directly
- User-centric testing
getByRole(preferred)getByLabelTextgetByTextgetByTestId(last resort)findByfor asyncqueryByfor non-existence
userEvent.click()userEvent.type()userEvent.keyboard()- Better than
fireEvent
import { test, expect } from '@playwright/test'
test('login flow', async ({ page }) => {
await page.goto('https://example.com')
await page.fill('input[name="email"]', 'user@example.com')
await page.click('button[type="submit"]')
await expect(page).toHaveURL(/dashboard/)
})describe('Login', () => {
it('should login successfully', () => {
cy.visit('/login')
cy.get('input[name="email"]').type('user@example.com')
cy.get('button').click()
cy.url().should('include', '/dashboard')
})
})- Independent tests
- Data seeding
- Cleanup between tests
- Parallel execution
- Record videos on failure
- Many unit tests (fast, cheap)
- Some integration tests
- Few E2E tests (slow, expensive)
- MSW (Mock Service Worker)
- Intercept network requests
- Realistic responses
- Capture component output
- Detect unintended changes
- Pros: Quick to write
- Cons: Can be brittle, large diffs
- Percy
- Chromatic
- Applitools
- Capture baseline screenshots
- Compare on changes
- Review and approve differences
- Automated performance testing
- CI/CD integration
- Budget enforcement
- LCP, FID, CLS
- Bundle size
- Time to Interactive
- Pagination vs infinite scroll
- Filtering and sorting
- Search functionality
- State management
- Caching strategy
- Performance optimization
- IntersectionObserver
- Pagination API
- Loading states
- Error handling
- Memory management
- Skeleton screens
- Input handling
- Debounce implementation
- API requests
- Loading/error states
- Result caching
- Autocomplete
- Service Worker
- Cache strategies
- Background sync
- IndexedDB
- Conflict resolution
- Online/offline detection
- JWT storage
- Refresh tokens
- Token expiration
- Secure storage
- CSRF protection
- Session management
- Module Federation
- Shared dependencies
- Communication between apps
- Deployment strategy
- Versioning
- Atomic design
- Container/Presentational
- Composition patterns
- Props drilling solutions
- Feature-based
- Layer-based
- Atomic structure
- Colocation
- Centralized API client
- Error handling
- Interceptors
- Request/response transformation
- HTTP Cache-Control headers
- ETag/If-None-Match
- Service Worker caching
- In-memory cache
- React Query / SWR
- Cache invalidation strategies
- Stale-while-revalidate
- Static asset caching
- Edge caching
- Cache purging
- Offset-based
- Cursor-based
- Pros/cons of each
- UI considerations
- Virtual scrolling
- Windowing (react-window, react-virtualized)
- Load more trigger
- Performance optimization
- WebSockets
- Server-Sent Events (SSE)
- Polling
- Long polling
- Immediate UI feedback
- Rollback on failure
- Conflict resolution
- Bidirectional communication
- Connection management
- Reconnection logic
- Message queuing
- Unidirectional (server to client)
- Auto-reconnection
- Event types
- Use cases
- Short polling vs long polling
- Trade-offs
- When to use
- Route-based
- Component-based
- Lazy loading
- Dynamic imports
- Tree shaking
- Code splitting
- Compression
- Minification
- Normalized state
- Selector memoization
- State slicing
- Middleware
- Sentry
- LogRocket
- Error boundaries
- Source maps
- User behavior tracking
- Conversion funnels
- A/B testing
- Feature flags
- Real User Monitoring (RUM)
- Synthetic monitoring
- Core Web Vitals tracking
- Types: Stored, Reflected, DOM-based
- Prevention
- Sanitize user input
- Use textContent instead of innerHTML
- Content Security Policy
- React auto-escapes by default
- DOMPurify for HTML sanitization
- Attack: Unauthorized actions on behalf of user
- Prevention
- CSRF tokens
- SameSite cookie attribute
- Custom headers
- Double submit cookies
- Attack: Invisible iframe overlay
- Prevention
- X-Frame-Options header
- CSP frame-ancestors directive
- Frame-busting scripts
- Attack: Redirect to malicious site
- Prevention
- Validate redirect URLs
- Whitelist allowed domains
- Avoid user-controlled redirects
- Structure: Header.Payload.Signature
- Storage: Where to store?
- localStorage: Vulnerable to XSS
- sessionStorage: Same as localStorage
- Cookies (HttpOnly, Secure, SameSite): Preferred
- Memory: Lost on refresh
- Long-lived token
- Rotate access tokens
- Store securely
- Revocation mechanism
- Authorization framework
- Flows: Authorization Code, Implicit, Client Credentials
- Third-party login
- Use HTTPS
- Secure cookie flags
- Token expiration
- Logout functionality
- Session management
- Never expose secrets in frontend
- Encrypt in transit (HTTPS)
- Mask sensitive input (passwords)
- Secure form submissions
- Never commit to version control
- Environment variables
- Backend proxy for API calls
- Minimize collection
- Secure storage
- Consent management
- GDPR compliance
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' https://trusted.cdn.com">- Whitelist sources
- Prevent XSS
- Report violations
- Restrict cross-origin requests
- Preflight requests
- Allowed origins configuration
<script src="https://cdn.com/library.js"
integrity="sha384-..."
crossorigin="anonymous"></script>- Client-side (UX)
- Server-side (security)
- Sanitize inputs
- Type checking
- Prevent abuse
- DDoS protection
- Per-user limits
- Encrypt data in transit
- SSL/TLS certificates
- HSTS header
- Target: < 2.5s
- Measures: Render time of largest element
- Optimization
- Optimize images
- Preload resources
- Reduce server response time
- Minimize render-blocking resources
- Target: < 100ms (FID), < 200ms (INP)
- Measures: Input responsiveness
- Optimization
- Reduce JavaScript execution
- Code splitting
- Web Workers for heavy tasks
- Debounce/throttle
- Target: < 0.1
- Measures: Visual stability
- Optimization
- Size attributes on images/videos
- Reserve space for ads
- Avoid inserting content above fold
- Web fonts with font-display
- Minimize critical resources
- Reduce critical bytes
- Optimize critical path length
- Preload:
<link rel="preload"> - Prefetch:
<link rel="prefetch"> - DNS-prefetch:
<link rel="dns-prefetch"> - Preconnect:
<link rel="preconnect">
- Route-based splitting
- Component-based splitting
- Dynamic imports
- Webpack chunks
- Images:
loading="lazy" - Components: React.lazy
- Intersection Observer
- Below-the-fold content
- Remove unused code
- ES6 modules
- Side-effect free code
- Webpack/Rollup optimization
- Avoid long tasks (>50ms)
- Use Web Workers
- Debounce/throttle
- RequestAnimationFrame
- React.memo
- useMemo / useCallback
- Virtual scrolling
- Lazy loading
- Code splitting
- Use CSS transforms
- Avoid layout thrashing
- Batch DOM updates
- will-change property
- Bundle files
- Sprite images
- Inline critical CSS
- HTTP/2 multiplexing
- Gzip / Brotli
- Text compression
- Image compression
- Browser caching
- Service Worker caching
- CDN caching
- Lighthouse
- WebPageTest
- Chrome DevTools Performance
- Real User Monitoring (RUM)
- TTFB (Time to First Byte)
- FCP (First Contentful Paint)
- LCP, FID/INP, CLS
- Time to Interactive (TTI)
- Set limits for metrics
- Fail builds on violations
- Bundle size budgets
- WebP, AVIF (modern)
- JPEG (photos)
- PNG (transparency)
- SVG (icons, logos)
- Responsive images (srcset)
- Lazy loading
- Image CDN
- Compression
- Blur placeholder
- Automatic optimization
- Modern formats
- Lazy loading
- Size optimization
- font-display: swap, block, fallback, optional
- Preload fonts
- Self-host fonts
- Variable fonts
- Flash of Unstyled Text
- Flash of Invisible Text
- font-display: swap to prevent FOIT
- Mature, widely used
- Plugin ecosystem
- Complex configuration
- Module federation
- Fast dev server (esbuild)
- HMR (Hot Module Replacement)
- Rollup for production
- Modern tooling
- ES modules focus
- Tree shaking
- Library bundling
- Simple configuration
- Extremely fast
- Written in Go
- Limited plugin ecosystem
- Good for libraries
- Zero config
- Fast build times
- Built-in optimizations
- Transform modern JS
- Plugin system
- Preset configurations
- JSX support
- Type checking
- JS generation
- Declaration files
- Rust-based
- Much faster than Babel
- Drop-in replacement
- Default Node.js package manager
- Largest registry
- package-lock.json
- Fast, reliable
- Workspaces support
- Plug'n'Play (PnP)
- Efficient disk usage
- Strict node_modules
- Fast installation
- Monorepo support
- Build system
- Code generation
- Dependency graph
- Cache optimization
- High-performance builds
- Remote caching
- Incremental builds
- Simple setup
- Multi-package management
- Versioning
- Publishing
- Bootstrap packages
- npm/yarn/pnpm workspaces
- Share dependencies
- Link packages
- Share code between apps
- Runtime integration
- Independent deployment
- Micro-frontends
- Micro-frontends
- Shared components
- Plugin architectures
- Automated workflows
- Build, test, deploy
- Matrix builds
- .gitlab-ci.yml
- Pipelines
- Runners
- Automation server
- Plugin ecosystem
- Self-hosted
- Automated testing
- Linting
- Build verification
- Deployment pipelines
- Environment management
let name: string = "John";
let age: number = 30;
let isActive: boolean = true;
let items: number[] = [1, 2, 3];
let tuple: [string, number] = ["hello", 10];- TypeScript infers types when possible
- Explicit types when needed
- Contextual typing
- Variable types
- Function parameters
- Return types
let value: string | number;type A = { name: string };
type B = { age: number };
type C = A & B; // { name: string, age: number }let status: "success" | "error" | "pending";type IsString<T> = T extends string ? true : false;type Readonly<T> = {
readonly [P in keyof T]: T[P];
};type EventName<T extends string> = `${T}Changed`;type Person = { name: string; age: number };
type PersonKeys = keyof Person; // "name" | "age"- Makes all properties optional
- Makes all properties required
- Makes all properties readonly
- Pick specific properties
- Omit specific properties
- Object type with specific keys and values
- Exclude types from union
- Extract types from union
- Remove null and undefined
- Get function return type
- Get function parameter types
function identity<T>(arg: T): T {
return arg;
}interface Box<T> {
value: T;
}function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}interface Container<T = string> {
value: T;
}if (typeof x === "string") {
// x is string here
}if (x instanceof Date) {
// x is Date here
}function isString(value: any): value is string {
return typeof value === "string";
}type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; size: number };
function area(shape: Shape) {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.size ** 2;
}
}{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}- strictNullChecks
- strictFunctionTypes
- strictBindCallApply
- strictPropertyInitialization
- noImplicitThis
- noImplicitAny
interface Props {
name: string;
age?: number;
onClick: () => void;
}
const Component: React.FC<Props> = ({ name, age, onClick }) => {
return <div onClick={onClick}>{name}</div>;
};const [state, setState] = useState<string>("");
const ref = useRef<HTMLDivElement>(null);const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
// ...
};- Push, pop, shift, unshift
- Map, filter, reduce
- Slice, splice
- Sort algorithms
- Singly linked list
- Doubly linked list
- Use cases: LRU cache
- LIFO principle
- Operations: push, pop, peek
- Use cases: Undo/redo, expression evaluation
- FIFO principle
- Operations: enqueue, dequeue
- Use cases: Task scheduling, BFS
- O(1) lookup
- Collision handling
- Use cases: Caching, counting
- Binary trees
- Binary search trees
- Use cases: DOM structure, file systems
- Nodes and edges
- Use cases: Network topology, dependencies
- Start and end pointers
- Use cases: Palindrome, two sum in sorted array
- Fixed or dynamic window
- Use cases: Subarray problems, longest substring
- Frequency counting
- Use cases: Two sum, anagrams
- Base case + recursive case
- Use cases: Tree traversal, backtracking
- Memoization (top-down)
- Tabulation (bottom-up)
- Use cases: Fibonacci, coin change
- Recursive clone
- Handle circular references
- structuredClone() API
function flatten(obj, prefix = '') {
let result = {};
for (let key in obj) {
const newKey = prefix ? `${prefix}.${key}` : key;
if (typeof obj[key] === 'object' && obj[key] !== null) {
Object.assign(result, flatten(obj[key], newKey));
} else {
result[newKey] = obj[key];
}
}
return result;
}function debounce(fn, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn.apply(this, args), delay);
};
}function throttle(fn, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
fn.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}class EventEmitter {
constructor() {
this.events = {};
}
on(event, listener) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(listener);
}
emit(event, ...args) {
if (this.events[event]) {
this.events[event].forEach(listener => listener(...args));
}
}
off(event, listenerToRemove) {
if (this.events[event]) {
this.events[event] = this.events[event].filter(
listener => listener !== listenerToRemove
);
}
}
}- Simplified reconciliation
- Tree comparison
- Minimal updates
class LRUCache {
constructor(capacity) {
this.capacity = capacity;
this.cache = new Map();
}
get(key) {
if (!this.cache.has(key)) return -1;
const value = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
put(key, value) {
if (this.cache.has(key)) {
this.cache.delete(key);
}
this.cache.set(key, value);
if (this.cache.size > this.capacity) {
this.cache.delete(this.cache.keys().next().value);
}
}
}- O(1): Constant
- O(log n): Logarithmic
- O(n): Linear
- O(n log n): Linearithmic
- O(n²): Quadratic
- O(2ⁿ): Exponential
- Auxiliary space
- In-place algorithms
- Trade-offs
localStorage.setItem('key', 'value');
const value = localStorage.getItem('key');
localStorage.removeItem('key');
localStorage.clear();- Asynchronous
- Transactional database
- Large storage capacity
- Service Worker cache
- Cache first/network first strategies
navigator.geolocation.getCurrentPosition(
position => console.log(position.coords),
error => console.error(error)
);- Accelerometer
- Gyroscope
- Magnetometer
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
.then(stream => {
videoElement.srcObject = stream;
});- Audio processing
- Synthesizers
- Visualizations
- Control media playback
- Lock screen controls
fetch('/api/data')
.then(response => response.json())
.then(data => console.log(data));const ws = new WebSocket('ws://localhost:8080');
ws.onmessage = (event) => console.log(event.data);
ws.send('message');const eventSource = new EventSource('/events');
eventSource.onmessage = (event) => {
console.log(event.data);
};- Communication between tabs
const channel = new BroadcastChannel('my-channel');
channel.postMessage('Hello');
channel.onmessage = (event) => console.log(event.data);- Sync when online
- Service Worker feature
- Large downloads
- Continues after page close
- Scheduled syncs
- Permission required
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
}
}mutation CreateUser($input: UserInput!) {
createUser(input: $input) {
id
name
}
}subscription OnMessageAdded {
messageAdded {
id
content
author
}
}fragment UserFields on User {
id
name
email
}- Caching
- Optimistic UI
- Local state management
- DevTools
- Lightweight
- Extensible
- Normalized cache (optional)
- Fragment colocation
- Pagination
- Optimized for large apps
- Immediate UI feedback
- Rollback on error
- Cursor-based
- Offset-based
- Relay connections
- Normalized cache
- Cache policies
- Cache updates
{
"name": "My App",
"short_name": "App",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#000000",
"icons": [...]
}- Lifecycle: install → activate → fetch
- Cache strategies
- Offline functionality
- beforeinstallprompt event
- Custom install UI
- Add to home screen
- Serve from cache, fallback to network
- Try network, fallback to cache
- Serve cache, update in background
- Specific use cases
- Service Worker notifications
- Permission handling
- Notification API
- Sync when online
- Queue requests
- Manifest shortcuts
- Quick actions
- Bundle size (JS, CSS)
- Image size
- Number of requests
- LCP, FID, CLS
- Time to Interactive
- Webpack bundle analyzer
- Lighthouse CI
- Build fails on violations
- Server-side composition
- Build-time composition
- Run-time composition
- iframe-based
- Webpack feature
- Share code at runtime
- Independent deployment
- Custom events
- Shared state
- Props (for nested MFEs)
- react-i18next
- formatjs
- LinguiJS
- Text direction (LTR/RTL)
- Date/time formatting
- Number/currency formatting
- Pluralization
- Translation management
- Transitions
- Keyframe animations
- Transform and opacity for performance
- Framer Motion
- GSAP
- React Spring
- Performance (60fps)
- Reduced motion preference
- Meaningful motion
class MyElement extends HTMLElement {
connectedCallback() {
this.innerHTML = `<p>Hello</p>`;
}
}
customElements.define('my-element', MyElement);- Encapsulation
- Scoped styles
<template>element- Clone and use
- Elements, Console, Network
- Performance profiler
- Memory profiler
- Lighthouse
- Component tree
- Props and state inspection
- Profiler
- Breakpoints
- Console methods
- Source maps
- Network throttling
- Atomic design principles
- Reusable component library
- Documentation (Storybook)
- Design tokens (colors, spacing, typography)
- Theme configuration
- Storybook
- Figma integration
- Component generators
- STAR method (Situation, Task, Action, Result)
- Prepare examples of: conflict, failure, leadership, technical decisions
- Show learning and growth
- Clarify requirements: Functional and non-functional
- High-level design: Components and data flow
- Deep dive: Specific areas based on interviewer focus
- Trade-offs: Discuss pros/cons of decisions
- Scalability: How to handle growth
- Think out loud
- Ask clarifying questions
- Draw diagrams
- Discuss trade-offs
- Be honest about unknowns
- Browser Internals are critical — understand rendering pipeline deeply
- React Server Components and Next.js App Router are mainstream
- TypeScript is expected, not optional
- Performance (Core Web Vitals) is a major focus
- System Design questions are common for mid+ levels
- Testing is non-negotiable — know Jest, RTL, Playwright
- Modern CSS (Container Queries, :has(), etc.) is important
- Security awareness is expected
- Real project experience matters more than memorization
- Communication skills are as important as technical knowledge
Good luck with your interviews! 🚀