
Modern CSS interviews test your ability to solve real problems, not recite definitions. You'll debug broken layouts, explain why a sticky navbar fails on mobile Safari, choose between Flexbox and Grid for specific use cases, and demonstrate knowledge of features like Container Queries and the :has() selector.
But here's what separates strong candidates from average ones: it's not just what you know, it's how you think. Interviewers want to see your problem-solving process, your ability to articulate trade-offs, and your understanding of "why" certain approaches work better than others. A candidate who can explain when to use em vs rem and justify their choice is far more valuable than someone who's memorized every CSS property.
This post focuses on:
Whether you're preparing for your first frontend role or interviewing at a senior level, this post will help you demonstrate both technical depth and clear reasoning - exactly what interviewers look for.
The box model is the foundation of CSS layout. Every element is a rectangular box with four areas: content, padding, border, and margin.
The interview question: "Explain the CSS box model and the difference between box-sizing: content-box and box-sizing: border-box."
/* content-box (default) */.content-box {box-sizing: content-box;width: 200px;padding: 20px;border: 5px solid black;/* Total width = 200 + 40 (padding) + 10 (border) = 250px */}/* border-box (modern approach) */.border-box {box-sizing: border-box;width: 200px;padding: 20px;border: 5px solid black;/* Total width = 200px (padding and border included) */}
Key points:
content-box adds padding and border to the specified widthborder-box includes padding and border within the specified widthbox-sizing: border-box globallybox-sizingPro tip: Explain that border-box makes responsive layouts more predictable because percentage widths behave intuitively.
The display property controls how an element participates in layout flow.
Common interview question: "What's the difference between display: none, visibility: hidden, and opacity: 0?"
| Property | Space Occupied | Accessible to Screen Readers | Events Triggered | Use Case |
|---|---|---|---|---|
display: none | No | No | No | Completely remove from layout |
visibility: hidden | Yes | No | No | Hide but maintain layout space |
opacity: 0 | Yes | Yes | Yes | Fade animations, accessible hiding |
Key display values:
/* Block - takes full width, stacks vertically */.block {display: block;}/* Inline - flows with text, respects horizontal spacing only */.inline {display: inline;}/* Inline-block - flows with text but respects width/height */.inline-block {display: inline-block;width: 100px;height: 100px;}/* Flex - modern one-dimensional layout */.flex {display: flex;}/* Grid - modern two-dimensional layout */.grid {display: grid;}
CSS Custom Properties (variables) enable dynamic, maintainable stylesheets.
Interview question: "How do CSS variables work, and what are their advantages over preprocessor variables?"
/* Define variables in :root for global scope */:root {--primary-color: #007bff;--spacing-unit: 8px;--border-radius: 4px;}/* Use variables with var() */.button {background-color: var(--primary-color);padding: calc(var(--spacing-unit) * 2);border-radius: var(--border-radius);}/* Override in specific contexts */.dark-theme {--primary-color: #66b3ff;}/* Fallback values */.element {color: var(--text-color, #333);}
CSS variables vs preprocessor variables:
| Feature | CSS Variables | Sass/Less Variables |
|---|---|---|
| Runtime changes | ✅ Yes | ❌ No (compile-time only) |
| JavaScript access | ✅ Yes | ❌ No |
| Cascade & inheritance | ✅ Yes | ❌ No |
| Browser support | ✅ Modern browsers | ✅ Compiles to CSS |
JavaScript integration:
// Read CSS variableconst primary = getComputedStyle(document.documentElement).getPropertyValue('--primary-color',);// Set CSS variabledocument.documentElement.style.setProperty('--primary-color', '#ff0000');
Specificity determines which CSS rule applies when multiple rules target the same element.
The classic question: "Explain CSS specificity. How is it calculated?"
Specificity hierarchy:
/* Specificity: 0-0-0-1 (1 element) */p {color: black;}/* Specificity: 0-0-1-0 (1 class) */.text {color: blue;}/* Specificity: 0-0-1-1 (1 class + 1 element) */p.text {color: green;}/* Specificity: 0-1-0-0 (1 ID) */#header {color: red;}/* Specificity: 1-0-0-0 (inline style) */<p style="color: purple;">/* Specificity: ∞ (!important overrides everything) */p {color: orange !important;}
Specificity calculation:
Example problem:
/* Which color wins? */#nav .menu li {color: red;} /* 0-1-1-1 = 111 */.header .menu li {color: blue;} /* 0-0-2-1 = 021 */li.active {color: green;} /* 0-0-1-1 = 011 *//* Answer: red (highest specificity) */
The position property controls how elements are positioned in the document flow.
Interview question: "Explain the different position values and when to use each."
/* Static (default) - normal document flow */.static {position: static;}/* Relative - offset from normal position, space preserved */.relative {position: relative;top: 10px;left: 20px;}/* Absolute - positioned relative to nearest positioned ancestor */.absolute {position: absolute;top: 0;right: 0;}/* Fixed - positioned relative to viewport */.fixed {position: fixed;bottom: 20px;right: 20px;}/* Sticky - hybrid of relative and fixed */.sticky {position: sticky;top: 0;}
Common use cases:
| Position | Use Case | Example |
|---|---|---|
static | Default flow | Regular content |
relative | Minor adjustments, positioning context | Offset badges, anchor for absolute children |
absolute | Overlays, tooltips | Dropdown menus, modals |
fixed | Persistent UI | Navigation bars, chat widgets |
sticky | Scroll-aware headers | Table headers, section titles |
Critical concept - positioning context:
/* Absolute positioning requires a positioned parent */.parent {position: relative; /* Creates positioning context */}.child {position: absolute;top: 0; /* Relative to .parent, not viewport */left: 0;}
Stacking context determines the 3D layering of elements along the z-axis.
Advanced interview question: "What creates a stacking context, and how does z-index work?"
What creates a stacking context:
/* 1. Root element (html) *//* 2. Positioned elements with z-index */.positioned {position: relative;z-index: 10;}/* 3. Flex/Grid items with z-index */.flex-item {z-index: 5;}/* 4. Elements with opacity < 1 */.transparent {opacity: 0.9;}/* 5. Transform, filter, perspective */.transformed {transform: translateZ(0);}/* 6. will-change */.optimized {will-change: transform;}
Z-index rules:
/* z-index only works on positioned elements */.static {z-index: 999; /* ❌ No effect (position: static) */}.relative {position: relative;z-index: 999; /* ✅ Works */}/* z-index is scoped to stacking context */.parent {position: relative;z-index: 1;}.child {position: relative;z-index: 9999; /* Still behind elements with z-index: 2 in different context */}
What to explain:
z-index only works on positioned elements (except flex/grid children)opacity, transform, filter, position + z-indexPseudo-classes select elements based on state, while pseudo-elements style specific parts of elements.
Interview question: "What's the difference between pseudo-classes and pseudo-elements?"
Pseudo-classes:
/* User interaction */a:hover {color: blue;}input:focus {border-color: blue;}button:active {transform: scale(0.98);}/* Form states */input:disabled {opacity: 0.5;}input:checked + label {font-weight: bold;}input:valid {border-color: green;}input:invalid {border-color: red;}/* Structural */li:first-child {margin-top: 0;}li:last-child {margin-bottom: 0;}li:nth-child(odd) {background: #f0f0f0;}li:nth-child(3n) {/* Every 3rd item */}
Pseudo-elements:
/* Generated content */.icon::before {content: '→';margin-right: 8px;}.external-link::after {content: ' ↗';}/* Text styling */p::first-line {font-weight: bold;}p::first-letter {font-size: 2em;float: left;}/* Selection styling */::selection {background: yellow;color: black;}
Key differences:
| Pseudo-classes | Pseudo-elements |
|---|---|
| Select elements in a specific state | Style specific parts of elements |
Single colon : (or ::) | Double colon :: |
:hover, :focus, :nth-child() | ::before, ::after, ::first-line |
CSS units determine how sizes are calculated. Choosing the right unit is crucial for responsive design.
Interview question: "Explain the difference between px, em, rem, %, and viewport units. When should you use each?"
/* Absolute - fixed size */.pixel {font-size: 16px; /* Always 16 pixels */}/* Relative to parent font-size */.em-unit {font-size: 1.5em; /* 1.5 × parent font-size */padding: 1em; /* 1 × this element's font-size */}/* Relative to root font-size */.rem-unit {font-size: 1.5rem; /* 1.5 × root font-size (usually 16px) */padding: 1rem; /* Always consistent */}/* Relative to parent dimensions */.percentage {width: 50%; /* 50% of parent width */}/* Relative to viewport */.viewport {width: 100vw; /* 100% of viewport width */height: 100vh; /* 100% of viewport height */}
Em vs rem - the compounding problem:
/* Em compounds */.parent {font-size: 16px;}.child {font-size: 1.5em; /* 16 × 1.5 = 24px */}.grandchild {font-size: 1.5em; /* 24 × 1.5 = 36px (compounds!) */}/* Rem doesn't compound */.parent {font-size: 1.5rem; /* 24px */}.child {font-size: 1.5rem; /* Still 24px (relative to root) */}
When to use each unit:
| Unit | Best For | Example |
|---|---|---|
px | Borders, shadows, precise control | border: 1px solid |
em | Spacing relative to font-size | padding: 0.5em 1em |
rem | Font sizes, consistent spacing | font-size: 1.125rem |
% | Responsive widths, fluid layouts | width: 50% |
vw/vh | Full-screen sections, responsive typography | height: 100vh |
Flexbox is a one-dimensional layout system for distributing space along a single axis.
Core interview question: "Explain Flexbox and its main properties."
Flex container properties:
.container {display: flex;/* Main axis direction */flex-direction: row; /* row | row-reverse | column | column-reverse *//* Wrapping */flex-wrap: wrap; /* nowrap | wrap | wrap-reverse *//* Main axis alignment */justify-content: space-between; /* flex-start | flex-end | center | space-between | space-around | space-evenly *//* Cross axis alignment */align-items: center; /* flex-start | flex-end | center | baseline | stretch *//* Gap between items */gap: 16px;}
Flex item properties:
.item {/* Growth factor */flex-grow: 1; /* Default: 0 *//* Shrink factor */flex-shrink: 1; /* Default: 1 *//* Base size */flex-basis: 200px; /* Default: auto *//* Shorthand: grow shrink basis */flex: 1 1 200px;flex: 1; /* Same as: 1 1 0 *//* Individual alignment */align-self: flex-end;}
Common patterns:
/* Equal-width columns */.column {flex: 1;}/* Center content */.center {display: flex;justify-content: center;align-items: center;}/* Space between header and footer */.layout {display: flex;flex-direction: column;min-height: 100vh;}.content {flex: 1; /* Takes remaining space */}
CSS Grid is a two-dimensional layout system for rows and columns.
Interview question: "When would you use Grid over Flexbox?"
Grid container properties:
.grid {display: grid;/* Define columns */grid-template-columns: 200px 1fr 1fr; /* Fixed + flexible */grid-template-columns: repeat(3, 1fr); /* 3 equal columns */grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); /* Responsive *//* Define rows */grid-template-rows: 100px auto 100px;/* Gap */gap: 20px;/* Alignment */justify-items: center; /* Align items horizontally */align-items: center; /* Align items vertically */}
Grid item properties:
.item {/* Column placement */grid-column: 1 / 3; /* Start at line 1, end at line 3 */grid-column: span 2; /* Span 2 columns *//* Row placement */grid-row: 1 / 3;grid-row: span 2;}
Named grid areas:
.layout {display: grid;grid-template-areas:'header header header''sidebar content content''footer footer footer';grid-template-columns: 200px 1fr 1fr;}.header {grid-area: header;}.sidebar {grid-area: sidebar;}.content {grid-area: content;}.footer {grid-area: footer;}
Interview question: "How do you decide between Flexbox and Grid?"
Use Flexbox for:
Use Grid for:
They work together:
/* Grid for page layout */.page {display: grid;grid-template-columns: 250px 1fr;}/* Flexbox for navigation inside header */.header nav {display: flex;justify-content: space-between;align-items: center;}/* Grid for card layout */.cards {display: grid;grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));gap: 2rem;}/* Flexbox inside each card */.card {display: flex;flex-direction: column;}.card-content {flex: 1;}
The classic interview question: "How do you center a div?"
Modern solutions:
/* 1. Flexbox (most common) */.flex-center {display: flex;justify-content: center;align-items: center;}/* 2. Grid */.grid-center {display: grid;place-items: center; /* Shorthand for align-items + justify-items */}/* 3. Absolute positioning */.absolute-center {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);}/* 4. Margin auto (horizontal only) */.margin-center {width: 300px;margin: 0 auto;}
When to use each:
| Method | Use Case | Pros | Cons |
|---|---|---|---|
| Flexbox | Most situations | Simple, flexible | Requires parent styling |
| Grid | Grid layouts | Very concise | Overkill for simple cases |
| Absolute + Transform | Overlays, modals | Works without knowing size | Removes from flow |
| Margin auto | Block elements | Simple for horizontal | Vertical requires height |
Media queries enable responsive designs that adapt to different screen sizes and device capabilities.
Interview question: "Explain mobile-first vs desktop-first approaches to responsive design."
Mobile-first approach (recommended):
/* Base styles for mobile */.container {padding: 1rem;font-size: 14px;}/* Tablet and up */@media (min-width: 768px) {.container {padding: 2rem;font-size: 16px;}}/* Desktop and up */@media (min-width: 1024px) {.container {padding: 3rem;max-width: 1200px;margin: 0 auto;}}
Beyond width - other media features:
/* Orientation */@media (orientation: landscape) {.gallery {grid-template-columns: repeat(4, 1fr);}}/* Hover capability (desktop vs touch) */@media (hover: hover) {.button:hover {background: blue;}}@media (hover: none) {.button:active {background: blue;}}/* Prefers color scheme */@media (prefers-color-scheme: dark) {:root {--bg-color: #1a1a1a;--text-color: #ffffff;}}/* Prefers reduced motion */@media (prefers-reduced-motion: reduce) {* {animation-duration: 0.01ms !important;transition-duration: 0.01ms !important;}}
What interviewers want to hear:
prefers-reduced-motionTransforms change an element's appearance without affecting document flow. Transitions animate property changes.
Interview question: "Explain the difference between transforms and transitions. How do they affect performance?"
Transforms:
/* 2D Transforms */.transform-2d {transform: translate(50px, 100px);transform: rotate(45deg);transform: scale(1.5);transform: translate(50px, 100px) rotate(45deg) scale(1.2);}/* 3D Transforms */.transform-3d {transform: translateZ(100px);transform: rotateY(45deg);transform: perspective(1000px) rotateY(45deg);}
Transitions:
/* Basic transition */.button {background: blue;transition: background 0.3s ease;}.button:hover {background: darkblue;}/* Multiple properties */.card {transform: scale(1);box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);transition:transform 0.3s ease,box-shadow 0.3s ease;}.card:hover {transform: scale(1.05);box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);}
Performance considerations:
/* ✅ GPU-accelerated (performant) */.performant {transform: translateX(100px);opacity: 0.5;}/* ❌ Triggers layout/paint (slow) */.slow {left: 100px; /* Use transform instead */width: 200px; /* Triggers reflow */}
What to emphasize:
transform and opacity are GPU-acceleratedwidth, height, top, left - use transform insteadwill-change hints to browser but use sparingly (memory cost)Responsive images adapt to different screen sizes and resolutions for optimal performance.
CSS approaches:
/* Basic responsive image */img {max-width: 100%;height: auto;}/* Object-fit for aspect ratio control */.image-container {width: 300px;height: 200px;}.image-container img {width: 100%;height: 100%;object-fit: cover; /* cover | contain | fill */object-position: center;}
HTML approaches:
<!-- srcset for different resolutions --><imgsrc="image.jpg"srcset="image.jpg 1x, image@2x.jpg 2x, image@3x.jpg 3x"alt="Description" /><!-- picture element for art direction --><picture><source media="(min-width: 1024px)" srcset="desktop.jpg" /><source media="(min-width: 768px)" srcset="tablet.jpg" /><img src="mobile.jpg" alt="Description" /></picture><!-- Modern formats with fallback --><picture><source srcset="image.avif" type="image/avif" /><source srcset="image.webp" type="image/webp" /><img src="image.jpg" alt="Description" /></picture>
Aspect ratio (modern CSS):
/* New way - aspect-ratio property */.aspect-ratio-new {aspect-ratio: 16 / 9;}.aspect-ratio-new img {width: 100%;height: 100%;object-fit: cover;}
Modern CSS functions enable responsive values without media queries.
Interview question: "Explain how clamp(), min(), and max() work and when to use them."
/* min() - picks the smallest value */.element {width: min(100%, 600px); /* Never wider than 600px */}/* max() - picks the largest value */.element {width: max(50%, 300px); /* At least 300px wide */}/* clamp() - value between min and max */.element {/* clamp(minimum, preferred, maximum) */font-size: clamp(1rem, 2.5vw, 2rem);/* Font size is 2.5vw, but never smaller than 1rem or larger than 2rem */}
Practical examples:
/* Responsive typography without media queries */h1 {font-size: clamp(2rem, 5vw, 4rem);}/* Responsive container width */.content {width: min(90%, 1200px);margin: 0 auto;}/* Responsive spacing */.container {padding: clamp(1rem, 5vw, 3rem);}
Container queries allow components to respond to their container size, not the viewport.
Interview question: "What are container queries and how do they differ from media queries?"
/* Define a container */.card-container {container-type: inline-size;container-name: card;}/* Query the container */@container card (min-width: 400px) {.card {display: grid;grid-template-columns: 200px 1fr;}}@container card (min-width: 600px) {.card {grid-template-columns: 250px 1fr;font-size: 1.125rem;}}
Why container queries matter:
:has() parent selectorThe :has() pseudo-class selects parent elements based on their children.
Interview question: "What is the :has() selector and what problems does it solve?"
/* Select parent that contains specific child */.card:has(img) {display: grid;grid-template-columns: 200px 1fr;}/* Select parent based on child state */form:has(input:invalid) {border: 2px solid red;}/* Style parent when checkbox is checked */.option:has(input:checked) {background: blue;color: white;}
Form validation styling:
/* Show error when input is invalid and touched */.form-field:has(input:invalid:not(:placeholder-shown)) .error {display: block;}/* Style label when input is focused */.form-field:has(input:focus) label {color: blue;transform: translateY(-1.5rem) scale(0.9);}
What makes :has() revolutionary:
:is() and :where()These pseudo-classes simplify complex selectors and reduce specificity issues.
Interview question: "What's the difference between :is() and :where()?"
/* Without :is() - repetitive */.header a:hover,.footer a:hover,.sidebar a:hover {color: blue;}/* With :is() - concise */:is(.header, .footer, .sidebar) a:hover {color: blue;}/* :where() - zero specificity */:where(.button) {background: gray;}.button.primary {background: blue; /* Easily overrides */}
Key difference:
:is() has specificity of its most specific argument:where() always has zero specificityLogical properties adapt to writing direction (LTR/RTL) automatically.
Interview question: "What are CSS logical properties and why are they important?"
/* Physical properties (direction-dependent) */.physical {margin-left: 1rem;margin-right: 2rem;}/* Logical properties (direction-independent) */.logical {margin-inline-start: 1rem; /* left in LTR, right in RTL */margin-inline-end: 2rem; /* right in LTR, left in RTL */}
Logical property mapping:
| Physical | Logical |
|---|---|
margin-left | margin-inline-start |
margin-right | margin-inline-end |
margin-top | margin-block-start |
margin-bottom | margin-block-end |
width | inline-size |
height | block-size |
Why logical properties matter:
Cascade layers provide explicit control over CSS specificity and cascade order.
Interview question: "What are cascade layers and how do they help manage CSS at scale?"
/* Define layer order (lowest to highest priority) */@layer reset, base, components, utilities;/* Add styles to layers */@layer reset {* {margin: 0;padding: 0;box-sizing: border-box;}}@layer components {.button {padding: 0.5rem 1rem;background: blue;}}
Layer priority:
/* Later layers win, regardless of specificity */@layer base {#id.class {color: red; /* High specificity, but in earlier layer */}}@layer components {.simple {color: blue; /* Wins! Even with lower specificity */}}
Why layers matter:
The aspect-ratio property maintains an element's width-to-height ratio.
/* Modern way */.video {aspect-ratio: 16 / 9;width: 100%;}/* Square */.avatar {aspect-ratio: 1;width: 100px; /* Height will be 100px */}
Practical examples:
/* Responsive image grid */.image-grid {display: grid;grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));gap: 1rem;}.image-grid img {aspect-ratio: 1;width: 100%;object-fit: cover;}
Interviewers aren't testing if you've memorized Tailwind classes. They want to understand:
Utility-first pros:
Utility-first cons:
How to articulate your understanding:
The key is showing you understand the trade-offs, not claiming one approach is universally better. For example:
It depends on the project context. Tailwind excels when you need rapid prototyping and design consistency across a component-based application. The utility-first approach reduces context switching and eliminates naming decisions. However, it does couple styles to markup, which some teams find harder to maintain. For content-heavy sites with diverse layouts, or teams new to utility-first CSS, traditional approaches might be more appropriate. I'd evaluate based on team experience, project requirements, and long-term maintainability needs.
This shows you can think critically about tools rather than following trends blindly.
Flexbox patterns:
<!-- Center content --><div class="flex min-h-screen items-center justify-center"><div>Centered content</div></div><!-- Space between --><nav class="flex items-center justify-between p-4"><div>Logo</div><div>Menu</div></nav><!-- Responsive direction --><div class="flex flex-col gap-4 md:flex-row"><div>Stacks on mobile, row on desktop</div></div>
Grid patterns:
<!-- Auto-fit grid --><div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3"><div>Card 1</div><div>Card 2</div><div>Card 3</div></div><!-- Sidebar layout --><div class="grid grid-cols-[250px_1fr] gap-4"><aside>Sidebar</aside><main>Content</main></div>
Spacing utilities:
<!-- Padding and margin --><div class="mx-auto p-4">Content</div><div class="px-4 py-2">Horizontal and vertical padding</div><!-- Space between children --><div class="space-y-4"><div>Item 1</div><div>Item 2</div></div>
@applyWhen it's helpful:
/* Extracting repeated patterns */.btn {@apply rounded px-4 py-2 font-medium transition-colors;}.btn-primary {@apply btn bg-blue-500 text-white hover:bg-blue-600;}
When it becomes an anti-pattern:
/* ❌ Defeats the purpose of utility-first */.card {@apply space-y-4 rounded-lg bg-white p-6 shadow-md;}/* You've just recreated traditional CSS! */
Better alternative - component abstraction:
// React component (better than @apply)function Button({ variant = 'primary', children }) {const baseClasses = 'px-4 py-2 rounded font-medium transition-colors';const variantClasses = {primary: 'bg-blue-500 text-white hover:bg-blue-600',secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',};return (<button className={clsx(baseClasses, variantClasses[variant])}>{children}</button>);}
Just-In-Time (JIT) compilation:
JIT generates styles on-demand as you write them, enabling:
Arbitrary values:
<!-- Custom spacing --><div class="mt-[137px]">Custom margin</div><!-- Custom colors --><div class="bg-[#1da1f2]">Twitter blue</div><!-- Custom sizes --><div class="w-[347px]">Exact width</div><!-- With CSS variables --><div class="bg-[var(--brand-color)]">Variable color</div>
How you should think about it:
Arbitrary values are escape hatches for one-off designs. Use them when you need a specific value that doesn't fit the design system, but don't abuse them - if you're using the same arbitrary value repeatedly, it should be in your config.
md:, lg:)Very common interview question: "How does responsive design work in Tailwind?"
Breakpoint system:
<!-- Mobile-first approach --><div class="text-sm md:text-base lg:text-lg xl:text-xl">Responsive text size</div><!-- Default breakpoints:sm: 640pxmd: 768pxlg: 1024pxxl: 1280px2xl: 1536px-->
Common responsive patterns:
<!-- Responsive grid --><div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3"><div>Card</div></div><!-- Hide/show at breakpoints --><div class="hidden md:block">Only visible on tablet and up</div><div class="block md:hidden">Only visible on mobile</div><!-- Responsive spacing --><div class="p-4 md:p-6 lg:p-8">More padding on larger screens</div>
What to emphasize:
Tailwind uses a mobile-first approach. Classes without prefixes apply to all screen sizes, and prefixed classes apply from that breakpoint up. This matches modern CSS best practices and makes responsive design intuitive.
This section is where most candidates struggle - and where you can truly stand out. Interviewers use scenarios to assess problem-solving, not just knowledge recall.
Question: "You have a sticky navbar that works on desktop but doesn't stick on mobile Safari. What could be the issue?"
Common issues:
/* ❌ Problem 1: Parent has overflow hidden */.parent {overflow: hidden; /* Sticky won't work! */}/* ✅ Solution: Remove overflow or use overflow: clip */.parent {overflow: clip; /* Allows sticky to work */}/* ❌ Problem 2: Mobile Safari viewport units */.navbar {position: sticky;top: 0;height: 10vh; /* Safari's vh includes address bar */}/* ✅ Solution: Use dvh (dynamic viewport height) */.navbar {position: sticky;top: 0;height: 10dvh; /* Accounts for mobile browser UI */}
Example answer:
I'd first check if the parent container has
overflow: hidden, which breaks sticky positioning. Then I'd verify the sticky element has room to scroll. For mobile Safari specifically, I'd check if we're usingvhunits - Safari's viewport height includes the address bar, so I'd switch todvh(dynamic viewport height) or fixed pixel values.
Question: "You set z-index: 9999 on a modal, but it still appears behind other elements. Why?"
/* ❌ Modal has high z-index but is trapped */.sidebar {position: relative;z-index: 1;transform: translateX(0); /* Creates stacking context! */}.modal {position: fixed;z-index: 9999; /* Doesn't matter - stuck in sidebar's context */}/* ✅ Solution: Move modal outside stacking context *//* Render modal at root level in HTML *//* ✅ Or remove stacking context creator */.sidebar {position: relative;z-index: 1;/* Remove transform */}
Example answer:
The issue is likely a stacking context. Even with
z-index: 9999, the modal is isolated within a parent's stacking context. Common culprits aretransform,opacity < 1,filter, orwill-change. I'd inspect the DOM tree to find which parent creates the stacking context, then either move the modal to the root level or remove the stacking context creator.
Question: "You have a flex container with text that overflows instead of wrapping. How do you fix it?"
/* ❌ Problem: Flex items won't shrink below content size */.item {flex: 1;/* min-width defaults to 'auto' (content size) */}/* ✅ Solution: Override min-width */.item {flex: 1;min-width: 0; /* Allow shrinking below content size */}.item p {overflow: hidden;text-overflow: ellipsis;white-space: nowrap;}
Example answer:
Flex items have
min-width: autoby default, which prevents them from shrinking below their content size. I'd setmin-width: 0on the flex item to allow it to shrink. Then I'd handle the text overflow with eithertext-overflow: ellipsisfor truncation orword-breakfor wrapping.
Question: "Your CSS Grid layout looks perfect on desktop but breaks on mobile with horizontal scrolling. What's wrong?"
/* ❌ Problem: Fixed column widths */.grid {display: grid;grid-template-columns: 300px 300px 300px; /* Overflows on mobile */}/* ✅ Solution: Responsive columns with minmax */.grid {display: grid;grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));gap: 1rem;}/* ✅ Or breakpoint-based columns */.grid {grid-template-columns: 1fr; /* Mobile: single column */}@media (min-width: 768px) {.grid {grid-template-columns: repeat(2, 1fr); /* Tablet: 2 columns */}}
Question: "Your animation is janky and causing performance issues. How do you optimize it?"
/* ❌ Problem: Animating layout properties */.slow-animation {transition:width 0.3s,left 0.3s;}.slow-animation:hover {width: 300px; /* Triggers layout */left: 100px; /* Triggers layout */}/* ✅ Solution: Use transform and opacity only */.fast-animation {transition:transform 0.3s,opacity 0.3s;will-change: transform;}.fast-animation:hover {transform: translateX(100px) scale(1.2); /* GPU-accelerated */opacity: 0.8; /* GPU-accelerated */}
Performance checklist:
transform and opacitywill-change sparingly (memory cost)width, height, top, leftQuestion: "You need to center a modal both horizontally and vertically, but it should scroll if content exceeds viewport height. How?"
/* ❌ Problem: Fixed positioning breaks scrolling */.modal-overlay {position: fixed;inset: 0;display: flex;align-items: center;justify-content: center;}.modal {max-height: 90vh;/* If content exceeds 90vh, it's cut off! */}/* ✅ Solution: Allow scrolling with proper overflow */.modal-overlay {position: fixed;inset: 0;display: flex;align-items: center;justify-content: center;padding: 2rem;overflow-y: auto; /* Allow scrolling */}.modal {max-width: 600px;width: 100%;margin: auto; /* Centers when scrolling */}
Question: "Implement dark mode that respects user preferences but allows manual override."
/* ✅ System preference detection */:root {--bg-color: #ffffff;--text-color: #000000;}@media (prefers-color-scheme: dark) {:root {--bg-color: #1a1a1a;--text-color: #ffffff;}}/* ✅ Manual override with data attribute */[data-theme='light'] {--bg-color: #ffffff;--text-color: #000000;}[data-theme='dark'] {--bg-color: #1a1a1a;--text-color: #ffffff;}/* Usage */body {background-color: var(--bg-color);color: var(--text-color);}
// JavaScript for manual toggleconst theme = localStorage.getItem('theme') || 'auto';if (theme === 'auto') {document.documentElement.removeAttribute('data-theme');} else {document.documentElement.setAttribute('data-theme', theme);}
When you're asked to debug CSS during a live coding interview, your DevTools skills reveal your experience level.
Essential DevTools skills:
Other techniques:
Interviewers want to understand your thought process. Silent coding makes them nervous.
The framework:
1. Over-engineering
Start simple, iterate. Don't build a complex solution when a simple one works.
2. Ignoring accessibility
<!-- ❌ Not accessible --><div onclick="handleClick()">Click me</div><!-- ✅ Accessible --><button type="button" onclick="handleClick()">Click me</button>
3. Not testing edge cases
Always test:
4. Forgetting browser compatibility
I'm using CSS Grid here, which has excellent browser support in modern browsers. If we needed to support IE11, I'd use Flexbox instead.
5. Not asking clarifying questions
Good questions to ask:
Today's interviewers want to see how you think, how you solve problems, and whether you understand the "why" behind CSS patterns - not just the "what".
The candidates who succeed aren't necessarily those who've memorized every CSS property. They're the ones who can:
Modern CSS knowledge - Container Queries, :has(), clamp(), logical properties - signals that you're staying current with the platform. But clarity in reasoning is what gets you hired.
Your next steps:
Practice real scenarios instead of memorizing Q&A. Build actual components, debug real issues, and explain your decisions out loud.
Master DevTools. Spend time exploring the Layout panel, Performance tab, and Accessibility inspector. These tools are your best friends in interviews.
Build a mental framework for approaching CSS problems:
Stay curious. CSS is evolving rapidly. Follow blogs, experiment with new features, and understand browser compatibility.
Get hands-on practice. Head over to GreatFrontEnd's CSS questions to practice build real components and practice for next interview.
Remember: companies aren't just hiring CSS experts - they're hiring problem solvers who can communicate clearly, work collaboratively, and build accessible, performant user interfaces.

Remember the first time you added TypeScript to a React project? The initial excitement of autocomplete and type safety quickly turned into frustration with cryptic error messages and confusing type definitions. You're not alone - this is the reality for most developers making the switch.
TypeScript with React has become the industry standard. React 19, Next.js 16, and virtually every modern framework now ship with first-class TypeScript support. Companies expect it, teams rely on it, and your career growth depends on mastering it.
But here's the problem: most developers learn TypeScript reactively, fixing errors as they appear rather than understanding the patterns that prevent them. This leads to codebases filled with any types, overly complex generics, and brittle component APIs that break during refactoring.
The cost is real. A single poorly-typed component can cascade into hours of debugging. Missing event handler types lead to runtime crashes. Incorrect hook typing creates subtle bugs that only appear in production.
This post reveals the 12 most common TypeScript mistakes React developers make and shows you exactly how to fix them. You'll learn the patterns senior developers use, understand why certain approaches work better than others, and gain the confidence to write type-safe React code from day one.
Ready to level up your TypeScript skills? Let's dive in. And when you're done, head over to GreatFrontEnd's TypeScript interview questions to practice and prepare for your next interview.
TypeScript isn't just about catching bugs - it's about building better software faster.
Type safety means catching errors before they reach production. Instead of discovering that user.profile.avatar is undefined at 2 AM when your app crashes, TypeScript tells you at compile time. No more defensive coding with endless null checks.
Productivity gains are immediate and measurable. IntelliSense shows you every available prop as you type. Autocomplete writes half your code for you. Refactoring a component name? TypeScript updates every import automatically. What used to take hours now takes minutes.
Collaboration improves dramatically. Your component's props are self-documenting. New team members understand your API without reading documentation. Code reviews focus on logic, not "what does this parameter do?"
Hiring trends are clear: 78% of React job postings now require TypeScript experience. It's no longer optional - it's expected.
// ❌ Without TypeScript - Runtime error waiting to happenfunction UserProfile({ user }) {return <div>{user.profile.name}</div>;}// ✅ With TypeScript - Error caught at compile timetype User = {profile?: {name: string;};};function UserProfile({ user }: { user: User }) {return <div>{user.profile?.name ?? 'Anonymous'}</div>;}
Did you know? Teams using TypeScript report 15% fewer production bugs and 20% faster onboarding for new developers.
React.FC seems convenient - it types children automatically and provides type inference. But it comes with hidden costs that experienced teams avoid.
What React.FC does:
children in props (even when you don't want it)displayName, propTypes, and other legacy propertiesWhy explicit typing is clearer:
// ❌ Using React.FC - children included even when not neededconst Button: React.FC<{ onClick: () => void }> = ({ onClick }) => {return <button onClick={onClick}>Click me</button>;};// This compiles but shouldn't - Button doesn't render children!<Button onClick={handleClick}><span>This text is ignored</span></Button>;// ✅ Explicit prop typing - clear and intentionaltype ButtonProps = {onClick: () => void;};function Button({ onClick }: ButtonProps) {return <button onClick={onClick}>Click me</button>;}// ❌ TypeScript error - children not accepted<Button onClick={handleClick}><span>Error!</span></Button>;
When teams avoid React.FC:
Using any for props defeats the entire purpose of TypeScript. Your components become black boxes, autocomplete disappears, and refactoring becomes dangerous.
Why any breaks correctness:
// ❌ Props typed as any - no safety, no autocompletefunction Card({ title, description, variant }: any) {return (<div className={variant}><h2>{title}</h2><p>{description}</p></div>);}// This compiles but crashes at runtime<Card variant={123} />;
Proper typing with optional props and variants:
// ✅ Explicit prop types with variantstype CardProps = {title: string;description?: string; // Optional propvariant?: 'primary' | 'secondary' | 'danger'; // Union type for variantsonClose?: () => void;};function Card({ title, description, variant = 'primary', onClose }: CardProps) {return (<div className={`card card--${variant}`}><h2>{title}</h2>{description && <p>{description}</p>}{onClose && <button onClick={onClose}>×</button>}</div>);}// ✅ TypeScript catches errors<Card title="Hello" variant="invalid" />; // Error: Type '"invalid"' is not assignable
Real-world design system example:
type TagProps = {label: string;size?: 'sm' | 'md' | 'lg';color?: 'blue' | 'green' | 'red' | 'gray';removable?: boolean;onRemove?: () => void;};function Tag({label,size = 'md',color = 'gray',removable = false,onRemove,}: TagProps) {return (<span className={`tag tag--${size} tag--${color}`}>{label}{removable && <button onClick={onRemove}>×</button>}</span>);}
Key takeaway: Always type your TypeScript React components explicitly. Mark optional props with ? and use union types for variants.
Event handlers are one of the most commonly mistyped patterns in React. Using Function or any seems harmless until you need to access event.target.value and TypeScript can't help you.
Common misuse:
// ❌ Too loose - no type safetyfunction SearchInput({ onChange }: { onChange: Function }) {return <input onChange={onChange} />;}// ❌ Using any - defeats the purposefunction SearchInput({ onChange }: { onChange: any }) {return <input onChange={onChange} />;}
Correct event type patterns:
// ✅ Proper event typingtype SearchInputProps = {onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;};function SearchInput({ onChange }: SearchInputProps) {return <input type="text" onChange={onChange} />;}// Usage with full type safetyfunction App() {const handleSearch = (event: React.ChangeEvent<HTMLInputElement>) => {console.log(event.target.value); // ✅ TypeScript knows this is a string};return <SearchInput onChange={handleSearch} />;}
Event types mapped to elements:
| Element type | Event type | Common use case |
|---|---|---|
<input>, <textarea> | React.ChangeEvent<HTMLInputElement> | Form inputs |
<form> | React.FormEvent<HTMLFormElement> | Form submission |
<button>, <div> | React.MouseEvent<HTMLButtonElement> | Click handlers |
<input> | React.KeyboardEvent<HTMLInputElement> | Keyboard shortcuts |
<input> | React.FocusEvent<HTMLInputElement> | Focus/blur events |
Real-world form validation scenario:
type LoginFormProps = {onSubmit: (email: string, password: string) => void;};function LoginForm({ onSubmit }: LoginFormProps) {const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {event.preventDefault();const formData = new FormData(event.currentTarget);const email = formData.get('email') as string;const password = formData.get('password') as string;onSubmit(email, password);};return (<form onSubmit={handleSubmit}><input name="email" type="email" required /><input name="password" type="password" required /><button type="submit">Login</button></form>);}
forwardRef typing confusionforwardRef is notoriously tricky to type correctly. The generic syntax is confusing, and combining it with custom props often leads to type errors that are hard to debug.
Why forwardRef is tricky:
// ❌ Common mistake - ref type is wrongconst Input = forwardRef((props, ref) => {return <input ref={ref} {...props} />;});// Error: Type 'ForwardedRef<unknown>' is not assignable to type 'LegacyRef<HTMLInputElement>'
Correct generic syntax:
// ✅ Proper forwardRef typingtype InputProps = {placeholder?: string;error?: boolean;};const Input = forwardRef<HTMLInputElement, InputProps>(({ placeholder, error }, ref) => {return (<inputref={ref}placeholder={placeholder}className={error ? 'input--error' : 'input'}/>);},);Input.displayName = 'Input';
Common error messages and fixes:
| Error message | Fix |
|---|---|
Type 'ForwardedRef<unknown>' is not assignable | Add generic types: forwardRef<ElementType, PropsType> |
Property 'displayName' does not exist | Add ComponentName.displayName = 'Name' after definition |
Type instantiation is excessively deep | Simplify prop spreading or use ComponentPropsWithoutRef |
useState with complex statesTypeScript can infer simple useState types, but it fails with complex objects, null unions, and API data. Explicit typing prevents runtime errors and improves autocomplete.
When inference fails:
// ❌ TypeScript infers type as undefined, can't add user laterconst [user, setUser] = useState();// Later in code...setUser({ id: 1, name: 'John' }); // Error: Argument of type '{ id: number; name: string; }' is not assignable
Null unions and API data patterns:
// ✅ Explicit typing with null uniontype User = {id: number;name: string;email: string;avatar?: string;};const [user, setUser] = useState<User | null>(null);// ✅ TypeScript knows user can be nullif (user) {console.log(user.name); // Safe access}
Auth/profile data example:
type AuthState = {user: User | null;isAuthenticated: boolean;isLoading: boolean;};function useAuth() {const [auth, setAuth] = useState<AuthState>({user: null,isAuthenticated: false,isLoading: true,});const login = async (email: string, password: string) => {setAuth((prev) => ({ ...prev, isLoading: true }));try {const user = await api.login(email, password);setAuth({ user, isAuthenticated: true, isLoading: false });} catch (error) {setAuth({ user: null, isAuthenticated: false, isLoading: false });}};return { auth, login };}
useRef typingRefs need careful typing because they start as null and get assigned later. Mistyping refs leads to runtime errors when accessing .current.
Why refs need null initial value:
// ❌ Wrong - TypeScript thinks ref is always HTMLInputElementconst inputRef = useRef<HTMLInputElement>();// Later...inputRef.current.focus(); // Error: Object is possibly 'undefined'
Matching element types:
// ✅ Correct - ref can be null initiallyconst inputRef = useRef<HTMLInputElement>(null);// ✅ Safe access with optional chainingconst focusInput = () => {inputRef.current?.focus();};return <input ref={inputRef} />;
Mutable value refs vs DOM refs:
// ✅ DOM ref - starts as nullconst buttonRef = useRef<HTMLButtonElement>(null);// ✅ Mutable value ref - doesn't need nullconst renderCount = useRef<number>(0);useEffect(() => {renderCount.current += 1;});// ✅ Storing previous valueconst prevValue = useRef<string>();useEffect(() => {prevValue.current = value;}, [value]);
useReducer without discriminated unionsString-based action types are error-prone. Discriminated unions make reducers type-safe and eliminate entire classes of bugs.
Why string action types are risky:
// ❌ Unsafe - typos compile but break at runtimefunction reducer(state, action) {switch (action.type) {case 'LOAD_START':return { ...state, loading: true };case 'LOAD_SUCESS': // Typo! This case never matchesreturn { ...state, loading: false, data: action.payload };}}
Discriminated unions enforce strictness:
// ✅ Type-safe reducer with discriminated unionstype LoadingState = {status: 'idle' | 'loading' | 'success' | 'error';data: string | null;error: string | null;};type Action =| { type: 'LOAD_START' }| { type: 'LOAD_SUCCESS'; payload: string }| { type: 'LOAD_ERROR'; error: string };function reducer(state: LoadingState, action: Action): LoadingState {switch (action.type) {case 'LOAD_START':return { status: 'loading', data: null, error: null };case 'LOAD_SUCCESS':return { status: 'success', data: action.payload, error: null };case 'LOAD_ERROR':return { status: 'error', data: null, error: action.error };default:return state;}}
Example with loading, success, error:
function DataFetcher() {const [state, dispatch] = useReducer(reducer, {status: 'idle',data: null,error: null,});const fetchData = async () => {dispatch({ type: 'LOAD_START' });try {const data = await api.getData();dispatch({ type: 'LOAD_SUCCESS', payload: data });} catch (error) {dispatch({ type: 'LOAD_ERROR', error: error.message });}};return (<div>{state.status === 'loading' && <Spinner />}{state.status === 'success' && <div>{state.data}</div>}{state.status === 'error' && <Error message={state.error} />}</div>);}
useContext without proper type guardsContext can resolve to undefined if consumed outside a provider. Type guards prevent runtime crashes and improve developer experience.
Why context can be undefined:
// ❌ Context might be undefinedconst UserContext = createContext<User | undefined>(undefined);function useUser() {const user = useContext(UserContext);return user; // Could be undefined!}// Later...function Profile() {const user = useUser();return <div>{user.name}</div>; // Runtime error if no provider!}
Safe custom hook pattern:
// ✅ Type guard ensures context is always definedconst UserContext = createContext<User | undefined>(undefined);function useUser() {const user = useContext(UserContext);if (!user) {throw new Error('useUser must be used within UserProvider');}return user;}// Now user is always definedfunction Profile() {const user = useUser(); // TypeScript knows user is User, not User | undefinedreturn <div>{user.name}</div>; // Safe!}
Complete example:
type Theme = {primary: string;secondary: string;background: string;};const ThemeContext = createContext<Theme | undefined>(undefined);export function ThemeProvider({ children }: { children: React.ReactNode }) {const theme: Theme = {primary: '#007bff',secondary: '#6c757d',background: '#ffffff',};return (<ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>);}export function useTheme() {const theme = useContext(ThemeContext);if (!theme) {throw new Error('useTheme must be used within ThemeProvider');}return theme;}
useMemo and useCallback type inference issuesTypeScript usually infers types for useMemo and useCallback, but complex scenarios require explicit typing for correctness and performance.
When explicit typing is necessary:
// ❌ Type inference fails with complex return typesconst processedData = useMemo(() => {return items.map((item) => ({...item,computed: expensiveCalculation(item),}));}, [items]);// TypeScript might infer too broad a type
Generic parameters:
// ✅ Explicit typing for claritytype ProcessedItem = {id: number;name: string;computed: number;};const processedData = useMemo<ProcessedItem[]>(() => {return items.map((item) => ({id: item.id,name: item.name,computed: expensiveCalculation(item),}));}, [items]);
Dependency array type checking:
// ✅ useCallback with explicit typesconst handleSubmit = useCallback((event: React.FormEvent<HTMLFormElement>) => {event.preventDefault();onSubmit(formData);},[formData, onSubmit], // TypeScript checks these dependencies);
Utility types like Pick, Omit, and Partial reduce duplication and make prop types more maintainable.
Common utility types:
type User = {id: number;name: string;email: string;password: string;createdAt: Date;};// ✅ Pick - select specific propertiestype UserPreview = Pick<User, 'id' | 'name'>;// ✅ Omit - exclude propertiestype PublicUser = Omit<User, 'password'>;// ✅ Partial - make all properties optionaltype UserUpdate = Partial<User>;// ✅ Required - make all properties requiredtype CompleteUser = Required<User>;// ✅ Readonly - make all properties readonlytype ImmutableUser = Readonly<User>;// ✅ Record - create object type with specific keystype UserRoles = Record<'admin' | 'user' | 'guest', boolean>;
Design system usage:
// ✅ Base button propstype BaseButtonProps = {variant: 'primary' | 'secondary' | 'danger';size: 'sm' | 'md' | 'lg';disabled?: boolean;loading?: boolean;};// ✅ Icon button omits size, adds icontype IconButtonProps = Omit<BaseButtonProps, 'size'> & {icon: React.ReactNode;};// ✅ Link button picks variant, adds hreftype LinkButtonProps = Pick<BaseButtonProps, 'variant'> & {href: string;external?: boolean;};
The children prop has multiple possible types. Using the wrong one causes type errors and limits component flexibility.
When to use each type:
// ✅ ReactNode - accepts anything renderabletype ContainerProps = {children: React.ReactNode; // string, number, JSX, array, null, etc.};function Container({ children }: ContainerProps) {return <div className="container">{children}</div>;}// ✅ ReactElement - only accepts JSX elementstype WrapperProps = {children: React.ReactElement; // Must be a single JSX element};function Wrapper({ children }: WrapperProps) {return <div className="wrapper">{children}</div>;}// ✅ JSX.Element - similar to ReactElementtype LayoutProps = {children: JSX.Element;};// ✅ string - only accepts stringstype LabelProps = {children: string;};function Label({ children }: LabelProps) {return <label>{children.toUpperCase()}</label>;}
Render prop patterns:
// ✅ Render prop with function typetype DataListProps<T> = {data: T[];renderItem: (item: T, index: number) => React.ReactNode;};function DataList<T>({ data, renderItem }: DataListProps<T>) {return (<ul>{data.map((item, index) => (<li key={index}>{renderItem(item, index)}</li>))}</ul>);}// Usage<DataList data={users} renderItem={(user) => <UserCard user={user} />} />;
Extending native HTML attributes improves autocomplete and type safety when spreading props.
Extending HTML attributes:
// ❌ Props don't extend native attributestype ButtonProps = {variant: 'primary' | 'secondary';};function Button({ variant, ...props }: ButtonProps) {return <button {...props} className={`btn btn--${variant}`} />;}// No autocomplete for onClick, disabled, etc.// ✅ Extend native button attributestype ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {variant: 'primary' | 'secondary';};function Button({ variant, ...props }: ButtonProps) {return <button {...props} className={`btn btn--${variant}`} />;}// Full autocomplete for all button attributes!
ComponentPropsWithoutRef and ComponentPropsWithRef:
// ✅ ComponentPropsWithoutRef - for components without reftype InputProps = React.ComponentPropsWithoutRef<'input'> & {label: string;error?: string;};function Input({ label, error, ...props }: InputProps) {return (<div><label>{label}</label><input {...props} />{error && <span>{error}</span>}</div>);}// ✅ ComponentPropsWithRef - for components with reftype ButtonProps = React.ComponentPropsWithRef<'button'> & {variant: 'primary' | 'secondary';};const Button = forwardRef<HTMLButtonElement, ButtonProps>(({ variant, ...props }, ref) => {return <button ref={ref} {...props} className={`btn btn--${variant}`} />;},);
Generic components are powerful but easy to mistype. Proper constraints and polymorphic patterns make them type-safe.
Common pitfalls:
// ❌ No constraints - T could be anythingfunction List<T>({ items }: { items: T[] }) {return (<ul>{items.map((item) => (<li>{item}</li>))}</ul>);}// Error: 'item' is of type 'unknown'
Proper constraint usage:
// ✅ Constrain T to have an idtype HasId = {id: string | number;};function List<T extends HasId>({ items }: { items: T[] }) {return (<ul>{items.map((item) => (<li key={item.id}>{JSON.stringify(item)}</li>))}</ul>);}
Polymorphic component pattern (the "as" prop):
// ✅ Polymorphic componenttype BoxProps<T extends React.ElementType = 'div'> = {as?: T;children: React.ReactNode;}function Box<T extends React.ElementType = 'div'>({as,children,...props}: BoxProps<T> & Omit<React.ComponentPropsWithoutRef<T>, keyof BoxProps<T>>) {const Component = as || 'div';return <Component {...props}>{children}</Component>;}// Usage<Box>Default div</Box><Box as="section">Section element</Box><Box as="a" href="/home">Link element with href autocomplete!</Box>
Flexible Table component:
type Column<T> = {key: keyof T;header: string;render?: (value: T[keyof T], item: T) => React.ReactNode;};type TableProps<T extends Record<string, any>> = {data: T[];columns: Column<T>[];};function Table<T extends Record<string, any>>({data,columns,}: TableProps<T>) {return (<table><thead><tr>{columns.map((col) => (<th key={String(col.key)}>{col.header}</th>))}</tr></thead><tbody>{data.map((item, idx) => (<tr key={idx}>{columns.map((col) => (<td key={String(col.key)}>{col.render? col.render(item[col.key], item): String(item[col.key])}</td>))}</tr>))}</tbody></table>);}
Union types are common in React (loading states, conditional rendering), but TypeScript needs help narrowing them.
Not properly narrowing union types:
// ❌ TypeScript can't narrow the typetype State =| { status: 'loading' }| { status: 'success'; data: string }| { status: 'error'; error: string };function Display({ state }: { state: State }) {if (state.status === 'success') {return <div>{state.data}</div>; // Error: Property 'data' does not exist on type 'State'}}
Creating custom type guard functions:
// ✅ Type guard functionfunction isSuccessState(state: State,): state is { status: 'success'; data: string } {return state.status === 'success';}function Display({ state }: { state: State }) {if (isSuccessState(state)) {return <div>{state.data}</div>; // ✅ TypeScript knows state has data}}
Using discriminated unions effectively:
// ✅ Discriminated union with type narrowingtype ApiState<T> =| { status: 'idle' }| { status: 'loading' }| { status: 'success'; data: T }| { status: 'error'; error: string };function DataDisplay<T>({ state }: { state: ApiState<T> }) {switch (state.status) {case 'idle':return <div>Click to load</div>;case 'loading':return <Spinner />;case 'success':return <div>{JSON.stringify(state.data)}</div>; // ✅ data is availablecase 'error':return <Error message={state.error} />; // ✅ error is available}}
The "in" operator and typeof checks:
// ✅ Using "in" operatortype Dog = {bark: () => void;};type Cat = {meow: () => void;};type Pet = Dog | Cat;function makeSound(pet: Pet) {if ('bark' in pet) {pet.bark(); // TypeScript knows it's a Dog} else {pet.meow(); // TypeScript knows it's a Cat}}// ✅ Using typeoffunction processValue(value: string | number) {if (typeof value === 'string') {return value.toUpperCase(); // TypeScript knows it's a string} else {return value.toFixed(2); // TypeScript knows it's a number}}
Assuming API shape without types is dangerous. Create response types and consider runtime validation.
Risk of assuming API shape:
// ❌ No typing - runtime errors waiting to happenasync function fetchUser(id: number) {const response = await fetch(`/api/users/${id}`);const user = await response.json();return user; // Type is 'any'}
Generic response type:
// ✅ Type API responsestype ApiResponse<T> = {success: boolean;data?: T;error?: string;};type User = {id: number;name: string;email: string;};async function fetchUser(id: number): Promise<ApiResponse<User>> {try {const response = await fetch(`/api/users/${id}`);const data = await response.json();return { success: true, data };} catch (error) {return { success: false, error: error.message };}}
Runtime validation with Zod:
// ✅ Runtime validation with Zodimport { z } from 'zod';const UserSchema = z.object({id: z.number(),name: z.string(),email: z.string().email(),});type User = z.infer<typeof UserSchema>;async function fetchUser(id: number): Promise<User> {const response = await fetch(`/api/users/${id}`);const data = await response.json();return UserSchema.parse(data); // Throws if data doesn't match schema}
Explicit Promise<T> return types improve clarity and help catch errors early.
Why explicit Promise<T> helps:
// ❌ Implicit return type - unclear what's returnedasync function loadData() {const response = await fetch('/api/data');return response.json();}// ✅ Explicit return type - clear contractasync function loadData(): Promise<{ items: string[] }> {const response = await fetch('/api/data');return response.json();}
Impact on debugging:
// ✅ TypeScript catches mismatches immediatelyasync function getUser(id: number): Promise<User> {const response = await fetch(`/api/users/${id}`);const data = await response.json();return data; // Error if data doesn't match User type}
Not all libraries have good TypeScript support. Learn to augment types when needed.
Understanding @types/* packages:
# Install type definitions for libraries without built-in typesnpm install --save-dev @types/lodashnpm install --save-dev @types/react-router-dom
Module augmentation:
// ✅ Augment existing module typesimport 'react';declare module 'react' {interface CSSProperties {'--custom-property'?: string;}}// Now you can use custom CSS properties<div style={{ '--custom-property': 'value' }} />;
Creating your own type declarations:
// ✅ Declare module for untyped librarydeclare module 'some-untyped-library' {export function doSomething(value: string): number;export interface Config {apiKey: string;timeout?: number;}}
The declare module pattern:
// types/custom.d.tsdeclare module '*.svg' {const content: React.FunctionComponent<React.SVGAttributes<SVGElement>>;export default content;}declare module '*.png' {const value: string;export default value;}
React.FCany for propsComponentPropsWithoutRefuseState for complex statesuseRef with null for DOM refsuseReduceruseContext hooksuseMemo and useCallback when inference failsPick, Omit, Partial) to reduce duplicationReactNode, ReactElement, string)@types/* packages for untyped librariesA proper tsconfig.json is essential for catching errors and enabling the best TypeScript features.
Minimal tsconfig.json:
{"compilerOptions": {"target": "ES2020","lib": ["ES2020", "DOM", "DOM.Iterable"],"jsx": "react-jsx","module": "ESNext","moduleResolution": "bundler","strict": true,"noUncheckedIndexedAccess": true,"esModuleInterop": true,"skipLibCheck": true,"forceConsistentCasingInFileNames": true,"resolveJsonModule": true,"isolatedModules": true,"noEmit": true},"include": ["src"],"exclude": ["node_modules"]}
Why strict mode matters:
Enabling "strict": true activates all strict type-checking options:
strictNullChecks - prevents null/undefined errorsstrictFunctionTypes - ensures function parameter safetystrictBindCallApply - types bind/call/apply correctlynoImplicitAny - requires explicit typesnoImplicitThis - prevents this confusionKey flags explained:
jsx: "react-jsx" - Uses the new JSX transform (React 17+), no need to import Reactjsx: "react" - Classic JSX transform, requires import ReactnoUncheckedIndexedAccess - Makes array access return T | undefined, preventing index errorsmoduleResolution: "bundler" - Modern resolution for Vite/webpack (use "node" for older setups)Top 5 confusing error messages:
What it means: You're trying to use a value where TypeScript expects a different type.
Quick fix:
// Error: Type 'string' is not assignable to type 'number'const age: number = '25';// Fix: Convert the typeconst age: number = parseInt('25');
What it means: You're accessing a property that might not exist.
Quick fix:
// Errorconst name = user.profile.name;// Fix: Use optional chainingconst name = user.profile?.name;// Or: Type guardif (user.profile) {const name = user.profile.name;}
What it means: TypeScript doesn't know about that property.
Quick fix:
// Error: Property 'customProp' does not exist<div customProp="value" />;// Fix: Extend the typedeclare module 'react' {interface HTMLAttributes<T> {customProp?: string;}}
What it means: Your types are too complex or recursive.
Quick fix:
// Simplify complex prop spreading// Instead of spreading everything, be explicitinterface ButtonPropsextends Pick<React.ButtonHTMLAttributes<HTMLButtonElement>,'onClick' | 'disabled' | 'type'> {variant: string;}
What it means: React isn't imported (only needed with old JSX transform).
Quick fix:
// If using "jsx": "react-jsx", you don't need this// If using "jsx": "react", add:import React from 'react';
When to use // @ts-expect-error vs // @ts-ignore:
// ✅ Use @ts-expect-error - fails if error is fixed// @ts-expect-error: Third-party library has wrong typesconst result = poorlyTypedLibrary.method();// ❌ Avoid @ts-ignore - silently ignores errors forever// @ts-ignoreconst result = poorlyTypedLibrary.method();
VS Code extensions:
Useful TypeScript tools:
TypeScript playground:
Recommended Reading:
Practice your skills: Ready to test your knowledge? Head over to GreatFrontEnd's TypeScript Interview Questions to practice and prepare for your next interview.
After mastering these mistakes, you're prepared for these React TypeScript interview questions:
Junior Level:
interface and type in TypeScript?useState hook?ReactNode and ReactElement?Mid Level:
useReducer hook? 8. What's the difference between ComponentPropsWithRef and ComponentPropsWithoutRef?Senior Level:
TypeScript mistakes in React can be frustrating, but they're also learning opportunities. Each any type you replace, each event handler you properly type, and each hook you correctly structure makes your codebase a bit more robust.
The patterns covered here - explicit prop typing, discriminated unions, type guards, and proper generic constraints - are widely used in production codebases. They're not just theoretical best practices, but practical solutions to common problems.
If you're working on an existing codebase, consider starting small. Pick one component that uses any and give it proper types. Try adding discriminated unions to a reducer. Add a type guard to a context hook. Each improvement helps.
As you apply these patterns, you'll likely notice:
Want more practice? Check out GreatFrontEnd's TypeScript interview questions for more TypeScript interview questions on library APIs, utility types, algorithms, and building strong, typed components to prepare for your next interview.

Getting ready for an Angular interview? Whether you're a fresher or an experienced developer, this comprehensive guide will help you prepare for Angular technical interviews at top tech companies.
We've compiled essential Angular interview questions, from basic fundamentals to advanced concepts, complete with detailed answers and real-world examples.
This guide is structured to help both freshers and experienced developers prepare for Angular interviews effectively:
For freshers (0-2 years experience):
For experienced developers (2+ years):
For developers starting their Angular journey, here are the key areas to focus on:
Dive deep into 20 essential basic Angular questions →
For senior developers and architects, the focus shifts to advanced concepts:
Master 25 advanced Angular concepts →
Real-world scenarios you might encounter:
Scenario: "Our Angular application is slow with a large list of items. How would you optimize it?"
Solution:
Scenario: "Design a scalable state management solution for a large Angular application."
Solution:
Scenario: "Implement communication between deeply nested components without prop drilling."
Solution:
Explore more scenario-based Angular questions →
Not Understanding Change Detection
Misusing Observables
Poor Performance Practices
To further enhance your Angular interview preparation:
| Concept | Basic level | Advanced level |
|---|---|---|
| Components | Creation, lifecycle | Performance, architecture |
| Services | Basic DI, HTTP | Custom providers, hierarchical injection |
| State Management | Services, Inputs | Signals, RxJS patterns |
| Performance | Basic optimization | Change detection strategies, bundle optimization |
| Testing | Component tests | E2E, integration testing |
Level up your Angular interview prep with our carefully curated collection of Angular interview questions at GreatFrontEnd. Practice real UI questions from top tech companies and compare your approach with official solutions from experts.

If you're an experienced frontend developer preparing for your next Angular interview, this post is for you. With the evolving features in Angular's ecosystem - from standalone components to Signals, hydration, and zone-less change detection - interviewers now expect deep architectural understanding.
This post is part of our comprehensive Angular Interview Questions and Answers Guide. Here, we'll cover the most asked Angular Interview Questions for Experienced Professionals, including advanced, scenario-based, and performance-related topics.
If you classify yourself as:
then this list of Angular interview questions for experienced professionals will be your ultimate preparation resource.
Below is a curated list of 25 questions that target key areas - architecture, DI internals, change detection, RxJS, and performance optimization.
Angular follows Model-View-ViewModel (MVVM) pattern where:
// ViewModel (Component)export class UserComponent {users$ = this.userService.getUsers(); // Model interactionconstructor(private userService: UserService) {}deleteUser(id: string) {this.userService.deleteUser(id).subscribe();}}
The component acts as the ViewModel, managing state and handling user interactions while the template renders the view.
An NgModule in Angular is a container that groups related code-components, directives, pipes, and services-into a cohesive block of functionality. It helps organize the application into logical modules for better maintainability and reusability.
Each Angular app has at least one root module (AppModule), which bootstraps the app. Other feature modules can be created to encapsulate specific features or functionalities.
import { NgModule } from '@angular/core';import { BrowserModule } from '@angular/platform-browser';import { AppComponent } from './app.component';@NgModule({declarations: [AppComponent], // Components, directives, pipesimports: [BrowserModule], // Other modulesproviders: [], // Servicesbootstrap: [AppComponent], // Root component})export class AppModule {}
Note: From Angular 14 onward, standalone components can be used without NgModules, but NgModules remain useful for grouping imports and providing services in larger apps.
Angular bootstrapping process:
platformBrowserDynamic().bootstrapModule(AppModule)// main.tsplatformBrowserDynamic().bootstrapModule(AppModule).catch((err) => console.error(err));
inject() API improve dependency injection compared to constructor-based DI?The inject() API, introduced in Angular 14, allows dependencies to be injected outside of class constructors, such as inside functions, factory providers, or standalone components. It provides more flexibility and cleaner code compared to constructor-based DI.
Benefits:
// Traditional constructor injectionexport class UserComponent {constructor(private userService: UserService) {}}// Using inject() APIexport class UserComponent {private userService = inject(UserService);// Can be used in functionsloadUsers = () => {const http = inject(HttpClient);return http.get('/api/users');};}
The OnPush change detection strategy improves performance by limiting when Angular checks for changes. Instead of running on every event, it only triggers when:
ChangeDetectorRef.markForCheck())This makes components more predictable and faster, especially in large apps.
How to use: Apply it in the component decorator:
@Component({changeDetection: ChangeDetectionStrategy.OnPush,template: `<div>{{ user.name }}</div>`,})export class UserComponent {@Input() user: User;constructor(private cdr: ChangeDetectorRef) {}updateUser() {// Use immutable updatesthis.user = { ...this.user, name: 'Updated' };this.cdr.detectChanges(); // Manual trigger if needed}}
Tip: Always pass new object references ({...obj} or arrays via spread) to trigger updates in OnPush components.
Lazy loading in Angular means loading feature modules or components only when needed, rather than at app startup. It improves performance by reducing the initial bundle size and speeding up load times - especially useful for large apps with multiple routes.
How to implement (module-based):
users.module.ts)loadChildren:const routes: Routes = [{path: 'users',loadChildren: () =>import('./users/users.module').then(m => m.UsersModule),},];
Standalone component example (Angular 15+):
const routes: Routes = [{path: 'profile',loadComponent: () =>import('./profile/profile.component').then(c => c.ProfileComponent),},];
Use it when: Your app has multiple large sections that aren't always visited (e.g., admin dashboard, reports, settings).
providers and viewProviders?Both providers and viewProviders define services that a component and its children can use - but they differ in scope.
providers - Makes the service available to the component and all its content children (including projected components via <ng-content>)viewProviders - Limits the service to the component's view only (excludes projected content)@Component({selector: 'app-parent',template: `<ng-content></ng-content>`,providers: [LoggerService], // Available to projected childrenviewProviders: [AuthService], // Only for this component's view})export class ParentComponent {}
Ahead-of-Time (AOT) Compilation is the process of compiling Angular templates and TypeScript code at build time, before the application runs in the browser.
Angular CLI uses AOT by default in production builds:
ng build --configuration production
Directives in Angular are classes that add behavior or modify the DOM. There are two main types:
import { Directive, ElementRef, Renderer2, HostListener } from '@angular/core';@Directive({selector: '[appHighlight]'})export class HighlightDirective {constructor(private el: ElementRef, private renderer: Renderer2) {}@HostListener('mouseenter') onMouseEnter() {this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', 'yellow');}@HostListener('mouseleave') onMouseLeave() {this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', 'transparent');}}**Usage:**```html<p appHighlight>Hover over me!</p>
Structural directives use * syntax and manipulate the DOM.
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';@Directive({selector: '[appUnless]'})export class UnlessDirective {constructor(private templateRef: TemplateRef<any>, private vcRef: ViewContainerRef) {}@Input() set appUnless(condition: boolean) {if (!condition) {this.vcRef.createEmbeddedView(this.templateRef);} else {this.vcRef.clear();}}}
Usage:
<p *appUnless="isVisible">I am visible only when isVisible is false</p>
declarations@Input and @Output to make directives flexible and configurableSignals and Observables both handle reactive data in Angular but differ in scope and use case.
import { signal } from '@angular/core';const count = signal(0);count.set(count() + 1);
Renderer2 work, and why is direct DOM access discouraged in Angular?Renderer2 is an Angular service that provides safe, platform-independent methods to manipulate the DOM (create elements, set styles, listen to events) without touching the browser APIs directly.
import { Directive, ElementRef, Renderer2, HostListener } from '@angular/core';@Directive({selector: '[appHighlight]'})export class HighlightDirective {constructor(private el: ElementRef, private renderer: Renderer2) {}@HostListener('mouseenter') onMouseEnter() {this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', 'yellow');}@HostListener('mouseleave') onMouseLeave() {this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', 'transparent');}}
Avoid direct DOM access (e.g., document.querySelector) because it breaks Angular's platform abstraction and may fail in SSR or web worker contexts.
Tree-shakable providers are services in Angular that are provided at the root level using providedIn: 'root' or in a standalone component/service, allowing unused services to be removed from the final bundle during build.
providersimport { Injectable } from '@angular/core';@Injectable({providedIn: 'root' // Tree-shakable provider})export class AuthService {login() { /* ... */ }}
Only services actually used in the app are included in the production bundle, optimizing performance and load time.
Angular uses a hierarchical DI system: injectors are organized in a tree, and services are resolved top-down.
Hierarchy:
providedIn: 'root')providers or viewProvidersToken Resolution: Angular checks the component injector first, then moves up the tree to module and root injectors. If the service is not found, an error is thrown.
@Injectable({ providedIn: 'root' })export class ApiService {}@Component({selector: 'app-child',template: `<p>Child Component</p>`,providers: [ChildService]})export class ChildComponent {constructor(private api: ApiService, private childService: ChildService) {}}
ApiService → root injector, ChildService → component injector.
Optional, Self, SkipSelf), and how are they used?Resolution modifiers control how Angular resolves dependencies in the injector hierarchy.
@Optional()null instead of throwing an errorconstructor(@Optional() private logger?: LoggerService) {}
@Self()constructor(@Self() private localService: LocalService) {}
@SkipSelf()constructor(@SkipSelf() private parentService: ParentService) {}
In large Angular applications, distant components (not parent-child) can share data using services with observables or signals.
import { Injectable } from '@angular/core';import { BehaviorSubject } from 'rxjs';@Injectable({ providedIn: 'root' })export class SharedService {private messageSource = new BehaviorSubject<string>('Hello');currentMessage$ = this.messageSource.asObservable();updateMessage(msg: string) {this.messageSource.next(msg);}}
Component A (sender):
this.sharedService.updateMessage('New Message');
Component B (receiver):
this.sharedService.currentMessage$.subscribe(msg => console.log(msg));
import { signal, effect } from '@angular/core';export const sharedSignal = signal('Hello');// Component AsharedSignal.set('New Message');// Component Beffect(() => {console.log(sharedSignal());});
InjectionToken, and when would you use it?An InjectionToken is a unique token used to inject non-class values (e.g., strings, objects) into Angular's DI system.
Example:
import { InjectionToken, Inject } from '@angular/core';export const API_URL = new InjectionToken<string>('API_URL');@NgModule({providers: [{ provide: API_URL, useValue: 'https://api.example.com' }]})export class AppModule {}@Component({ /* ... */ })export class ApiService {constructor(@Inject(API_URL) private apiUrl: string) {}}
Use for: Configuration objects, strings, functions, or any non-class dependencies.
HttpClient or routing modules?HttpClient or RoutingUse Angular's testing modules to mock HTTP requests and routes without real calls.
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';TestBed.configureTestingModule({imports: [HttpClientTestingModule]});
Use HttpTestingController to mock requests and provide test data.
import { RouterTestingModule } from '@angular/router/testing';TestBed.configureTestingModule({imports: [RouterTestingModule.withRoutes([{ path: 'home', component: MyComponent }])]});
Use RouterTestingModule to simulate navigation and test route-related logic.
Pipes transform data in templates and can be pure or impure.
@Pipe({ name: 'pureExample', pure: true })export class PureExamplePipe implements PipeTransform {transform(value: string) { return value.toUpperCase(); }}
@Pipe({ name: 'impureExample', pure: false })export class ImpureExamplePipe implements PipeTransform {transform(value: string) { return value.toUpperCase(); }}
Directives in Angular modify the DOM or element behavior. They are of two types: structural and attribute.
* syntax*ngIf, *ngFor<p *ngIf="isVisible">Visible only when isVisible is true</p>
ngClass, ngStyle, custom directives<div [ngClass]="{ active: isActive }">Content</div>
*ngFor for large listsinput, scroll)shareReplay, take, debounceTime@Component({selector: 'app-list',template: `<div *ngFor="let item of items; trackBy: trackById">{{ item.name }}</div>`,changeDetection: ChangeDetectionStrategy.OnPush})export class ListComponent {@Input() items: Item[] = [];trackById(index: number, item: Item) {return item.id;}}
Regular profiling and change detection strategy adjustments help maintain smooth performance in large Angular apps.
trackBy in *ngFor, and how does it improve rendering?trackBy helps Angular identify which items changed, preventing unnecessary DOM re-rendering.
@Component({template: `<div *ngFor="let user of users; trackBy: trackByUserId">{{ user.name }}</div>`,})export class UserListComponent {users = [{ id: 1, name: 'John' },{ id: 2, name: 'Jane' },];// Only re-renders changed items instead of recreating all DOM nodestrackByUserId(index: number, user: User): number {return user.id;}}
Benefits: Reduces DOM manipulation, improves performance for large lists, preserves component state during updates.
combineLatest, withLatestFrom, and forkJoin in RxJS.These operators handle multiple observables differently:
combineLatest - emits latest values from all observables whenever any emitscombineLatest([obs1, obs2]).subscribe(([a,b]) => console.log(a,b));
withLatestFrom - emits when the source observable emits, combining it with the latest from other observablesobs1.pipe(withLatestFrom(obs2)).subscribe(([a,b]) => console.log(a,b));
forkJoin - waits for all observables to complete, then emits the last values as an arrayforkJoin([obs1, obs2]).subscribe(([a,b]) => console.log(a,b));
Use: combineLatest for real-time updates, withLatestFrom to combine with source events, forkJoin for parallel one-time operations.
Signal effects are reactive side-effects that run automatically whenever a signal value changes similar to useEffect in React. They let you respond to state changes outside the template.
import { signal, effect } from '@angular/core';const count = signal(0);effect(() => {console.log(`Count changed: ${count()}`);});count.set(1); // Triggers effect, logs "Count changed: 1"
This error occurs when Angular detects a change to a value after change detection has run.
ngAfterViewInit or ngAfterContentInit directlysetTimeout or Promise: Delay updates to the next microtask cycle if necessaryngAfterViewInit() {setTimeout(() => {this.value = newValue; // Updates safely after change detection});}
The key is to update values before or after change detection, not during.
NgZone and how do you optimize applications using runOutsideAngular()?NgZone manages Angular's change detection, automatically triggering it when async tasks (e.g., timers, events) complete.
runOutsideAngular() to execute code without triggering change detection, e.g., for high-frequency events like scrolling or animations.import { Component, NgZone } from '@angular/core';@Component({ selector: 'app-scroll', template: `<div>Scroll Demo</div>` })export class ScrollComponent {constructor(private ngZone: NgZone) {this.ngZone.runOutsideAngular(() => {window.addEventListener('scroll', this.onScroll);});}onScroll() {console.log('Scrolling...');}}
This reduces unnecessary change detection cycles, improving performance in heavy or frequently updating scenarios.
These Angular scenario-based interview questions for experienced professionals test problem-solving in real-world situations.
When an Angular app becomes slow because of frequent change detection, you can debug and optimize using the following steps.
@Component({selector: 'app-list',templateUrl: './list.component.html',changeDetection: ChangeDetectionStrategy.OnPush})export class ListComponent {}
<div *ngFor="let item of items; trackBy: trackById">{{ item.name }}</div>
this.ngZone.runOutsideAngular(() => {window.addEventListener('scroll', this.onScroll);});
Migrating from RxJS to Angular Signals requires careful planning to maintain reactivity and performance while reducing boilerplate.
BehaviorSubject, Observable, Subject) in components and servicesBehaviorSubject / Observable used for UI state with signals:import { signal } from '@angular/core';export class CounterComponent {count = signal(0);increment() {this.count.set(this.count() + 1);}}
import { computed } from '@angular/core';total = computed(() => this.items().reduce((sum, i) => sum + i.value, 0));
.subscribe() in templates and componentsimport { effect } from '@angular/core';effect(() => {console.log('Count changed:', this.count());});
mergeMap, combineLatest, keep RxJS to avoid complex refactorsYou can manage shared state using services with RxJS/Signals or NgRx depending on app complexity.
BehaviorSubject / signal to hold state and provide getters/setters.@Injectable({ providedIn: 'root' })export class DashboardService {// RxJSprivate widgetsSubject = new BehaviorSubject<Widget[]>([]);widgets$ = this.widgetsSubject.asObservable();updateWidgets(widgets: Widget[]) {this.widgetsSubject.next(widgets);}// Signals (Angular 16+)widgets = signal<Widget[]>([]);}
Usage in components:
this.dashboardService.widgets$.subscribe(widgets => { ... });// orthis.dashboardService.widgets.set(newWidgets);
// Actionexport const loadWidgets = createAction('[Dashboard] Load Widgets');// Reducerexport const dashboardReducer = createReducer(initialState,on(loadWidgets, state => ({ ...state, loading: true })));// Selectorexport const selectWidgets = (state: AppState) => state.dashboard.widgets;
Components: Subscribe via store.select(selectWidgets) and dispatch actions to update state.
CanActivate to restrict routes based on authentication or roles@Injectable({ providedIn: 'root' })export class AuthGuard implements CanActivate {constructor(private auth: AuthService, private router: Router) {}canActivate(): boolean {if (this.auth.isLoggedIn()) return true;this.router.navigate(['/login']);return false;}}
Usage in routing module:
{ path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] }
@Injectable()export class AuthInterceptor implements HttpInterceptor {constructor(private auth: AuthService) {}intercept(req: HttpRequest<any>, next: HttpHandler) {const token = this.auth.getToken();const authReq = req.clone({ setHeaders: { Authorization: `Bearer ${token}` } });return next.handle(authReq);}}
Register interceptor:
providers: [{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }]
If your Angular app's production build is large (e.g., 3MB), you can optimize it using these strategies:
{ path: 'dashboard', loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule) }
ng build --prod --optimization --build-optimizer
Combining lazy loading, tree-shaking, and asset optimization can significantly reduce bundle size.
To add Server-Side Rendering (SSR) to an existing Angular Single Page Application (SPA), I would use Angular Universal, which enables SSR for Angular apps. The steps are:
ng add @nguniversal/express-engine
This sets up a server-side app module (app.server.module.ts) and configures an Express server (server.ts) to render Angular on the server.
window, document, and localStorage - are only used in the browser contextisPlatformBrowser from @angular/commonnpm run build:ssrnpm run serve:ssr
This serves the pre-rendered HTML from the server, improving SEO and initial load performance.
Meta and Title services for better indexingResult: Users and search engines receive fully rendered HTML on the first request, improving SEO, performance, and crawlability of the Angular app.
To break down a monolithic Angular app into feature modules, I would follow these steps:
Identify Features:
Analyze the app and group related functionality (components, services, and routes) into logical features like UserModule, DashboardModule, ProductsModule, etc.
Create Feature Modules: Use the Angular CLI or manually create modules:
ng generate module feature-name --route feature-name --module app.module
This sets up lazy-loaded feature modules if needed.
providedIn: FeatureModule if appropriate)feature-name-routing.module.ts) to handle internal routes{ path: 'dashboard', loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule) }
SharedModuleCoreModule imported only once in AppModuleWhen structuring unit tests for complex component hierarchies, I follow these practices:
TestBed to create a testing module for the component@Input() bindings, @Output() events, template rendering, and user interactionsdescribe blocksResult: This approach ensures each component's behavior is tested reliably while keeping tests fast, maintainable, and isolated from unrelated parts of the hierarchy.
These questions push beyond fundamentals - they're frequent in senior developer and tech lead interviews.
Angular creates a single root injector for eagerly-loaded code, while each lazy-loaded route gets its own child injector.
providedIn: 'root' → one instance in root; providedIn: 'any' → one per injector (root and each lazy)@Injectable({ providedIn: 'any' })export class FeatureService {}// Eager context and each lazy module will get different instancesconstructor(private s: FeatureService) {}
This scoping helps isolate features and avoid cross-feature state leaks while keeping true singletons in root.
Hydration attaches Angular runtime to server-rendered HTML without re-rendering it. The DOM remains; Angular wires up event listeners and state.
// main.ts (standalone app)bootstrapApplication(AppComponent, {providers: [provideClientHydration()],});// Template: defer non-critical islands to reduce hydration cost@defer (on viewport) {<heavy-widget />} @placeholder { Loading... }
Standalone components remove NgModule indirection-dependencies are imported where used, and providers are scoped closer to usage.
providers reduce accidental singletons@Component({standalone: true,selector: 'user-card',imports: [CommonModule],template: `{{ user.name }}`,providers: [UserLocalLogger],})export class UserCard {@Input() user!: User;}bootstrapApplication(AppComponent, {providers: [provideRouter(routes)],});
// Zoneless bootstrap (no global auto change detection)bootstrapApplication(AppComponent, { ngZone: 'noop' });@Component({ changeDetection: ChangeDetectionStrategy.OnPush })export class Cmp {// Prefer Signals or async pipe; call cdr.markForCheck() when bridging imperative codeconstructor(private cdr: ChangeDetectorRef) {}}
Use zoneless with Signals and async pipe for most UI; use runOutsideAngular() for high-frequency events.
Implement PreloadingStrategy to decide per-route preloading (e.g., based on flags or network conditions).
@Injectable({ providedIn: 'root' })export class SelectivePreloading implements PreloadingStrategy {preload(route: Route, load: () => Observable<any>) {return route.data?.['preload'] ? load() : of(null);}}const routes: Routes = [{ path: 'admin', loadChildren: () => import('./admin/admin.routes'), data: { preload: true } },{ path: 'reports', loadChildren: () => import('./reports/reports.routes') },];provideRouter(routes, withPreloading(SelectivePreloading));
You can enrich logic with navigator.connection, user roles, or time-based delays.
source-map-analyzer to cut bundle sizes?// angular.json (excerpt){"configurations": {"production": {"budgets": [{ "type": "initial", "maximumWarning": "500kb", "maximumError": "2mb" },{ "type": "anyComponentStyle", "maximumWarning": "150kb" }]}}}
Workflow:
ng build --configuration production --stats-jsonnpx source-map-explorer dist/**/*.js (or webpack bundle analyzer)providedIn: 'root'/tree-shakable APIssubscribe() without unsubscribe → prefer async pipe or takeUntilDestroyed()switchMap, concatMap)shareReplay({ refCount: true, bufferSize: 1 }))catchError with fallbacksimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';this.form.valueChanges.pipe(debounceTime(300),switchMap(v => this.api.search(v)),takeUntilDestroyed(this.destroyRef)).subscribe(result => this.results.set(result));
Prefer async pipe in templates; for shared streams, cache with shareReplay(1).
Core stages: install → lint → test → build (SPA/SSR) → artifact → deploy.
# .github/workflows/ci.yml (simplified)name: Angular CIon: [push, pull_request]jobs:build:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v4- uses: actions/setup-node@v4with: { node-version: 20, cache: npm }- run: npm ci- run: npm run lint- run: npm test -- --watch=false --browsers=ChromeHeadless- run: npm run build:ssr # e.g., ng build && ng run app:server- uses: actions/upload-artifact@v4with: { name: dist, path: dist/ }# Deploy job can download artifact and push to hosting (Firebase, Vercel, Azure, etc.)
Include budgets in CI to block regressions; for SSR, validate hydration with e2e smoke tests before deploy.
trackBy in *ngFor to minimize DOM re-rendersasync pipe or takeUntilDestroyed()Renderer2 for cross-platform DOM-safe operationsFollowing these keeps senior developers' Angular apps maintainable and performant in large-scale setups
Here are a few resources to expand your prep:
Angular has matured rapidly with standalone APIs, fine-grained reactivity using Signals, and better SSR tooling. Interviewers now test practical knowledge - not syntax, but design reasoning and architectural excellence.
If your goal is to ace Angular Experienced Interview Questions, practice code samples, profile performance, and be ready to justify your design choices like a senior engineer.
Level up your Angular interview prep with our carefully curated collection of Angular interview questions at GreatFrontEnd. Practice real UI questions from top tech companies and compare your approach with official solutions from experts.

Got an Angular interview coming up? Whether you're new to Angular or brushing up before your Angular interview, this post is for you.
This post is part of our comprehensive Angular Interview Questions and Answers Guide, covering fundamental Angular concepts about - components, services, data binding, routing - and a few modern essentials like Signals and standalone components.
If you're looking for Angular basic interview questions or preparing for Angular interview questions for freshers, you're in the right place.
Let's jump in.
Angular is a modern, open-source framework developed by Google using TypeScript that allows developers to build dynamic, modern single-page web applications.
It provides:
Angular components are the core building blocks of any Angular application. Each component manages a specific section of the user interface called a view.
A component is made up of three key parts:
@Component, links the class with its template, selector, and stylesComponents are self-contained, reusable, and testable, forming a tree of nested components that make up the entire application.
Code example:
import { Component } from '@angular/core';@Component({selector: 'app-welcome',template: `<h2>Welcome, {{ name }}!</h2>`,styles: [`h2 {color: #1976d2;}`,],})export class WelcomeComponent {name = 'Angular Developer';}
Common follow-up: What's the difference between a component's template and templateUrl?
NgModule is a class decorated with @NgModule that defines an Angular module - a container grouping related components, directives, pipes, and services into a cohesive unit.
It helps:
Key properties in @NgModule include:
declarations - components, directives, and pipes in this moduleimports - other modules to use their featuresproviders - services available for dependency injectionexports - elements made available to other modulesCode example:
import { NgModule } from '@angular/core';import { BrowserModule } from '@angular/platform-browser';import { AppComponent } from './app.component';import { WelcomeComponent } from './welcome.component';@NgModule({declarations: [AppComponent, WelcomeComponent],imports: [BrowserModule],providers: [],bootstrap: [AppComponent],})export class AppModule {}
Since Angular 14, standalone components can work without NgModules, but modules remain useful for organizing imports and providing services.
Data binding in Angular connects the component's data with the template (view), keeping them in sync. It lets you display values, respond to user input, and update the UI automatically.
There are two main types:
Data flows in one direction - either from component → view or view → component.
{{ value }} - shows data[property]="value" - binds DOM properties(event)="handler()" - listens to user actions<h3>{{ title }}</h3><!-- Interpolation --><img [src]="imageUrl" /><!-- Property binding --><button (click)="onClick()">Click</button><!-- Event binding -->
Data flows both ways - updates in the view reflect in the component and vice versa.
Uses the [(ngModel)] directive (requires FormsModule).
<input [(ngModel)]="username" /><p>Hello, {{ username }}!</p>
export class AppComponent {title = 'Data Binding Example';imageUrl = 'logo.png';username = '';onClick() {alert(`Hello ${this.username || 'Angular Dev'}!`);}}
Directives in Angular are classes that add behavior to elements. They let you manipulate the DOM, change appearance, or modify structure.
Types of directives:
Code example:
<!-- Component Directive --><app-greeting [name]="userName"></app-greeting><!-- Structural Directive --><div *ngIf="isVisible">Visible content</div><!-- Attribute Directive --><div [ngClass]="{'highlight': isHighlighted}">Styled content</div>
// Parent Componentimport { Component } from '@angular/core';@Component({selector: 'app-example',templateUrl: './example.component.html',})export class ExampleComponent {userName = 'Nitesh';isVisible = true;isHighlighted = false;}// Child Component (Component Directive)@Component({selector: 'app-greeting',template: `<p>Hello, {{ name }}!</p>`,})export class GreetingComponent {name = '';}
A Service is a class decorated with @Injectable() that contains business logic and data operations shared across multiple components.
Key features:
Common uses:
Code example:
import { Injectable } from '@angular/core';import { HttpClient } from '@angular/common/http';@Injectable({providedIn: 'root',})export class DataService {constructor(private http: HttpClient) {}getData() {return this.http.get('/api/data');}}
Usage in component:
export class MyComponent {constructor(private dataService: DataService) {}loadData() {this.dataService.getData().subscribe((data) => {// Handle data});}}
Common follow-up: What's the difference between providedIn: 'root' and adding a service to the providers array in a module?
Dependency Injection (DI) is a design pattern where Angular automatically provides dependencies (like services) to components instead of components creating them manually.
Key benefits:
How it works:
@Injectable() and providedInCode example:
// Service@Injectable({providedIn: 'root', // Makes the service a singleton available throughout the app})export class AuthService {isLoggedIn(): boolean {return true;}}// Componentexport class HeaderComponent {constructor(private authService: AuthService) {// authService is now an instance provided by Angular's DI}checkAuth() {return this.authService.isLoggedIn();}}
Angular creates the AuthService instance automatically and injects it into any component that needs it.
Common follow-up: What are the benefits of using Dependency Injection?
TypeScript is a superset of JavaScript that adds static typing and modern features. Angular is built with TypeScript by default.
Benefits for Angular:
Example:
interface User {id: number;name: string;email: string;}export class UserComponent {user: User = {id: 1,name: 'John Doe',email: 'john@example.com',};}
Angular Router enables navigation between different views/components in a single-page application.
Key concepts:
Basic setup:
// app-routing.module.tsconst routes: Routes = [{ path: 'home', component: HomeComponent },{ path: 'about', component: AboutComponent },{ path: '', redirectTo: '/home', pathMatch: 'full' },];
<!-- app.component.html --><nav><a routerLink="/home">Home</a><a routerLink="/about">About</a></nav><router-outlet></router-outlet>
Pipes transform data in templates without changing the original data. They're used for formatting display values.
Built-in pipes:
Usage:
<p>{{ today | date:'short' }}</p><p>{{ price | currency:'USD' }}</p><p>{{ name | uppercase }}</p><p>{{ user | json }}</p>
Custom pipe example:
import { Pipe, PipeTransform } from '@angular/core';@Pipe({ name: 'reverse' })export class ReversePipe implements PipeTransform {transform(value: string): string {return value.split('').reverse().join('');}}
Usage: <p>{{ 'Angular' | reverse }}</p> outputs "ralugnA"
Lifecycle hooks are methods that Angular calls at specific moments in a component's lifecycle.
Common hooks:
Example:
export class MyComponent implements OnInit, OnDestroy {ngOnInit() {console.log('Component initialized');}ngOnDestroy() {console.log('Component destroyed');// Cleanup subscriptions}}
Common follow-up: When should you use ngOnDestroy to clean up resources?
Event binding listens to DOM events and responds to user actions like clicks, key presses, or mouse movements.
Syntax: (event)="handler()"
Examples:
<button (click)="onClick()">Click me</button><input (keyup)="onKeyUp($event)" /><div (mouseenter)="onMouseEnter()">Hover me</div>
export class MyComponent {onClick() {console.log('Button clicked!');}onKeyUp(event: any) {console.log('Key pressed:', event.target.value);}onMouseEnter() {console.log('Mouse entered!');}}
Property binding sets DOM element properties dynamically using component data.
Syntax: [property]="expression"
Examples:
<img [src]="imageUrl" [alt]="imageAlt" /><button [disabled]="isDisabled">Submit</button><div [class.active]="isActive">Content</div><input [value]="inputValue" />
export class MyComponent {imageUrl = 'logo.png';imageAlt = 'Company Logo';isDisabled = false;isActive = true;inputValue = 'Default text';}
Interpolation displays component data in templates using double curly braces {{ }}.
Features:
Examples:
<h1>{{ title }}</h1><p>Welcome, {{ user.name }}!</p><p>Total: {{ price * quantity }}</p><p>Current time: {{ getCurrentTime() }}</p>
export class MyComponent {title = 'My App';user = { name: 'John' };price = 10;quantity = 2;getCurrentTime() {return new Date().toLocaleTimeString();}}
Signals (introduced in Angular 16) are a reactive primitive for managing state and change detection. They provide a simpler, more performant way to handle reactive data.
Key features:
Basic usage:
import { Component, signal, computed } from '@angular/core';@Component({selector: 'app-counter',template: `<p>Count: {{ count() }}</p><p>Double: {{ doubleCount() }}</p><button (click)="increment()">Increment</button>`,})export class CounterComponent {// Create a signalcount = signal(0);// Computed signal - automatically updatesdoubleCount = computed(() => this.count() * 2);increment() {// Update signal valuethis.count.update((value) => value + 1);}}
Signals improve Angular's reactivity model and are the future direction of the framework.
Standalone components (introduced in Angular 14+) are components that don't require NgModules. They simplify Angular applications by allowing components to be self-contained.
Key benefits:
Example:
import { Component } from '@angular/core';import { CommonModule } from '@angular/common';import { FormsModule } from '@angular/forms';@Component({selector: 'app-user',standalone: true,imports: [CommonModule, FormsModule],template: `<h2>{{ userName }}</h2><input [(ngModel)]="userName" />`,})export class UserComponent {userName = 'John';}
Angular 17+ introduced a new built-in control flow syntax with @if, @for, and @switch to replace structural directives like *ngIf and *ngFor.
Key features:
Examples:
<!-- Conditional rendering with @if -->@if (isLoggedIn) {<p>Welcome back!</p>} @else {<p>Please log in</p>}<!-- Loop with @for -->@for (user of users; track user.id) {<div>{{ user.name }}</div>} @empty {<p>No users found</p>}<!-- Switch statement -->@switch (status) { @case ('pending') { <span>Pending...</span> } @case('complete') { <span>Done!</span> } @default { <span>Unknown</span> } }
export class MyComponent {isLoggedIn = true;users = [{ id: 1, name: 'Alice' },{ id: 2, name: 'Bob' },];status = 'pending';}
Angular Forms handle user input, validation, and form submission. There are two approaches:
Template-driven forms:
FormsModuleReactive forms:
ReactiveFormsModuleTemplate-driven example:
<form #userForm="ngForm" (ngSubmit)="onSubmit(userForm)"><input name="username" [(ngModel)]="user.username" required /><button type="submit" [disabled]="!userForm.valid">Submit</button></form>
export class MyComponent {user = { username: '' };onSubmit(form: any) {if (form.valid) {console.log(this.user);}}}
Reactive forms example:
import { FormBuilder, Validators } from '@angular/forms';export class MyComponent {userForm = this.fb.group({username: ['', Validators.required],email: ['', [Validators.required, Validators.email]],});constructor(private fb: FormBuilder) {}onSubmit() {if (this.userForm.valid) {console.log(this.userForm.value);}}}
@Input and @Output decorators enable communication between parent and child components.
@Input - passes data from parent to child:
// Child Componentexport class ChildComponent {@Input() userName: string = '';}
<!-- Parent Template --><app-child [userName]="parentName"></app-child>
@Output - sends events from child to parent:
// Child Componentexport class ChildComponent {@Output() notify = new EventEmitter<string>();sendMessage() {this.notify.emit('Hello from child!');}}
<!-- Parent Template --><app-child (notify)="handleMessage($event)"></app-child>
// Parent ComponenthandleMessage(message: string) {console.log(message);}
Observables are a key part of Angular's reactive programming approach, used extensively for handling asynchronous operations.
Key concepts:
Basic example:
import { Observable } from 'rxjs';export class DataComponent {constructor(private http: HttpClient) {}getData(): void {// HTTP returns an Observablethis.http.get('/api/users').subscribe({next: (data) => console.log(data),error: (error) => console.error(error),complete: () => console.log('Request completed'),});}}
Common RxJS operators: map, filter, switchMap, debounceTime
Remember: Always unsubscribe from Observables in ngOnDestroy to prevent memory leaks (except for HTTP requests which auto-complete).
Avoid these common pitfalls that can cost you in interviews:
ngOnDestroy to prevent memory leaks (except HTTP calls).// ❌ BadngOnInit() {this.dataService.getData().subscribe(data => this.data = data);}// ✅ Goodsubscription: Subscription;ngOnInit() {this.subscription = this.dataService.getData().subscribe(data => this.data = data);}ngOnDestroy() {this.subscription?.unsubscribe();}
[], (), and {{}} syntax{{ }} for interpolation[property] for property binding(event) for event binding[(ngModel)] for two-way bindingprovidedIn: 'root' creates app-wide singleton; providers[] creates instance per module/component.*ngIf and *ngFor@if, @for, and standalone components are now standard.ngOnInit for initialization, constructor only for dependency injection.Here's a rapid-fire review of key concepts:
| Concept | Key point |
|---|---|
| Components | Building blocks with template, class, and metadata |
| Modules | Container for organizing related components (less common with standalone) |
| Data Binding | One-way ({{ }}, [], ()) and two-way ([()]) |
| Services | Reusable business logic, injected via DI |
| Dependency Injection | Angular provides dependencies automatically |
| Directives | Add behavior to DOM elements |
| Pipes | Transform data in templates |
| Lifecycle Hooks | ngOnInit, ngOnDestroy, ngOnChanges, etc. |
| Router | Navigate between views in SPA |
| Forms | Template-driven (simple) vs Reactive (complex) |
| Observables | Handle async operations with RxJS |
| Signals | Modern reactive primitive (Angular 16+) |
| Standalone | Components without NgModules (Angular 14+) |
| Control Flow | @if, @for, @switch syntax (Angular 17+) |
Remember, every Angular developer started exactly where you are now. The fact that you're here preparing shows you're already ahead of the curve. Take a deep breath, trust in the work you've put in, and let your passion for learning shine through in your interview. You've got this! 🚀
Level up your Angular interview prep with our carefully curated collection of Angular interview questions at GreatFrontEnd. Practice real UI questions from top tech companies and compare your approach with official solutions from experts.

Preparing for a React interview can be daunting, but having access to the right questions can make all the difference. We've created a prioritized list of the top 100 questions and solutions, covering essential topics like React fundamentals, React Hooks, React Router, internationalization in React, and testing of React apps.
This comprehensive guide is designed to help you prepare effectively, boost your confidence, and ensure you make a strong impression during your interview.
If you're looking for more in-depth React interview preparation materials, also check out these resources:
Mastering React fundamentals is crucial in front end interviews because most companies rely on React for building modern web applications, and interview questions often test your ability to reason about components, state management, and data flow. A solid grasp of these core concepts is an important step to achieving interview success.
React is a JavaScript library developed by Facebook for creating user interfaces, particularly in single-page applications. It enables the use of reusable components that manage their own state. Key advantages include a component-driven architecture, optimized updates through the virtual DOM, a declarative approach for better readability, and robust community backing.

Find in-depth explanations and track study progress here ->
JSX, short for JavaScript XML, is a syntax extension for JavaScript that allows you to write HTML-like code within JavaScript. It makes building React components easier. JSX gets converted into JavaScript function calls, often by Babel. For instance, <div>Hello, world!</div> is transformed into React.createElement('div', null, 'Hello, world!').

Find in-depth explanations and track study progress here ->
The virtual DOM is a simplified version of the actual DOM used by React. It allows for efficient UI updates by comparing the virtual DOM to the real DOM and making only the necessary changes through a process known as reconciliation.
Find in-depth explanations and track study progress here ->
The virtual DOM in React is an in-memory representation of the real DOM. When state or props change, React creates a new virtual DOM tree, compares it to the previous one using a diffing algorithm, and efficiently updates only the parts of the real DOM that changed.
Find in-depth explanations and track study progress here ->
A React Node refers to any unit that can be rendered in React, such as an element, string, number, or null. A React Element is an immutable object that defines what should be rendered, typically created using JSX or React.createElement. A React Component is either a function or class that returns React Elements, enabling the creation of reusable UI components.
Find in-depth explanations and track study progress here ->
React Fragments allow you to group multiple elements without adding extra nodes to the DOM. They are particularly useful when you need to return multiple elements from a component but don't want to wrap them in a container element. You can utilize shorthand syntax <>...</> or React.Fragment.
return (<><ChildComponent1 /><ChildComponent2 /></>);
Find in-depth explanations and track study progress here ->
key prop in React?In React, the key prop is used to uniquely identify elements in a list, allowing React to optimize rendering by updating and reordering items more efficiently. Without unique keys, React might re-render elements unnecessarily, causing performance problems and potential bugs.
{items.map((item) => <ListItem key={item.id} value={item.value} />);}
Find in-depth explanations and track study progress here ->
Using array indices as keys can lead to performance issues and unexpected behavior, especially when reordering or deleting items. React relies on keys to identify elements uniquely, and using indices can cause components to be re-rendered unnecessarily or display incorrect data.
Find in-depth explanations and track study progress here ->
Props (short for properties) are inputs to React components that allow you to pass data from a parent component to a child component. They are immutable and are used to configure a component. In contrast, state is internal to a component and can change over time, typically due to user interactions or other events.
Find in-depth explanations and track study progress here ->
Class components are ES6 classes that extend React.Component and can hold state, lifecycle methods, and other features. Functional components are simpler, functional JavaScript components that take props as input and return JSX. With the introduction of hooks, functional components can now manage state and lifecycle methods, making them more versatile.
Class components are useful when you need to manage state, use lifecycle methods, or optimize performance through shouldComponentUpdate. However, with the introduction of hooks, functional components can now handle state and lifecycle methods, making them a preferred choice for most use cases due to their simplicity and readability.
React Fiber is a complete rewrite of the React core algorithm, designed to improve performance and enable new features like async rendering, error boundaries, and incremental rendering. It breaks down the rendering process into smaller chunks, allowing React to pause, abort, or prioritize updates as needed.
Find in-depth explanations and track study progress here ->
Reconciliation is the process by which React updates the DOM to match the virtual DOM efficiently. It involves comparing the new virtual DOM tree with the previous one and determining the minimum number of changes required to update the actual DOM. This process ensures optimal performance by avoiding unnecessary re-renders.

Find in-depth explanations and track study progress here ->
The Shadow DOM is a web standard that encapsulates a part of the DOM, isolating it from the rest of the document. It's used for creating reusable, self-contained components without affecting the global styles or scripts.
The Virtual DOM is an in-memory representation of the actual DOM used to optimize rendering. It compares the current and previous states of the UI, updating only the necessary parts of the DOM, which improves performance.
In controlled components, form data is managed through the component's state, making it the definitive source of truth. Input value changes are handled by event handlers. In uncontrolled components, the form state is managed internally and accessed via refs. Controlled components provide more control and are easier to test, while uncontrolled components are simpler for basic use cases.
Example of a controlled component:
function ControlledInput() {const [value, setValue] = React.useState('');return (<inputtype="text"value={value}onChange={(e) => setValue(e.target.value)}/>);}
Example of an uncontrolled component:
function UncontrolledInput() {const inputRef = React.useRef();return <input type="text" ref={inputRef} />;}
Find in-depth explanations and track study progress here ->
Lifting state up in React involves moving the state from child components to their nearest common ancestor. This pattern is used to share state between components that don't have a direct parent-child relationship. By lifting state up, you can avoid prop drilling and simplify the management of shared data.
// Lifting state upconst Parent = () => {const [counter, setCounter] = useState(0);return (<div><Child1 counter={counter} /><Child2 setCounter={setCounter} /></div>);};const Child1 = ({ counter }) => <h1>{counter}</h1>;const Child2 = ({ setCounter }) => (<button onClick={() => setCounter((prev) => prev + 1)}>Increment</button>);
In this example, the state is managed in the Parent component, and both child components access it via props.
Pure Components in React are components that only re-render when their props or state change. They use shallow comparison to check if the props or state have changed, preventing unnecessary re-renders and improving performance.
React.PureComponent to become pureReact.memo for the same effectconst PureFunctionalExample = React.memo(function ({ value }) {return <div>{value}</div>;});
createElement and cloneElement?The difference between createElement and cloneElement in React is as follows:
createElement:React.createElement('div', { className: 'container' }, 'Hello World');
cloneElement:const element = <button className="btn">Click Me</button>;const clonedElement = React.cloneElement(element, { className: 'btn-primary' });
PropTypes in React?PropTypes in React is used for type-checking props passed to components, ensuring the correct data types are used and warning developers during development.
import PropTypes from 'prop-types';function MyComponent({ name, age }) {return (<div>{name} is {age} years old</div>);}MyComponent.propTypes = {name: PropTypes.string.isRequired,age: PropTypes.number.isRequired,};
It helps catch errors early, improving code quality. However, modern React components tend to be written in TypeScript and prop type mismatches can be caught at compile time rather than at runtime.
Stateless components in React are components that do not manage or hold any internal state. They simply receive data via props and render UI based on that data. These components are often functional components and are used for presentational purposes.
this.statepropsfunction StatelessComponent({ message }) {return <div>{message}</div>;}
Stateless components are simpler, easier to test, and often more reusable. With the introduction of hooks, React components are mostly written using functions and can contain state via the useState hook.
Stateful components in React are components that manage and hold their own internal state. They can modify their state in response to user interactions or other events and re-render themselves when the state changes.
this.state (in class components) or useState (in functional components)function StatefulComponent() {const [count, setCount] = React.useState(0);return (<div><p>{count}</p><button onClick={() => setCount(count + 1)}>Increment</button></div>);}
Stateful components are essential for handling dynamic and interactive UIs.
The recommended ways for static type checking in React are:
TypeScript: A superset of JavaScript that adds optional static typing. It provides strong type checking, autocompletion, and other benefits at development time.
interface MyComponentProps {name: string;age: number;}function MyComponent({ name, age }: MyComponentProps) {return <div>{name} is {age} years old</div>;}
PropTypes: A runtime type-checking tool for React props, primarily for development purposes. It helps catch errors by checking prop types during development but doesn't offer full static analysis like TypeScript.
import PropTypes from 'prop-types';function MyComponent({ name, age }) {return (<div>{name} is {age} years old</div>);}MyComponent.propTypes = {name: PropTypes.string.isRequired,age: PropTypes.number.isRequired,};
TypeScript is the preferred choice for static type checking due to its integration with the build process and comprehensive tooling, whereas PropTypes is useful for smaller projects or when you need runtime checks.
React advises against mutating state as it can lead to unexpected behaviors and bugs. State immutability helps efficiently determine when components need re-rendering; direct mutations may prevent React from detecting changes.
Find in-depth explanations and track study progress here ->
Mastering React hooks is important in front end interviews because hooks are the standard way to manage state, side effects, and component lifecycle in modern React. Demonstrating a solid understanding of hooks shows you can write clean, functional components and solve complex problems without relying on outdated class patterns.
Hooks enable the use of state and other React features in functional components, replacing the need for class components. They streamline code by reducing the reliance on lifecycle methods, enhance readability, and facilitate the reuse of stateful logic across components.
Popular hooks like useState and useEffect are used for managing state and side effects.
Find in-depth explanations and track study progress here ->
React hooks should be called at the top level of a function, not inside loops, conditions, or nested functions. They must only be used within React function components or custom hooks. These guidelines ensure proper state management and lifecycle behavior.
Find in-depth explanations and track study progress here ->
useEffect and useLayoutEffect in React?useEffect and useLayoutEffect both handle side effects in React functional components but differ in when they run:
useEffect runs asynchronously after the DOM has rendered, making it suitable for tasks like data fetching or subscriptions.useLayoutEffect runs synchronously after DOM updates but before the browser paints, ideal for tasks like measuring DOM elements or aligning the UI with the DOM. Example:import React, { useEffect, useLayoutEffect, useRef } from 'react';function Example() {const ref = useRef();useEffect(() => {console.log('useEffect: Runs after DOM paint');});useLayoutEffect(() => {console.log('useLayoutEffect: Runs before DOM paint');console.log('Element width:', ref.current.offsetWidth);});return <div ref={ref}>Hello</div>;}
Find in-depth explanations and track study progress here ->
useEffect affect?The dependency array of useEffect controls when the effect re-runs:
Find in-depth explanations and track study progress here ->
useRef hook in React and when should it be used?The useRef hook creates a mutable object that persists through renders, allowing direct access to DOM elements, storing mutable values without causing re-renders, and maintaining references to values.
For instance, useRef can be utilized to focus on an input element:
import React, { useRef, useEffect } from 'react';function TextInputWithFocusButton() {const inputEl = useRef(null);useEffect(() => {inputEl.current.focus();}, []);return <input ref={inputEl} type="text" />;}
Find in-depth explanations and track study progress here ->
setState() in React class components and when should it be used?The callback function format of setState() in React ensures that state updates are based on the most current state and props. This is essential when the new state depends on the previous state. Instead of passing an object directly to setState(), you provide a function that takes the previous state and props as arguments, returning the updated state.
this.setState((prevState, props) => ({counter: prevState.counter + props.increment,}));
Using this approach helps avoid issues related to asynchronous updates, ensuring that your state reflects the latest values accurately.
Find in-depth explanations and track study progress here ->
useCallback hook in React and when should it be used?The useCallback hook memoizes functions to prevent their recreation on every render. This is especially beneficial when passing callbacks to optimized child components that depend on reference equality to avoid unnecessary renders. Use it when a function is passed as a prop to a child component.
const memoizedCallback = useCallback(() => {doSomething(a, b);}, [a, b]);
Find in-depth explanations and track study progress here ->
useMemo hook in React and when should it be used?The useMemo hook memoizes costly calculations, recomputing them only when dependencies change. This enhances performance by avoiding unnecessary recalculations. It should be used for computationally intensive functions that don't need to run on every render.
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
Find in-depth explanations and track study progress here ->
useReducer hook in React and when should it be used?The useReducer hook manages complex state logic in functional components, serving as an alternative to useState. It's ideal when state has multiple fields (and there are constraints around how they should be mutated), or when the next state relies on the previous one.
The useReducer hook accepts a reducer function + an initial state. The reducer function is passed the current state and action and returns a new state.
const [state, dispatch] = useReducer(reducer, initialState);
Find in-depth explanations and track study progress here ->
useId hook in React and when should it be used?The useId hook generates unique IDs for elements within a component, which is crucial for accessibility by dynamically creating ids that can be used for linking form inputs and labels. It guarantees unique IDs across the application even if the component renders multiple times.
import { useId } from 'react';function MyComponent() {const id = useId();return (<div><label htmlFor={id}>Name:</label><input id={id} type="text" /></div>);}
Find in-depth explanations and track study progress here ->
To create and use custom hooks in React:
useState or useEffectExample:
function useForm(initialState) {const [formData, setFormData] = useState(initialState);const handleChange = (e) =>setFormData({ ...formData, [e.target.name]: e.target.value });return [formData, handleChange];}
Use the Hook:
function MyForm() {const [formData, handleChange] = useForm({ name: '', email: '' });return <input name="name" value={formData.name} onChange={handleChange} />;}
Custom hooks let you reuse logic across components, keeping your code clean.
Mastering React's advanced concepts like Suspense, forwardRef(), and context demonstrates that you can handle performance optimization, code splitting, and complex component patterns. In interviews, this shows you're prepared to build scalable, maintainable applications beyond just basic component logic.
In React, re-rendering refers to the process of updating the user interface (UI) in response to changes in the component's state or props. When the state or props of a component change, React re-renders the component to reflect the updated data in the UI.
This involves:

Find in-depth explanations and track study progress here ->
forwardRef() in React used for?forwardRef() allows passing a ref through a component to one of its children. This is useful for accessing a DOM element or child component's instance directly from a parent.
import React, { forwardRef } from 'react';const MyComponent = forwardRef((props, ref) => <input ref={ref} {...props} />);
Find in-depth explanations and track study progress here ->
Error boundaries catch JavaScript errors in their child components, log them, and display fallback UI instead of crashing the application. They utilize componentDidCatch and static getDerivedStateFromError methods but do not catch errors in event handlers or asynchronous code.
Find in-depth explanations and track study progress here ->
React Suspense allows handling asynchronous operations more elegantly within components. It provides fallback content while waiting for resources like data or code to load. You can use it alongside React.lazy() for code splitting.
const LazyComponent = React.lazy(() => import('./LazyComponent'));function MyComponent() {return (<React.Suspense fallback={<div>Loading...</div>}><LazyComponent /></React.Suspense>);}
Find in-depth explanations and track study progress here ->
Hydration involves attaching event listeners and making server-rendered HTML interactive on the client side. After server-side rendering, React initializes dynamic behavior by attaching event handlers.

Find in-depth explanations and track study progress here ->
React Portals allow rendering children into a DOM node outside the parent component's hierarchy. This is useful for modals or tooltips that need to escape parent overflow or z-index constraints.
Find in-depth explanations and track study progress here ->
React Strict Mode is a development feature in React that activates extra checks and warnings to help identify potential issues in your app.
<React.StrictMode><App /></React.StrictMode>
Wrapping components in <React.StrictMode> activates these development checks without affecting production builds.
Code splitting enhances performance by dividing code into smaller chunks loaded on demand, thereby reducing initial load times. This can be achieved through dynamic import() statements or using React's React.lazy and Suspense.
// Using React.lazy and Suspenseconst LazyComponent = React.lazy(() => import('./LazyComponent'));function App() {return (<React.Suspense fallback={<div>Loading...</div>}><LazyComponent /></React.Suspense>);}
Find in-depth explanations and track study progress here ->
Optimizing context performance involves memoizing context values with useMemo, splitting contexts for isolated state changes, and employing selectors to rerender only necessary components.
const value = useMemo(() => ({ state, dispatch }), [state, dispatch]);
Find in-depth explanations and track study progress here ->
The Flux pattern manages application state through unidirectional data flow, simplifying debugging and enhancing maintainability with clear separation of concerns between Dispatcher, Stores, Actions, and Views.

Find in-depth explanations and track study progress here ->
In React, one-way data flow means data moves from parent to child components through props.
Example:
function Parent() {const [count, setCount] = React.useState(0);return <Child count={count} increment={() => setCount(count + 1)} />;}function Child({ count, increment }) {return <button onClick={increment}>Count: {count}</button>;}
This ensures data flows in one direction, making the app more predictable.

Find in-depth explanations and track study progress here ->
Context in React can lead to performance issues if not handled carefully, causing unnecessary re-renders of components that consume the context, even if only part of the context changes. Overusing context for state management can also make the code harder to maintain and understand. It's best to use context sparingly and consider other state management solutions like Redux or Zustand for more complex scenarios.
Find in-depth explanations and track study progress here ->
React anti-patterns are practices that can lead to inefficient or hard-to-maintain code. Common examples include:
setStatecomponentWillMount for data fetchingcomponentWillReceivePropsFind in-depth explanations and track study progress here ->
Choosing between React state, context, and external state managers depends on your application's complexity. Use React state for local component state, context for global state shared across multiple components, and external managers like Redux or MobX for complex state management requiring advanced features like optimizing re-renders.
Find in-depth explanations and track study progress here ->
setState is called in React?When setState is called in React:
Example:
function Counter() {const [count, setCount] = React.useState(0);const increment = () => {setCount(count + 1); // Calls setState to update state};return <button onClick={increment}>Count: {count}</button>;}
In this example, calling setState (via setCount) triggers a re-render with the updated count.
Prop drilling is when you pass data from a parent component to a deeply nested child component through props, even if intermediate components don't use it.
Example:
function Grandparent() {const data = 'Hello from Grandparent';return <Parent data={data} />;}function Parent({ data }) {return <Child data={data} />;}function Child({ data }) {return <p>{data}</p>;}
In this example, data is passed through multiple components, even though only the Child component uses it. Prop drilling is acceptable for small applications where the component hierarchy is shallow. When global state is needed to be accessed in deeper levels of the app, using context and/or external state managers might be better.
Lazy loading in React is a technique where components are loaded only when they are needed, rather than at the initial page load. This helps reduce the initial load time and improve performance by splitting the code into smaller chunks.
Example:
import React, { Suspense, lazy } from 'react';const LazyComponent = lazy(() => import('./LazyComponent'));function App() {return (<Suspense fallback={<div>Loading...</div>}><LazyComponent /></Suspense>);}
In this example, LazyComponent is loaded only when it's rendered, and while loading, a fallback UI (Loading...) is displayed.
Synthetic events in React are a wrapper around native DOM events that ensure consistent behavior across browsers. They normalize the way events are handled, providing a unified API for React applications.
These events are wrapped in the SyntheticEvent object and offer methods like preventDefault() and stopPropagation() to control event behavior. React uses event pooling to reuse event objects, which helps optimize performance.
Example:
function MyComponent() {const handleClick = (event) => {event.preventDefault();console.log('Button clicked');};return <button onClick={handleClick}>Click me</button>;}
In this example, handleClick handles the click event consistently across all browsers using a synthetic event.
React class components have lifecycle methods for different phases:
constructor: Initializes state or binds methodscomponentDidMount: Runs after the component mounts, useful for API calls or subscriptionscomponentDidMount() {console.log('Component mounted');}
shouldComponentUpdate: Determines if the component should re-rendercomponentDidUpdate: Runs after updates, useful for side effectscomponentWillUnmount: Cleans up (e.g., removing event listeners).componentWillUnmount() {console.log('Component will unmount');}
These methods allow you to manage component behavior throughout its lifecycle.
Concurrent Mode allows React to work on multiple tasks simultaneously without blocking the main UI thread. It enables React to prioritize updates and provide smoother rendering for complex applications.
React uses the priority system in Concurrent Mode to schedule updates. It can break up large updates into smaller chunks and give priority to user interactions (like clicks or input) to ensure the app remains responsive.
To avoid blocking the UI, use Web Workers, setTimeout, or requestIdleCallback for offloading heavy computations. Alternatively, break tasks into smaller parts and use React's Suspense or useMemo to only recompute when necessary.
Example using setTimeout for deferring computation:
const [data, setData] = useState(null);useEffect(() => {setTimeout(() => {const result = computeExpensiveData();setData(result);}, 0);}, []);
Server-side rendering (SSR) involves rendering components on the server before sending fully rendered HTML to clients, improving initial load times and SEO through efficient hydration processes.

Find in-depth explanations and track study progress here ->
Static generation pre-renders HTML at build time instead of runtime; this approach enhances performance by delivering static content quickly while improving SEO outcomes.
Find in-depth explanations and track study progress here ->
Higher-order components (HOCs) are functions that take a component and return a new one with added props or behavior, facilitating logic reuse across components.
const withExtraProps = (WrappedComponent) => {return (props) => <WrappedComponent {...props} extraProp="value" />;};const EnhancedComponent = withExtraProps(MyComponent);
Find in-depth explanations and track study progress here ->
In React, the presentational vs container component pattern distinguishes between components that focus on appearance (presentational components) and those that manage logic and state (container components). Presentational components render HTML and CSS, while container components handle data and behavior. This separation leads to a cleaner and more organized codebase.
Find in-depth explanations and track study progress here ->
Render props in React allow code sharing between components through a prop that is a function. This function returns a React element, enabling data to be passed to child components. This technique facilitates logic reuse without relying on higher-order components or hooks.
class DataFetcher extends React.Component {state = { data: null };componentDidMount() {fetch(this.props.url).then((response) => response.json()).then((data) => this.setState({ data }));}render() {return this.props.render(this.state.data);}}// Usage<DataFetcherurl="/api/data"render={(data) => <div>{data ? data.name : 'Loading...'}</div>}/>;
Find in-depth explanations and track study progress here ->
The composition pattern in React involves building components by combining smaller, reusable ones instead of using inheritance. This encourages creating complex UIs by passing components as children or props.
function WelcomeDialog() {return (<Dialog><h1>Welcome</h1><p>Thank you for visiting our spacecraft!</p></Dialog>);}function Dialog(props) {return <div className="dialog">{props.children}</div>;}
Find in-depth explanations and track study progress here ->
To re-render the view on browser resize, use the useEffect hook to listen for the resize event and update state.
Example:
import React, { useState, useEffect } from 'react';function ResizeComponent() {const [windowWidth, setWindowWidth] = useState(window.innerWidth);useEffect(() => {const handleResize = () => setWindowWidth(window.innerWidth);window.addEventListener('resize', handleResize);return () => window.removeEventListener('resize', handleResize);}, []);return <div>Window width: {windowWidth}px</div>;}export default ResizeComponent;
This updates the state and re-renders the component whenever the window is resized.
Asynchronous data loading uses useEffect alongside useState hooks; fetching data inside useEffect updates state with fetched results ensuring re-renders occur with new data.
import React, { useState, useEffect } from 'react';const FetchAndDisplayData = () => {const [info, updateInfo] = useState(null);const [isLoading, toggleLoading] = useState(true);useEffect(() => {const retrieveData = async () => {try {const res = await fetch('https://api.example.com/data');const data = await res.json();updateInfo(data);} catch (err) {console.error('Error fetching data:', err);} finally {toggleLoading(false);}};retrieveData();}, []);return (<div>{isLoading ? (<p>Fetching data, please wait...</p>) : (<pre>{JSON.stringify(info, null, 2)}</pre>)}</div>);};export default FetchAndDisplayData;
Find in-depth explanations and track study progress here ->
Common pitfalls in data fetching with React include failing to handle loading and error states, neglecting to clean up subscriptions which can cause memory leaks, and improperly using lifecycle methods or hooks. Always ensure proper handling of these states, clean up after components, and utilize useEffect for side effects in functional components.
Find in-depth explanations and track study progress here ->
Understanding React Router is important in front end interviews because most real-world applications need client-side routing to handle navigation, dynamic URLs, and nested layouts. Proficiency with routing shows you can structure applications effectively and provide a seamless user experience.
React Router is a popular routing library for React applications that enables navigation between different components based on the URL. It provides declarative routing, allowing you to define routes and their corresponding components in a straightforward manner.
React Router maps URL paths to components, enabling navigation in single-page apps. Dynamic routing allows you to use URL parameters to render components based on dynamic values.
import { BrowserRouter, Routes, Route, useParams } from 'react-router-dom';function UserPage() {const { id } = useParams(); // Access dynamic parameterreturn <h1>User ID: {id}</h1>;}export default function App() {return (<BrowserRouter><Routes><Route path="/user/:id" element={<UserPage />} /> {/* Dynamic path */}</Routes></BrowserRouter>);}
Key features:
:id captures dynamic data from the URL.useParams Hook: Accesses these dynamic values for rendering.Nested routes allow you to create hierarchies of components, and useParams helps access dynamic route parameters.
Key techniques:
<Outlet>: Renders child routes within a parent layoutuseParams: Retrieves route parameters for dynamic routingimport {BrowserRouter,Routes,Route,Outlet,useParams,} from 'react-router-dom';function UserProfile() {const { userId } = useParams();return <h2>User ID: {userId}</h2>;}function App() {return (<BrowserRouter><Routes><Route path="user/:userId" element={<Outlet />}><Route path="profile" element={<UserProfile />} /></Route></Routes></BrowserRouter>);}
BrowserRouter: Uses the HTML5 History API to manage navigation, enabling clean URLs without the hash (#). It requires server-side configuration to handle routes correctly, especially for deep linking.
HashRouter: Uses the hash (#) portion of the URL to simulate navigation. It doesn't require server-side configuration, as the hash is never sent to the server. This makes it suitable for environments where server-side routing isn't possible (e.g., static hosting).
React Router is a routing library for React that provides a declarative API for defining routes and handling navigation. It manages components and URLs.
History library is a lower-level utility that only manages browser history (e.g., pushing and popping history entries). It doesn't handle UI rendering or routing, making it more generic and not React-specific.
React Router uses the history library internally but adds additional features like routing and component management.
<Router> components of React Router v6?In React Router v6, the key <Router> components are:
<BrowserRouter>: Uses the HTML5 history API to keep the UI in sync with the URL. It's commonly used for web applications.<HashRouter>: Uses URL hash fragments (#) to manage routing, making it suitable for static file hosting or legacy browsers that don't support the HTML5 history API.<MemoryRouter>: Keeps the URL in memory (no address bar changes), useful for non-browser environments like tests or embedded apps.<StaticRouter>: Used for server-side rendering (SSR), where routing is handled without a browser, typically in Node.js environments.Each of these routers serves different use cases but provides the same routing functionality within a React app.
The push and replace methods of the history library are used to manage the browser's history stack and control navigation.
push:history.push('/new-page')replace:history.replace('/new-page')In React Router v6, you can navigate programmatically by using the useNavigate hook. First, import useNavigate from react-router-dom and call it to get the navigate function. Then, you can use navigate('/new-page') to navigate to a different route.
For example:
import { useNavigate } from 'react-router-dom';function MyComponent() {const navigate = useNavigate();const goToPage = () => navigate('/new-page');return <button onClick={goToPage}>Go to New Page</button>;}
In React Router v5, the useHistory hook provides access to the history object, which you can use to push a new route. For example, history.push('/new-page') will navigate to the specified route.
For example:
import { useHistory } from 'react-router-dom';function MyComponent() {const history = useHistory();const goToPage = () => history.push('/new-page');return <button onClick={goToPage}>Go to New Page</button>;}
Both methods allow you to navigate programmatically in React Router.
To implement private routes, create a component that checks if the user is authenticated before rendering the desired route.
Example:
import { Navigate } from 'react-router-dom';function PrivateRoute({ children }) {return isAuthenticated ? children : <Navigate to="/login" />;}
PrivateRoute: Checks authentication and either renders the children (protected routes) or redirects to the login page.<Navigate>: Replaces the deprecated <Redirect> for redirecting in React Router v6+.Use the useLocation hook to get the current route, and conditionally apply styles for the active state.
Example:
import { useLocation } from 'react-router-dom';function NavBar() {const location = useLocation();return (<nav><ul><li className={location.pathname === '/home' ? 'active' : ''}>Home</li><li className={location.pathname === '/about' ? 'active' : ''}>About</li></ul></nav>);}
To handle 404 errors or page not found in React Router, create a catch-all route at the end of your route configuration that renders a custom 404 component.
Example:
import { Routes, Route } from 'react-router-dom';function NotFound() {return <h1>404 - Page Not Found</h1>;}function App() {return (<Routes><Route path="/" element={<Home />} /><Route path="/about" element={<About />} /><Route path="*" element={<NotFound />} /></Routes>);}
In this example, the NotFound component is rendered when no other routes match the URL, indicating a 404 error.
In React Router v6, you can use the useSearchParams hook to access query parameters from the URL.
Example:
import { useSearchParams } from 'react-router-dom';function MyComponent() {const [searchParams] = useSearchParams();const queryParam = searchParams.get('paramName');return <div>Query Param: {queryParam}</div>;}
This hook allows you to retrieve and manipulate query parameters in React Router v6.
To perform an automatic redirect after login in React Router, use the useNavigate hook to navigate to the desired route after successful authentication.
Example:
import { useNavigate } from 'react-router-dom';function Login() {const navigate = useNavigate();const handleLogin = () => {// Perform login logicnavigate('/dashboard');};return (<div><button onClick={handleLogin}>Login</button></div>);}
In this example, the handleLogin function navigates to the /dashboard route after successful login.
In React Router v6, you can pass props to a route component using the element prop in the <Route> component.
Example:
import { Routes, Route } from 'react-router-dom';function MyComponent({ propValue }) {return <div>Prop Value: {propValue}</div>;}function App() {return (<Routes><Route path="/my-route" element={<MyComponent propValue="Hello" />} /></Routes>);}
In this example, the propValue prop is passed to the MyComponent component when rendering the /my-route route.
Understanding internationalization (i18n) in React is important in front end interviews because many products serve global audiences and must support multiple languages and locales. Showing you can implement i18n demonstrates attention to accessibility, user experience, and readiness to build applications for diverse users.
Localization typically involves libraries like react-i18next or react-intl. Set up translation files for different languages and configure the library within your app using provided hooks or components.
// Example using react-i18nextimport { useTranslation } from 'react-i18next';const MyComponent = () => {const { t } = useTranslation();return <p>{t('welcome_message')}</p>;};
Find in-depth explanations and track study progress here ->
react-intl?react-intl is a library that provides internationalization (i18n) support for React applications. It helps in formatting numbers, dates, strings, and handling translation/localization. It integrates with the Intl API in JavaScript to provide locale-specific data and translation management.
react-intl?react-intl?<FormattedMessage />, <FormattedNumber />, <FormattedDate />, etc., to format content.useIntl for formatting messages, numbers, or dates imperatively within components.FormattedMessage as a placeholder using react-intl?You can use the FormattedMessage component to handle placeholders within strings. Placeholders are replaced dynamically with variables in the translated string.
import { FormattedMessage } from 'react-intl';function WelcomeMessage() {return (<FormattedMessageid="welcome"defaultMessage="Hello, {name}!"values={{ name: 'John' }}/>);}
Here, {name} is a placeholder, and John will replace it.
You can access the current locale using the useIntl hook or the IntlProvider's locale prop.
Using useIntl:
import { useIntl } from 'react-intl';function LocaleDisplay() {const intl = useIntl();return <div>Current locale: {intl.locale}</div>;}
Using IntlProvider:
<IntlProvider locale="en" messages={messages}><MyComponent /></IntlProvider>
Here, locale="en" defines the current locale.
react-intl?You can format dates using the <FormattedDate /> component or the useIntl hook's formatDate method.
Using <FormattedDate /> component:
import { FormattedDate } from 'react-intl';function DateComponent() {return (<FormattedDatevalue={new Date()}year="numeric"month="long"day="2-digit"/>);}
Using useIntl hook:
import { useIntl } from 'react-intl';function DateComponent() {const intl = useIntl();const formattedDate = intl.formatDate(new Date(), {year: 'numeric',month: 'long',day: '2-digit',});return <div>{formattedDate}</div>;}
These methods allow you to format the date in a locale-sensitive manner.
Understanding testing in React is important in front end interviews because it shows you can write reliable, maintainable code and catch bugs early through unit, integration, and UI tests. Proficiency with tools like Jest and React Testing Library signals that you prioritize code quality and can work effectively in team environments with CI/CD workflows.
Testing React applications can be done using Jest and React Testing Library. Jest serves as the testing framework while React Testing Library provides utilities for testing components similarly to user interactions.
Find in-depth explanations and track study progress here ->
Jest is a JavaScript testing framework that provides a test runner, assertion library, and mocking support. It's commonly used for testing React applications due to its simplicity and integration with tools like React Testing Library.
React Testing Library is a testing utility for React that helps test components in a way that resembles how users interact with the application. It provides functions to render components, interact with them, and assert on the rendered output.
To test React components using React Testing Library, you can:
render.getByText, queryByRole, etc.Example:
import { render, screen, fireEvent } from '@testing-library/react';import MyComponent from './MyComponent';test('renders component', () => {render(<MyComponent />);const button = screen.getByRole('button');fireEvent.click(button);expect(screen.getByText('Clicked!')).toBeInTheDocument();});
In this example, the test renders MyComponent, clicks a button, and asserts that the text 'Clicked!' is present.
To test asynchronous code in React components, you can use async/await with waitFor from React Testing Library to handle asynchronous operations like data fetching or API calls.
Example:
import { render, screen, waitFor } from '@testing-library/react';import MyComponent from './MyComponent';test('fetches data and renders it', async () => {render(<MyComponent />);await waitFor(() => {expect(screen.getByText('Data loaded')).toBeInTheDocument();});});
In this example, the test waits for the data to be loaded before asserting that the text 'Data loaded' is present.
To mock API calls in React component tests, you can use Jest's jest.mock to mock the API module and return mock data. This allows you to simulate API responses without making actual network requests.
Example:
import { render, screen } from '@testing-library/react';jest.mock('./api', () => ({fetchData: jest.fn(() => Promise.resolve('mocked data')),}));import MyComponent from './MyComponent';test('fetches data and renders it', async () => {render(<MyComponent />);expect(screen.getByText('Loading...')).toBeInTheDocument();expect(await screen.findByText('mocked data')).toBeInTheDocument();});
In this example, the fetchData function from the api module is mocked to return 'mocked data' for testing purposes.
To test React hooks in functional components, you can use the renderHook function from @testing-library/react-hooks to render the hook and test its behavior.
Example:
import { renderHook, act } from '@testing-library/react-hooks';import useCounter from './useCounter';test('increments counter', () => {const { result } = renderHook(() => useCounter());act(() => {result.current.increment();});expect(result.current.count).toBe(1);});
In this example, the useCounter hook is tested by rendering it with renderHook and asserting that the counter increments correctly.
To test custom hooks in React, you can use the renderHook function from @testing-library/react-hooks to render the hook and test its behavior.
Example:
import { renderHook, act } from '@testing-library/react-hooks';import useCustomHook from './useCustomHook';test('hook behavior', () => {const { result } = renderHook(() => useCustomHook());act(() => {result.current.doSomething();});expect(result.current.value).toBe('expected value');});
In this example, the useCustomHook hook is tested by rendering it with renderHook and asserting its behavior.
The Shallow Renderer in React testing is a technique that allows you to render a React component without rendering its child components. This helps isolate the component being tested, focusing only on its logic, behavior, and output, without interference from its dependencies. It is useful for testing component states, event handling, and props in isolation.
Here's an example of using Shallow Renderer with Enzyme:
import React from 'react';import { shallow } from 'enzyme';// Example Componentfunction Button({ onClick, label }) {return <button onClick={onClick}>{label}</button>;}// Test using Shallow Rendererdescribe('Button Component', () => {it('renders the button with the correct label', () => {const wrapper = shallow(<Button label="Click Me" />);expect(wrapper.text()).toBe('Click Me'); // Test the button label});it('calls onClick when the button is clicked', () => {const onClickMock = jest.fn(); // Mock functionconst wrapper = shallow(<Button label="Click Me" onClick={onClickMock} />);wrapper.simulate('click'); // Simulate click eventexpect(onClickMock).toHaveBeenCalledTimes(1); // Verify onClick was called});});
Shallow Rendering renders the component without its children, making tests simpler and faster. The shallow function is used to test the component's output, props, and event handling. In the example, the label of the button and the click event handler are tested in isolation, ensuring the component behaves as expected.
Snapshot Testing in React is a testing technique that captures the rendered output of a component and saves it as a snapshot. Subsequent test runs compare the current output with the saved snapshot to detect any unexpected changes. If the output differs from the snapshot, the test fails, indicating that the component's output has changed.
Here's an example of using Snapshot Testing with Jest:
import React from 'react';import renderer from 'react-test-renderer';import MyComponent from './MyComponent';test('renders correctly', () => {const tree = renderer.create(<MyComponent />).toJSON();expect(tree).toMatchSnapshot();});
In this example, the renderer.create function renders the MyComponent and converts it to a JSON tree. The toMatchSnapshot function saves the snapshot of the component's output. Subsequent test runs compare the current output with the saved snapshot, ensuring the component's output remains consistent.
To test React components that use context, you can wrap the component in a context provider with the desired context values for testing. This allows you to simulate the context values and test the component's behavior based on those values.
Example:
import { render } from '@testing-library/react';import { MyContextProvider } from './MyContextProvider';import MyComponent from './MyComponent';test('renders correctly with context', () => {const { getByText } = render(<MyContextProvider value="test value"><MyComponent /></MyContextProvider>,);expect(getByText('test value')).toBeInTheDocument();});
In this example, the MyComponent is wrapped in a MyContextProvider with a specific context value for testing. The test verifies that the component renders correctly with the provided context value.
To test React components that use Redux, you can use the redux-mock-store library to create a mock store with the desired state for testing. This allows you to simulate the Redux store and test the component's behavior based on the state.
Example:
import { render } from '@testing-library/react';import configureStore from 'redux-mock-store';import { Provider } from 'react-redux';import MyComponent from './MyComponent';const mockStore = configureStore([]);test('renders correctly with Redux state', () => {const store = mockStore({ counter: 0 });const { getByText } = render(<Provider store={store}><MyComponent /></Provider>,);expect(getByText('Counter: 0')).toBeInTheDocument();});
In this example, the MyComponent is wrapped in a Provider with a mock Redux store containing the initial state { counter: 0 } for testing. The test verifies that the component renders correctly with the provided Redux state.
The TestRenderer package in React is a utility for rendering components and capturing their output for testing purposes. It provides a simple API for rendering components and inspecting their rendered output, making it easier to write tests for React components.
Example:
import TestRenderer from 'react-test-renderer';import MyComponent from './MyComponent';const renderer = TestRenderer.create(<MyComponent />);const tree = renderer.toJSON();expect(tree).toMatchSnapshot();
In this example, the TestRenderer.create function renders the MyComponent and converts it to a JSON tree. The toMatchSnapshot function saves the snapshot of the component's output for testing.
These 100 questions should give you a good idea on what to expect in React interviews. If you're looking for more in-depth React interview preparation materials, check out these:
You can also explore the Top ReactJS Interview Questions repo - a collection of 50 commonly asked questions compiled from real interview experiences.

JavaScript is an essential skill for anyone pursuing a career in web development, but securing a job in this field can be particularly challenging for newcomers. A critical part of the hiring process is the technical interview, where your JavaScript expertise will be thoroughly evaluated.
To support your preparation and build your confidence, we've put together a list of the top 50 must-know JavaScript interview questions and answers frequently encountered in interviews.
If you're looking for additional JavaScript interview preparation materials, also check out these resources:
Debouncing is a smart way to handle events that fire repeatedly within a short time, such as typing in a search box or resizing a window. Instead of executing a function every single time the event is triggered, debouncing ensures the function runs only after the event stops firing for a specified time.
It prevents performance bottlenecks by reducing the number of unnecessary function calls, making your app smoother and more efficient.
The debounce method delays a function's execution until after a defined "waiting period" has passed since the last event. Let's see an example using Lodash:
import { debounce } from 'lodash';const searchInput = document.getElementById('search-input');const debouncedSearch = debounce(() => {// Perform the search operation hereconsole.log('Searching for:', searchInput.value);}, 300);searchInput.addEventListener('input', debouncedSearch);
While debouncing waits until user activity stops, throttling ensures the function runs at fixed intervals, regardless of how often the event occurs. Each technique suits specific use cases, such as search boxes (debouncing) versus scroll events (throttling).
Practice implementing a Debounce function on GreatFrontEnd ->
Promise.all()Promise.all() is a powerful method in JavaScript that allows you to handle multiple asynchronous tasks simultaneously. It takes an array of promises and returns a single promise that resolves when all the promises resolve, or rejects if any one of them fails.
This method is perfect when you need to wait for several independent asynchronous tasks to finish before proceeding, like fetching data from multiple APIs.
Here's how Promise.all() works with multiple API requests:
const promise1 = fetch('https://api.example.com/data/1');const promise2 = fetch('https://api.example.com/data/2');const promise3 = fetch('https://api.example.com/data/3');Promise.all([promise1, promise2, promise3]).then((responses) => {// Executes only when all promises are resolved.console.log('All responses:', responses);}).catch((error) => {// Catches any error from any promise.console.error('Error:', error);});
Promise.allPractice implementing a Promise.all function on GreatFrontEnd ->
Deep equality involves comparing two objects or arrays to determine if they are structurally identical. Unlike shallow equality, which only checks if object references are the same, deep equality examines whether all nested values are equal.
Here's a simple deepEqual implementation:
function deepEqual(obj1, obj2) {if (obj1 === obj2) return true;if (obj1 == null ||typeof obj1 !== 'object' ||obj2 == null ||typeof obj2 !== 'object')return false;let keys1 = Object.keys(obj1);let keys2 = Object.keys(obj2);if (keys1.length !== keys2.length) return false;for (let key of keys1) {if (!keys2.includes(key) || !deepEqual(obj1[key], obj2[key])) return false;}return true;}// Example usageconst object1 = {name: 'John',age: 30,address: {city: 'New York',zip: '10001',},};const object2 = {name: 'John',age: 30,address: {city: 'New York',zip: '10001',},};console.log(deepEqual(object1, object2)); // true
This function uses recursion to check nested properties, ensuring all values match in both objects or arrays. It's a critical concept for comparing complex data structures in frontend development.
Practice implementing Deep Equal on GreatFrontEnd ->
An EventEmitter is a utility that enables objects to listen for and emit events. It implements the observer pattern, allowing you to subscribe to actions or changes and handle them when triggered. This concept is fundamental in both JavaScript and Node.js for managing event-driven programming.
const eventEmitter = new EventEmitter();// Subscribe to an eventeventEmitter.on('customEvent', (data) => {console.log('Event emitted with data:', data);});// Emit the eventeventEmitter.emit('customEvent', { message: 'Hello, world!' });
EventEmitter allows flexible communication between components, making it useful in scenarios like state management, logging, or real-time updates.
Practice implementing an Event Emitter on GreatFrontEnd ->
Array.prototype.reduce()?Array.prototype.reduce() is a versatile method for iterating through an array and reducing it to a single value. It processes each element with a callback function, carrying over an accumulator to build the final result. Common use cases include summing numbers, flattening arrays, or even building complex objects.
const numbers = [1, 2, 3, 4, 5];const sum = numbers.reduce(function (accumulator, currentValue) {return accumulator + currentValue;}, 0);console.log(sum); // Output: 15
reduce?Practice implementing Array.protoype.reduce on GreatFrontEnd ->
Flattening transforms a nested array into a single-level array, making it more manageable. Since ES2019, JavaScript provides the Array.prototype.flat() method for this.
const nestedArray = [1, [2, [3, [4, [5]]]]];const flatArray = nestedArray.flat(Infinity);console.log(flatArray); // Output: [1, 2, 3, 4, 5]
Here, .flat(Infinity) ensures the entire array is flattened, no matter how deep. For less deeply nested arrays, you can specify the depth.
Before ES2019, custom solutions were common:
// Custom recursive array flattenerfunction flattenArray(arr) {return arr.reduce((acc, val) =>Array.isArray(val) ? acc.concat(flattenArray(val)) : acc.concat(val),[],);}const nestedArray = [1, [2, [3, [4, [5]]]]];const flatArray = flattenArray(nestedArray);console.log(flatArray); // Output: [1, 2, 3, 4, 5]
Practice implementing a flatten function on GreatFrontEnd ->
Merging data is crucial when handling complex structures. JavaScript provides efficient ways to combine objects or arrays.
The spread operator is concise and intuitive for merging objects:
const obj1 = { a: 1, b: 2 };const obj2 = { b: 3, c: 4 };const mergedObj = { ...obj1, ...obj2 };console.log(mergedObj); // Output: { a: 1, b: 3, c: 4 }
Object.assign()Another approach is Object.assign():
const obj1 = { a: 1, b: 2 };const obj2 = { b: 3, c: 4 };const mergedObj = Object.assign({}, obj1, obj2);console.log(mergedObj); // Output: { a: 1, b: 3, c: 4 }
const array1 = [1, 2, 3];const array2 = [4, 5, 6];const mergedArray = [...array1, ...array2];console.log(mergedArray); // Output: [1, 2, 3, 4, 5, 6]
Array.concat()const array1 = [1, 2, 3];const array2 = [4, 5, 6];const mergedArray = array1.concat(array2);console.log(mergedArray); // Output: [1, 2, 3, 4, 5, 6]
For nested objects, you'll need custom logic or libraries:
function deepMerge(target, source) {for (const key in source) {if (source[key] instanceof Object && key in target) {Object.assign(source[key], deepMerge(target[key], source[key]));}}Object.assign(target || {}, source);return target;}const obj1 = { a: 1, b: { x: 10, y: 20 } };const obj2 = { b: { y: 30, z: 40 }, c: 3 };const mergedObj = deepMerge(obj1, obj2);console.log(mergedObj); // Output: { a: 1, b: { x: 10, y: 30, z: 40 }, c: 3 }
Alternatively, libraries like Lodash simplify deep merging:
const _ = require('lodash');const obj1 = { a: 1, b: { x: 10, y: 20 } };const obj2 = { b: { y: 30, z: 40 }, c: 3 };const mergedObj = _.merge({}, obj1, obj2);console.log(mergedObj); // Output: { a: 1, b: { x: 10, y: 30, z: 40 }, c: 3 }
Practice implementing a deep merge function on GreatFrontEnd ->
getElementsByClassNamegetElementsByClassName fetches elements matching a specific class and returns them as a live HTMLCollection.
// Fetch and loop through elementsconst elements = document.getElementsByClassName('example');for (let i = 0; i < elements.length; i++) {console.log(elements[i].textContent);}
You can combine class names for more specific selections:
const elements = document.getElementsByClassName('class1 class2');
HTMLCollection updates automatically if DOM elements are added or removed.
For more complex selectors, use querySelectorAll:
const elements = document.querySelectorAll('.example');
Practice Using getElementsByClassName on GreatFrontEnd ->
Memoization saves computed results to avoid redundant calculations.
function expensiveOperation(n) {console.log('Calculating for', n);return n * 2;}// Memoize functionfunction memoize(func) {const cache = {};return function (n) {if (cache[n] !== undefined) {console.log('From cache for', n);return cache[n];}const result = func(n);cache[n] = result;return result;};}const memoizedExpensiveOperation = memoize(expensiveOperation);console.log(memoizedExpensiveOperation(5)); // Calculating for 5, 10console.log(memoizedExpensiveOperation(5)); // From cache for 5, 10
Libraries like Lodash also provide a memoize utility.
Practice implementing a memoize function on GreatFrontEnd ->
getAccessing nested object properties risk errors if any property is undefined. Tools like Lodash's get or JavaScript's optional chaining (?.) help mitigate this.
const user = { address: { city: 'New York' } };console.log(_.get(user, 'address.city')); // 'New York'console.log(user.address?.city); // 'New York'
These methods safely retrieve nested properties without crashing the program.
Practice implementing a get function on GreatFrontEnd ->
Hoisting refers to how JavaScript moves variable and function declarations to the top of their scope during compilation. While only the declaration is hoisted (not the initialization), understanding hoisting helps in writing cleaner and bug-free code.
varVariables declared with var are hoisted and initialized as undefined. Accessing them before initialization results in undefined.
console.log(foo); // undefinedvar foo = 1;console.log(foo); // 1
let, const, and classVariables declared with let, const, and class are hoisted but exist in a "temporal dead zone" until their declaration is reached, causing a ReferenceError if accessed early.
console.log(y); // ReferenceErrorlet y = 'local';
Both the declaration and definition of functions are hoisted, allowing them to be called before their declaration.
foo(); // 'FOOOOO'function foo() {console.log('FOOOOO');}
For function expressions, only the variable is hoisted, not the function itself.
console.log(bar); // undefinedbar(); // TypeError: bar is not a functionvar bar = function () {console.log('BARRRR');};
Imports are hoisted, making them available throughout the module. However, their initialization happens before the module code executes.
foo.doSomething(); // Works fineimport foo from './modules/foo';
Modern JavaScript uses let and const to avoid hoisting pitfalls. Declare variables at the top of their scope for better readability and use tools like ESLint to enforce best practices:
By following these practices, you can write robust, maintainable code.
Read more about the concept of "Hoisting" on GreatFrontEnd ->
let, var, and const?In JavaScript, let, var, and const are used to declare variables, but they differ in scope, initialization, redeclaration, reassignment, and behavior when accessed before declaration.
Variables declared with var are function-scoped or global, while let and const are block-scoped (confined to the nearest {} block).
if (true) {var foo = 1;let bar = 2;const baz = 3;}console.log(foo); // 1console.log(bar); // ReferenceErrorconsole.log(baz); // ReferenceError
var and let can be declared without initialization, but const requires an initial value.
var a; // Validlet b; // Validconst c; // SyntaxError: Missing initializer
Variables declared with var can be redeclared, but let and const cannot.
var x = 10;var x = 20; // Allowedlet y = 10;let y = 20; // SyntaxError: Identifier 'y' has already been declared
var and let allow reassignment, while const does not.
let a = 1;a = 2; // Allowedconst b = 1;b = 2; // TypeError: Assignment to constant variable
All variables are hoisted, but var initializes to undefined, whereas let and const exist in a "temporal dead zone" until the declaration is reached.
console.log(foo); // undefinedvar foo = 'foo';console.log(bar); // ReferenceErrorlet bar = 'bar';
const for variables that don't change to ensure immutability.let when reassignment is needed.var due to its hoisting and scoping issues.Read more about the differences between let, var, and const on GreatFrontEnd ->
== and === in JavaScript?The == operator checks for equality after performing type conversion, while === checks for strict equality without type conversion.
==)== allows type coercion, which means JavaScript converts values to the same type before comparison. This can lead to unexpected results.
42 == '42'; // true0 == false; // truenull == undefined; // true
===)=== checks both value and type, avoiding the pitfalls of type coercion.
42 === '42'; // false0 === false; // falsenull === undefined; // false
=== for most comparisons as it avoids implicit type conversion and makes code more predictable.== only when comparing null or undefined for simplicity.let x = null;console.log(x == null); // trueconsole.log(x == undefined); // true
Object.is()Object.is() is similar to === but treats -0 and +0 as distinct and considers NaN equal to itself.
console.log(Object.is(-0, +0)); // falseconsole.log(Object.is(NaN, NaN)); // true
=== for strict comparisons to avoid bugs caused by type coercion.Object.is() for nuanced comparisons like distinguishing -0 and +0.Explore the differences between == and === on GreatFrontEnd ->
The event loop is the backbone of JavaScript's asynchronous behavior, enabling single-threaded execution without blocking.
setTimeout and HTTP requests on separate threadssetTimeout and UI eventsPromise callbacks, executed before macrotasksconsole.log('Start');setTimeout(() => console.log('Timeout 1'), 0);Promise.resolve().then(() => console.log('Promise 1'));setTimeout(() => console.log('Timeout 2'), 0);console.log('End');
Output:
StartEndPromise 1Timeout 1Timeout 2
Explanation:
Start, End) run first.Promise 1) follow.Timeout 1, Timeout 2) run last.Explore the event loop in JavaScript on GreatFrontEnd ->
Event delegation is an efficient way to manage events for multiple elements by attaching a single event listener to their common parent.
event.target to determine the clicked element.// HTML:// <ul id="item-list">// <li>Item 1</li>// <li>Item 2</li>// </ul>const itemList = document.getElementById('item-list');itemList.addEventListener('click', (event) => {if (event.target.tagName === 'LI') {console.log(`Clicked on ${event.target.textContent}`);}});
Explore event delegation in JavaScript on GreatFrontEnd ->
this works in JavaScriptThe value of this depends on how a function is called. Let's explore its different behaviors.
Using new: When creating objects, this refers to the newly created object.
function Person(name) {this.name = name;}const person = new Person('Alice');console.log(person.name); // 'Alice'
Using apply, call, or bind: Explicitly sets this to a specified object.
function greet() {console.log(this.name);}const person = { name: 'Alice' };greet.call(person); // 'Alice'
Method call: this refers to the object the method is called on.
const obj = {name: 'Alice',greet() {console.log(this.name);},};obj.greet(); // 'Alice'
Free function call: Defaults to the global object (window in browsers) or undefined in strict mode.
function greet() {console.log(this); // global object or undefined}greet();
Arrow functions: Capture this from their enclosing scope.
const obj = {name: 'Alice',greet: () => {console.log(this.name); // Inherits `this` from enclosing scope},};obj.greet(); // undefined
thisArrow functions simplify usage by capturing this from their lexical scope.
function Timer() {this.seconds = 0;setInterval(() => {this.seconds++;console.log(this.seconds);}, 1000);}const timer = new Timer();
Explore how this works in JavaScript on GreatFrontEnd ->
sessionStorage, and localStorage apart?When it comes to client-side storage, cookies, localStorage, and sessionStorage serve distinct roles:
// Set a cookie with an expiry datedocument.cookie = 'userId=12345; expires=Fri, 31 Dec 2025 23:59:59 GMT; path=/';// Read all cookiesconsole.log(document.cookie);// Delete a cookiedocument.cookie = 'userId=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';
localStorage// Store data in localStoragelocalStorage.setItem('username', 'john_doe');// Retrieve dataconsole.log(localStorage.getItem('username'));// Remove an itemlocalStorage.removeItem('username');// Clear all localStorage datalocalStorage.clear();
sessionStoragelocalStorage (around 5MB).// Store data in sessionStoragesessionStorage.setItem('sessionId', 'abcdef');// Retrieve dataconsole.log(sessionStorage.getItem('sessionId'));// Remove an itemsessionStorage.removeItem('sessionId');// Clear all sessionStorage datasessionStorage.clear();
Learn more about cookies, sessionStorage, and localStorage on GreatFrontEnd ->
<script>, <script async>, and <script defer> differ?<script>When using the <script> tag without attributes, it fetches and executes the script immediately, pausing HTML parsing.
Use case: Critical scripts needed before page rendering.
<script src="main.js"></script>
<script async>With async, the script loads in parallel to HTML parsing and executes as soon as it's ready.
Use case: Independent scripts like analytics or ads.
<script async src="analytics.js"></script>
<script defer>When using defer, the script loads alongside HTML parsing but only executes after the HTML is fully parsed.
Use Case: Scripts that rely on a complete DOM structure.
<script defer src="deferred.js"></script>
Discover more about <script>, <script async>, and <script defer> on GreatFrontEnd ->
null, undefined?Variables not defined using var, let, or const are considered undeclared and can cause global scope issues.
undefinedA declared variable that hasn't been assigned a value is undefined.
nullRepresents the intentional absence of any value. It's an explicit assignment. Example Code:
let a;console.log(a); // undefinedlet b = null;console.log(b); // nulltry {console.log(c); // ReferenceError: c is not defined} catch (e) {console.log('c is undeclared');}
Read more about null, undefined, and undeclared variables on GreatFrontEnd ->
.call() vs .apply()?Both .call and .apply let you invoke a function with a specified this value. The key difference lies in how arguments are passed:
.call: Accepts arguments as a comma-separated list..apply: Accepts arguments as an array.Memory aid:
function sum(a, b) {return a + b;}console.log(sum.call(null, 1, 2)); // 3console.log(sum.apply(null, [1, 2])); // 3
Learn more about .call and .apply on GreatFrontEnd ->
Function.prototype.bind work?The bind method is used to create a new function with a specific this value and, optionally, preset arguments. This ensures that the function always has the correct this context, regardless of how or where it's called.
bind:this is correctly set for the function.const john = {age: 42,getAge: function () {return this.age;},};console.log(john.getAge()); // 42const unboundGetAge = john.getAge;console.log(unboundGetAge()); // undefinedconst boundGetAge = john.getAge.bind(john);console.log(boundGetAge()); // 42const mary = { age: 21 };const boundGetAgeMary = john.getAge.bind(mary);console.log(boundGetAgeMary()); // 21
Explore Function.prototype.bind on GreatFrontEnd ->
Using arrow functions for methods in constructors automatically binds the this context to the constructor, avoiding the need to manually bind it. This eliminates issues caused by this referring to unexpected contexts.
const Person = function (name) {this.name = name;this.sayName1 = function () {console.log(this.name);};this.sayName2 = () => {console.log(this.name);};};const john = new Person('John');const dave = new Person('Dave');john.sayName1(); // Johnjohn.sayName2(); // Johnjohn.sayName1.call(dave); // Davejohn.sayName2.call(dave); // John
Arrow functions are particularly useful in React class components, ensuring methods maintain the correct context when passed to child components.
Explore the advantage for using the arrow syntax for a method in a constructor on GreatFrontEnd ->
Prototypal inheritance is a way for objects to share properties and methods through their prototype chain.
null.new to create objects.function Animal(name) {this.name = name;}Animal.prototype.sayName = function () {console.log(`My name is ${this.name}`);};function Dog(name, breed) {Animal.call(this, name);this.breed = breed;}Dog.prototype = Object.create(Animal.prototype);Dog.prototype.bark = function () {console.log('Woof!');};let fido = new Dog('Fido', 'Labrador');fido.bark(); // "Woof!"fido.sayName(); // "My name is Fido"
Explore how prototypal inheritance works on GreatFrontEnd ->
function Person(){}, const person = Person(), and const person = new Person()?function Person(){}: A function declaration, typically used for constructors if written in PascalCase.const person = Person(): Calls the function normally and assigns the result to person. No object creation happens unless explicitly returned.const person = new Person(): Invokes the function as a constructor, creating a new object and setting its prototype to Person.prototype.function foo() {}foo(); // "Hello!"function foo() {console.log('Hello!');}
var foo = function() {}foo(); // TypeError: foo is not a functionvar foo = function () {console.log('Hello!');};
Here are various approaches to creating objects in JavaScript:
Object literals: The simplest and most common way to create an object is using curly braces {} with key-value pairs.
const person = {firstName: 'John',lastName: 'Doe',};
Object constructor: Use the built-in Object constructor with the new keyword.
const person = new Object();person.firstName = 'John';person.lastName = 'Doe';
Object.create() method: Create an object with a specific prototype.
const personPrototype = {greet() {console.log(`Hello, my name is ${this.name}.`);},};const person = Object.create(personPrototype);person.name = 'John';person.greet(); // Hello, my name is John.
ES2015 classes: Define objects using the class syntax for a blueprint-like structure.
class Person {constructor(name, age) {this.name = name;this.age = age;}greet() {console.log(`Hi, I'm ${this.name} and I'm ${this.age} years old.`);}}const john = new Person('John', 30);john.greet(); // Hi, I'm John and I'm 30 years old.
Constructor functions: Use a function as a template for creating multiple objects.
function Person(name, age) {this.name = name;this.age = age;}const john = new Person('John', 30);console.log(john.name); // John
Explore various ways to create objects in JavaScript on GreatFrontEnd ->
A higher-order function is a function that either:
Accepts another function as an argument:
function greet(name) {return `Hello, ${name}!`;}function greetUser(greeter, name) {console.log(greeter(name));}greetUser(greet, 'Alice'); // Hello, Alice!
Returns another function:
function multiplier(factor) {return function (num) {return num * factor;};}const double = multiplier(2);console.log(double(4)); // 8
Explore the definition of a higher-order function on GreatFrontEnd ->
ES5 constructor functions use function constructors and prototypes for object creation and inheritance.
function Person(name, age) {this.name = name;this.age = age;}Person.prototype.greet = function () {console.log(`Hi, I'm ${this.name} and I'm ${this.age} years old.`);};const john = new Person('John', 30);john.greet(); // Hi, I'm John and I'm 30 years old.
ES2015 Classes use the class keyword for cleaner and more intuitive syntax.
class Person {constructor(name, age) {this.name = name;this.age = age;}greet() {console.log(`Hi, I'm ${this.name} and I'm ${this.age} years old.`);}}const john = new Person('John', 30);john.greet(); // Hi, I'm John and I'm 30 years old.
static in ES2015.extends and super keywords in ES2015.Explore differences between ES2015 classes and ES5 constructor functions on GreatFrontEnd ->
Event bubbling is the process where an event triggers on the target element and then propagates upwards through its ancestors in the DOM.
const parent = document.getElementById('parent');const child = document.getElementById('child');parent.addEventListener('click', () => {console.log('Parent clicked');});child.addEventListener('click', () => {console.log('Child clicked');});
Clicking the child element will log both "Child clicked" and "Parent clicked" due to bubbling.
Use event.stopPropagation() to prevent the event from propagating upwards.
child.addEventListener('click', (event) => {event.stopPropagation();console.log('Child clicked only');});
Explore event bubbling on GreatFrontEnd ->
Event capturing, also called "trickling", is the reverse of bubbling. The event propagates from the root element down to the target element.
Capturing is enabled by passing { capture: true } to addEventListener() as the third argument.
const parent = document.getElementById('parent');const child = document.getElementById('child');parent.addEventListener('click',() => {console.log('Parent capturing');},{ capture: true },);child.addEventListener('click', () => {console.log('Child clicked');});
Clicking the child will log "Parent capturing" first, followed by "Child clicked."
Explore event capturing on GreatFrontEnd ->
mouseenter and mouseover differ?mouseentermouseoverExplore the differences between mouseenter and mouseover on GreatFrontEnd ->
const fs = require('fs');const data = fs.readFileSync('file.txt', 'utf8');console.log(data); // Blocks until the file is fully readconsole.log('Program ends');
console.log('Start');fetch('https://api.example.com/data').then((response) => response.json()).then((data) => console.log(data)) // Non-blocking.catch((error) => console.error(error));console.log('End');
Explore the difference between synchronous and asynchronous functions on GreatFrontEnd ->
AJAX (Asynchronous JavaScript and XML) is a technique that allows web pages to fetch and send data asynchronously, enabling dynamic updates without reloading the entire page.
XMLHttpRequest; fetch() is the modern alternative.XMLHttpRequest:let xhr = new XMLHttpRequest();xhr.onreadystatechange = function () {if (xhr.readyState === XMLHttpRequest.DONE) {if (xhr.status === 200) {console.log(xhr.responseText);} else {console.error('Request failed');}}};xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos/1', true);xhr.send();
fetch():fetch('https://jsonplaceholder.typicode.com/todos/1').then((response) => response.json()).then((data) => console.log(data)).catch((error) => console.error('Fetch error:', error));
Explore AJAX in detail on GreatFrontEnd ->
Explore the advantages and disadvantages of using AJAX on GreatFrontEnd ->
XMLHttpRequest and fetch()?XMLHttpRequestonprogress.onerror event.let xhr = new XMLHttpRequest();xhr.open('GET', 'https://example.com/api', true);xhr.onload = function () {if (xhr.status === 200) {console.log(xhr.responseText);}};xhr.send();
fetch().catch() for better error management.AbortController for cancellations.fetch('https://example.com/api').then((response) => response.json()).then((data) => console.log(data)).catch((error) => console.error(error));
fetch() has cleaner syntax and better Promise integration.XMLHttpRequest supports progress tracking, which fetch() does not.Explore the differences between XMLHttpRequest and fetch() on GreatFrontEnd ->
JavaScript features a mix of primitive and non-primitive (reference) data types.
true or false.Tip: Use the typeof operator to determine the type of a variable.
Explore the various data types in JavaScript on GreatFrontEnd ->
JavaScript provides multiple ways to iterate over objects and arrays.
for...inLoops over all enumerable properties, including inherited ones.
for (const property in obj) {if (Object.hasOwn(obj, property)) {console.log(property);}}
Object.keys()Retrieves an array of an object's own enumerable properties.
Object.keys(obj).forEach((key) => console.log(key));
Object.entries()Returns an array of [key, value] pairs.
Object.entries(obj).forEach(([key, value]) => console.log(`${key}: ${value}`));
Object.getOwnPropertyNames()Includes both enumerable and non-enumerable properties.
Object.getOwnPropertyNames(obj).forEach((prop) => console.log(prop));
for LoopClassic approach for iterating through arrays:
for (let i = 0; i < arr.length; i++) {console.log(arr[i]);}
Array.prototype.forEach()Executes a callback for each array item.
arr.forEach((element, index) => console.log(element, index));
for...ofIdeal for looping through iterable objects like arrays.
for (const element of arr) {console.log(element);}
Array.prototype.entries()Iterates with both index and value.
for (const [index, element] of arr.entries()) {console.log(index, ':', element);}
Explore iteration techniques on GreatFrontEnd ->
...)The spread operator is used to expand elements of arrays or objects.
Copying arrays/objects:
const array = [1, 2, 3];const newArray = [...array]; // [1, 2, 3]
Merging arrays/objects:
const arr1 = [1, 2];const arr2 = [3, 4];const mergedArray = [...arr1, ...arr2]; // [1, 2, 3, 4]
Passing function arguments:
const nums = [1, 2, 3];console.log(Math.max(...nums)); // 3
...)The rest operator collects multiple elements into an array or object.
Function parameters:
function sum(...numbers) {return numbers.reduce((a, b) => a + b);}sum(1, 2, 3); // 6
Destructuring:
const [first, ...rest] = [1, 2, 3];console.log(rest); // [2, 3]
Explore spread and rest syntax on GreatFrontEnd ->
Mapsize property.const map = new Map();map.set('key', 'value');console.log(map.size); // 1
Object.keys(), Object.values(), or Object.entries().size property.const obj = { key: 'value' };console.log(Object.keys(obj).length); // 1
Explore the difference between Map and plain objects on GreatFrontEnd ->
Map/Set and WeakMap/WeakSetWeakMap and WeakSet keys must be objects, while Map and Set accept any data type.WeakMap and WeakSet allow garbage collection of keys, making them useful for managing memory.Map and Set have a size property.WeakMap and WeakSet are not iterable.// Map Exampleconst map = new Map();map.set({}, 'value');console.log(map.size); // 1// WeakMap Exampleconst weakMap = new WeakMap();let obj = {};weakMap.set(obj, 'value');obj = null; // Key is garbage-collected
Explore the differences between Map/Set and WeakMap/WeakSet on GreatFrontEnd ->
Arrow functions simplify function syntax, making them ideal for inline callbacks.
// Traditional function syntaxconst numbers = [1, 2, 3, 4, 5];const doubledNumbers = numbers.map(function (number) {return number * 2;});console.log(doubledNumbers); // [2, 4, 6, 8, 10]// Arrow function syntaxconst doubledWithArrow = numbers.map((number) => number * 2);console.log(doubledWithArrow); // [2, 4, 6, 8, 10]
Explore a use case for the new arrow function syntax on GreatFrontEnd ->
A callback is a function passed as an argument to another function, executed after the completion of an asynchronous task.
function fetchData(callback) {setTimeout(() => {const data = { name: 'John', age: 30 };callback(data);}, 1000);}fetchData((data) => {console.log(data); // { name: 'John', age: 30 }});
Explore the concept of a callback function in asynchronous operations on GreatFrontEnd ->
Debouncing delays execution of a function until a specified time has elapsed since its last invocation.
function debounce(func, delay) {let timeoutId;return (...args) => {clearTimeout(timeoutId);timeoutId = setTimeout(() => func.apply(this, args), delay);};}
Throttling ensures a function executes at most once within a set time interval.
function throttle(func, limit) {let inThrottle;return (...args) => {if (!inThrottle) {func.apply(this, args);inThrottle = true;setTimeout(() => (inThrottle = false), limit);}};}
Explore the concept of debouncing and throttling on GreatFrontEnd ->
Destructuring simplifies extracting values from arrays or objects into individual variables.
// Array destructuringconst [a, b] = [1, 2];// Object destructuringconst { name, age } = { name: 'John', age: 30 };
Explore the concept of destructuring assignment on GreatFrontEnd ->
Hoisting moves function declarations to the top of their scope during the compilation phase. However, function expressions and arrow functions do not get hoisted in the same way.
// Function declarationhoistedFunction(); // Works finefunction hoistedFunction() {console.log('This function is hoisted');}// Function expressionnonHoistedFunction(); // Throws an errorvar nonHoistedFunction = function () {console.log('This function is not hoisted');};
Explore the concept of hoisting on GreatFrontEnd ->
Classes in ES2015 use extends for inheritance and super to access parent constructors and methods.
class Animal {constructor(name) {this.name = name;}speak() {console.log(`${this.name} makes a noise.`);}}class Dog extends Animal {constructor(name, breed) {super(name);this.breed = breed;}speak() {console.log(`${this.name} barks.`);}}const dog = new Dog('Rex', 'German Shepherd');dog.speak(); // Rex barks.
Explore the concept of inheritance in ES2015 classes on GreatFrontEnd ->
Lexical scoping determines variable access based on where functions are defined, not where they're called.
function outerFunction() {let outerVariable = 'I am outside!';function innerFunction() {console.log(outerVariable); // I am outside!}innerFunction();}outerFunction();
Explore the concept of lexical scoping on GreatFrontEnd ->
JavaScript has three main types of scope: global, function, and block.
// Global scopevar globalVar = 'I am global';function myFunction() {// Function scopevar functionVar = 'I am in a function';if (true) {// Block scopelet blockVar = 'I am in a block';console.log(blockVar); // Accessible here}// console.log(blockVar); // Error}
Explore the concept of scope in JavaScript on GreatFrontEnd ->
The spread operator (...) expands elements of an iterable (like arrays) or properties of objects into individual elements.
// Copying an arrayconst arr1 = [1, 2, 3];const arr2 = [...arr1];// Merging arraysconst mergedArray = [...arr1, [4, 5]];// Copying an objectconst obj1 = { a: 1, b: 2 };const obj2 = { ...obj1 };// Passing as function argumentsconst sum = (x, y, z) => x + y + z;const nums = [1, 2, 3];sum(...nums); // 6
Explore the spread operator on GreatFrontEnd ->
this work in event handlers?In JavaScript, this in event handlers refers to the element that triggered the event. Its context can be explicitly bound using bind(), arrow functions, or direct assignment.
const button = document.querySelector('button');button.addEventListener('click', function () {console.log(this); // Refers to the button});const obj = {handleClick: function () {console.log(this); // Refers to obj},};button.addEventListener('click', obj.handleClick.bind(obj));
Explore the concept of this in event handlers on GreatFrontEnd ->
Congratulations on reaching the end of our comprehensive collection of JavaScript interview questions and answers! We hope this guide has equipped you with the knowledge and confidence needed to excel in your next JavaScript interview. Keep in mind that consistent practice is essential. Continue coding and revisiting these concepts until they feel effortless.
If you're looking for more JavaScript interview preparation materials, also check out these resources:

Preparing for React developer interviews can be daunting, especially with so many questions to choose from. With a wealth of resources available, it's crucial to identify the most relevant topics that will give you the best return on your time investment.
React interviews often emphasize a mix of core concepts and practical skills, such as state management, hooks, performance optimization, etc. To enhance your preparation, focus on the most commonly asked questions and those that integrate multiple skills for a comprehensive understanding.
In this list, we've compiled 30 essential React interview questions that are essential for your success.
If you're looking for more in-depth React interview preparation materials, also check out these resources:
React is an open-source JavaScript library developed by Facebook for building user interfaces, particularly single-page applications (SPAs). It allows developers to create reusable UI components that manage their own state. Key benefits include a component-based structure, efficient updates with the virtual DOM, declarative UI for readability, and strong community support.

JSX stands for JavaScript XML and is a syntax extension for JavaScript that lets you write HTML-like code within JavaScript. It simplifies creating React components. JSX is transformed into JavaScript function calls, usually by Babel. For example, <div>Hello, world!</div> becomes React.createElement('div', null, 'Hello, world!').

A React Node is any renderable unit in React, like an element, string, number, or null. A React Element is an immutable object describing what to render, created with JSX or React.createElement. A React Component is a function or class that returns React Elements, allowing for reusable UI pieces.
Fragments allow grouping multiple elements without adding extra nodes to the DOM, helping keep the markup clean and efficient.
return (<><h1>Title</h1><p>Description</p></>);
The virtual DOM is a lightweight copy of the real DOM. React uses it to optimize rendering by updating only the parts of the DOM that have changed, rather than re-rendering the entire tree.
The key prop in React uniquely identifies elements in a list, helping React optimize rendering by efficiently updating and reordering items. Without unique keys, React may unnecessarily re-render elements, leading to performance issues and bugs.
{items.map((item) => <ListItem key={item.id} value={item.value} />);}
keys in React?Using array indices as keys in React can lead to performance problems and unexpected behavior. When the order of items in an array changes, React might struggle to accurately determine which items have been modified. This can result in unnecessary re-renders or incorrect updates to the UI. To ensure efficient DOM management, it is advisable to use unique identifiers for keys instead of relying on array indices.
React hooks must be called at the top level of a function, never inside loops, conditions, or nested functions. They should only be called from React function components or custom hooks. These rules help maintain correct state and lifecycle behavior.

Controlled components are form elements whose values are controlled by React state, allowing for more predictable behavior and easier validation.
function MyForm() {const [inputValue, setInputValue] = useState('');const handleChange = (e) => {setInputValue(e.target.value);};return <input type="text" value={inputValue} onChange={handleChange} />;}
Hooks are functions that allow developers to use state and other React features without writing a class. Common hooks include useState for managing state and useEffect for side effects.
useEffect from useLayoutEffect in React?useEffect and useLayoutEffect are both hooks utilized for managing side effects in React functional components, but they differ in terms of execution timing:
useEffect: This hook runs asynchronously after the DOM has been updated and painted. It is well-suited for operations like data fetching or setting up subscriptions, as it does not block the rendering process.useLayoutEffect: In contrast, useLayoutEffect executes synchronously immediately after DOM mutations but before the browser has a chance to paint. This makes it ideal for tasks that require immediate access to the DOM, such as measuring element sizes or synchronizing the UI with the current DOM state.Code Example:
import React, { useEffect, useLayoutEffect, useRef } from 'react';function Example() {const ref = useRef();useEffect(() => {console.log('useEffect: Runs after DOM paint');});useLayoutEffect(() => {console.log('useLayoutEffect: Runs before DOM paint');console.log('Element width:', ref.current.offsetWidth);});return <div ref={ref}>Hello</div>;}
useEffect affect?The dependency array of useEffect controls when the effect re-runs:
Redux is a predictable state management library often used with React to manage application state through actions and reducers, promoting a unidirectional data flow.
Prop drilling occurs when you pass data through multiple layers of components, even if some intermediate components don't need that data. This can make your code cumbersome and harder to maintain.
// ParentComponent.jsimport React from 'react';import ChildComponentA from './ChildComponentA';function ParentComponent() {const data = 'Hello from Parent';return <ChildComponentA data={data} />;}// ChildComponentA.jsimport React from 'react';import ChildComponentB from './ChildComponentB';function ChildComponentA({ data }) {return <ChildComponentB data={data} />;}// ChildComponentB.jsimport React from 'react';import ChildComponentC from './ChildComponentC';function ChildComponentB({ data }) {return <ChildComponentC data={data} />;}// ChildComponentC.jsimport React from 'react';function ChildComponentC({ data }) {return <h1>{data}</h1>;}export default ChildComponentC;
In the example above, data is passed from ParentComponent to ChildComponentC through ChildComponentA and ChildComponentB, which don't use it. This unnecessary passing exemplifies prop drilling. To avoid this, consider using the Context API for more efficient data sharing.
HOCs are functions that take a component and return a new component, allowing for code reuse and abstraction of common functionality.
function withLogging(WrappedComponent) {return function EnhancedComponent(props) {console.log('Rendering:', WrappedComponent.name);return <WrappedComponent {...props} />;};}
Lazy loading is an optimization technique where components or modules are loaded only when they are needed, improving application performance.
const LazyComponent = React.lazy(() => import('./LazyComponent'));function App() {return (<React.Suspense fallback={<div>Loading...</div>}><LazyComponent /></React.Suspense>);}
The Context API provides a way to share values (like global state) between components without having to explicitly pass props through every level of the tree.
Techniques include:
React.memo for functional components.shouldComponentUpdate for class components.Error boundaries are special components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of crashing the whole application.
class ErrorBoundary extends React.Component {constructor(props) {super(props);this.state = { hasError: false };}static getDerivedStateFromError(error) {return { hasError: true };}componentDidCatch(error, info) {console.error('Error caught:', error);}render() {if (this.state.hasError) {return <h1>Something went wrong.</h1>;}return this.props.children;}}
Custom hooks allow developers to extract component logic into reusable functions, promoting cleaner code and better organization.
function useFetch(url) {const [data, setData] = useState(null);useEffect(() => {fetch(url).then((response) => response.json()).then((data) => setData(data));}, [url]);return data;}
Forms can be controlled or uncontrolled; controlled forms use state to manage input values while uncontrolled forms rely on DOM elements directly.
Server-side rendering (SSR) involves rendering components on the server before sending fully rendered HTML to clients, improving initial load times and SEO through efficient hydration processes.

Testing React applications can be done using Jest and React Testing Library. Jest serves as the testing framework while React Testing Library provides utilities for testing components similarly to user interactions.
Synthetic events are cross-browser wrappers around native events that provide consistent behavior across different browsers while maintaining performance optimizations.
useReducer hook in React.The useReducer hook is an alternative to useState for managing complex state logic by using reducers similar to Redux patterns.
const initialState = { count: 0 };function reducer(state, action) {switch (action.type) {case 'increment':return { count: state.count + 1 };case 'decrement':return { count: state.count - 1 };default:throw new Error();}}function Counter() {const [state, dispatch] = useReducer(reducer, initialState);return (<>Count: {state.count}<button onClick={() => dispatch({ type: 'increment' })}>+</button><button onClick={() => dispatch({ type: 'decrement' })}>-</button></>);}
Hydration involves attaching event listeners and making server-rendered HTML interactive on the client side. After server-side rendering, React initializes dynamic behavior by attaching event handlers.

React anti-patterns are practices that can lead to inefficient or hard-to-maintain code. Common examples include:
You can implement routing using react-router-dom. Here's an example:
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';function App() {return (<Router><Switch><Route path="/" exact component={Home} /><Route path="/about" component={About} /><Route path="/contact" component={Contact} /></Switch></Router>);}
Localization typically involves libraries like react-i18next or react-intl. Set up translation files for different languages and configure the library within your app using provided hooks or components.
// Example using react-i18nextimport { useTranslation } from 'react-i18next';const MyComponent = () => {const { t } = useTranslation();return <p>{t('welcome_message')}</p>;};
If you're looking for more in-depth React interview preparation materials, also check out these resources:

HTML, CSS, and JavaScript are fundamental skills for any aspiring web developer, and securing a job in this field can be a challenging endeavor, especially for beginners. A critical part of the interview process is the technical interview, where your proficiency in these core web technologies is thoroughly assessed.
To help you prepare and boost your confidence, we’ve compiled a list of the top 50 essential interview questions and answers covering HTML, CSS, and JavaScript that are frequently asked in interviews.
If you're looking for additional JavaScript interview preparation materials, also check out these resources:
Hoisting refers to JavaScript's behavior of moving variable and function declarations to the top of their scope during the compilation phase. While declarations are hoisted, initializations are not.
console.log(foo); // undefinedvar foo = 1;console.log(foo); // 1
Visualized as:
var foo;console.log(foo); // undefinedfoo = 1;console.log(foo); // 1
let, const, and classThese are hoisted but remain uninitialized, leading to a ReferenceError if accessed before declaration.
console.log(bar); // ReferenceErrorlet bar = 'value';
Function declarations are fully hoisted (both declaration and definition), while function expressions are only partially hoisted (declaration without initialization).
console.log(declared()); // Worksfunction declared() {return 'Declared function';}console.log(expr); // undefinedconsole.log(expr()); // TypeError: expr is not a functionvar expr = function () {return 'Function expression';};
Import statements are hoisted, making imported modules available throughout the file.
import foo from './foo';foo.doSomething(); // Accessible
Explore the concept of "hoisting" in JavaScript on GreatFrontEnd
let, var, and const Differ?var: Function-scoped or globally scoped.let and const: Block-scoped, confined to their nearest enclosing block.function test() {var a = 1;let b = 2;const c = 3;}console.log(a); // ReferenceErrorconsole.log(b); // ReferenceErrorconsole.log(c); // ReferenceError
var and let: Can be declared without initialization.const: Must be initialized during declaration.var a;let b;const c; // SyntaxError: Missing initializer
var: Allows redeclaration in the same scope.let and const: Redeclaration is not allowed.var x = 1;var x = 2; // Validlet y = 1;let y = 2; // SyntaxError
var and let: Reassignment is allowed.const: Reassignment is not allowed.const z = 1;z = 2; // TypeError
var: Hoisted and initialized to undefined.let and const: Hoisted but not initialized, causing a ReferenceError if accessed before declaration.console.log(a); // undefinedvar a = 1;console.log(b); // ReferenceErrorlet b = 2;
Explore the differences between let, var, and const on GreatFrontEnd
== and ===?==):42 == '42'; // true0 == false; // truenull == undefined; // true
===):42 === '42'; // false0 === false; // falsenull === undefined; // false
Prefer === to avoid unexpected behavior caused by type coercion, except when comparing against null or undefined.
var value = null;console.log(value == null); // trueconsole.log(value === null); // true
Explore the difference between == and === on GreatFrontEnd
The event loop allows JavaScript to handle asynchronous tasks on a single thread, ensuring smooth execution without blocking.
setTimeout and UI events.Promise callbacks.console.log('Start');setTimeout(() => console.log('Timeout'), 0);Promise.resolve().then(() => console.log('Promise'));console.log('End');
Output:
StartEndPromiseTimeout
Explore the event loop in JavaScript on GreatFrontEnd
Event delegation uses a single event listener on a parent element to manage events on its child elements. This approach takes advantage of event bubbling, improving efficiency.
document.getElementById('parent').addEventListener('click', (event) => {if (event.target.tagName === 'BUTTON') {console.log(`Clicked ${event.target.textContent}`);}});
Explore event delegation in JavaScript on GreatFrontEnd
this Work in JavaScript?The value of this depends on how a function is invoked:
window in browsers).call, apply, or bind.this from the surrounding scope.const obj = {name: 'Alice',greet() {console.log(this.name);},};obj.greet(); // Alice
Explore how this works in JavaScript on GreatFrontEnd
localStorage, and sessionStorage Differ?document.cookie = 'token=abc123; expires=Fri, 31 Dec 2025 23:59:59 GMT; path=/';console.log(document.cookie);
localStorage:localStorage.setItem('key', 'value');console.log(localStorage.getItem('key'));
sessionStorage:sessionStorage.setItem('key', 'value');console.log(sessionStorage.getItem('key'));
Explore the difference between cookies, localStorage, and sessionStorage on GreatFrontEnd
<script>, <script async>, and <script defer>?<script>:<script async>:<script defer>:<script src="main.js"></script><script async src="async.js"></script><script defer src="defer.js"></script>
Explore the difference between <script>, <script async>, and <script defer> on GreatFrontEnd
null, undefined, and Undeclared Variables Differ?null:Explicitly represents no value. Use === to check.
undefined:Indicates a variable has been declared but not assigned a value.
Variables not declared will throw a ReferenceError.
let a;console.log(a); // undefinedlet b = null;console.log(b); // null
Explore the difference between null, undefined, and undeclared variables on GreatFrontEnd
.call and .apply?.call:Accepts arguments as a comma-separated list.
.apply:Accepts arguments as an array.
function sum(a, b) {return a + b;}console.log(sum.call(null, 1, 2)); // 3console.log(sum.apply(null, [1, 2])); // 3
Explore the difference between .call and .apply on GreatFrontEnd
Function.prototype.bind and Why Is It Useful?The Function.prototype.bind method allows you to create a new function with a specific this context and optional preset arguments. It’s particularly useful for ensuring a function has the correct this context when passed to another function or used as a callback.
const john = {age: 42,getAge: function () {return this.age;},};console.log(john.getAge()); // 42const unboundGetAge = john.getAge;console.log(unboundGetAge()); // undefinedconst boundGetAge = john.getAge.bind(john);console.log(boundGetAge()); // 42const mary = { age: 21 };const boundGetAgeMary = john.getAge.bind(mary);console.log(boundGetAgeMary()); // 21
this: bind is often used to fix the this value for a method, ensuring it always refers to the intended object.bind.bind allows methods from one object to be used on another object.Explore Function.prototype.bind on GreatFrontEnd
Arrow functions automatically bind the this value to the surrounding lexical scope, which eliminates issues with context in methods. This behavior makes code more predictable and easier to debug.
const Person = function (name) {this.name = name;this.sayName1 = function () {console.log(this.name);};this.sayName2 = () => {console.log(this.name);};};const john = new Person('John');const dave = new Person('Dave');john.sayName1(); // Johnjohn.sayName2(); // Johnjohn.sayName1.call(dave); // Davejohn.sayName2.call(dave); // John
this context.Explore the advantage of using the arrow syntax for a method in a constructor on GreatFrontEnd
Prototypal inheritance allows objects to inherit properties and methods from other objects through the prototype chain.
Every JavaScript object has a prototype, which is another object from which it inherits properties.
function Person(name, age) {this.name = name;this.age = age;}Person.prototype.sayHello = function () {console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);};const john = new Person('John', 30);john.sayHello(); // Hello, my name is John and I am 30 years old.
JavaScript looks for properties and methods on the object and continues up the chain until it finds the property or reaches null.
Used with new to create objects and set their prototype.
function Animal(name) {this.name = name;}Animal.prototype.sayName = function () {console.log(`My name is ${this.name}`);};function Dog(name, breed) {Animal.call(this, name);this.breed = breed;}Dog.prototype = Object.create(Animal.prototype);Dog.prototype.bark = function () {console.log('Woof!');};const fido = new Dog('Fido', 'Labrador');fido.sayName(); // My name is Fidofido.bark(); // Woof!
Explore how prototypal inheritance works on GreatFrontEnd
function Person(){}, const person = Person(), and const person = new Person()?function Person() {} is a standard function declaration. When written in PascalCase, it conventionally represents a constructor function.
const person = Person() calls the function and executes its code but does not create a new object.
const person = new Person() creates a new object, setting its prototype to Person.prototype.
function foo() {console.log('Function declaration');}
const foo = function () {console.log('Function expression');};
Explore the differences between function declarations and expressions on GreatFrontEnd
const person = { firstName: 'John', lastName: 'Doe' };
Object() Constructor:const person = new Object();person.firstName = 'John';person.lastName = 'Doe';
Object.create():const proto = {greet() {console.log('Hello!');},};const person = Object.create(proto);person.greet(); // Hello!
class Person {constructor(name, age) {this.name = name;this.age = age;}}
Explore ways to create objects in JavaScript on GreatFrontEnd
Higher-order functions either:
function multiplier(factor) {return function (number) {return number * factor;};}const double = multiplier(2);console.log(double(5)); // 10
Explore higher-order functions on GreatFrontEnd
function Person(name) {this.name = name;}Person.prototype.greet = function () {console.log(`Hello, I’m ${this.name}`);};
class Person {constructor(name) {this.name = name;}greet() {console.log(`Hello, I’m ${this.name}`);}}
Key Differences:
extends and super.Explore ES2015 classes and ES5 constructors on GreatFrontEnd
Event bubbling is when an event starts at the target element and propagates up through its ancestors.
parent.addEventListener('click', () => console.log('Parent clicked'));child.addEventListener('click', () => console.log('Child clicked'));
Clicking the child triggers both handlers.
Explore event bubbling on GreatFrontEnd
Event capturing is when an event starts at the root and propagates down to the target element.
parent.addEventListener('click', () => console.log('Parent capturing'), true);
Explore event capturing on GreatFrontEnd
mouseenter and mouseover Events Differ in JavaScript and Browsers?mouseentermouseoverExample:
const fs = require('fs');const data = fs.readFileSync('large-file.txt', 'utf8');console.log(data); // Blocks until file is readconsole.log('End of the program');
Example:
console.log('Start of the program');fetch('https://api.example.com/data').then((response) => response.json()).then((data) => console.log(data)) // Non-blocking.catch((error) => console.error(error));console.log('End of program');
Understand the distinctions between synchronous and asynchronous functions on GreatFrontEnd
AJAX (Asynchronous JavaScript and XML) encompasses a collection of web development techniques that utilize various client-side technologies to build asynchronous web applications. Unlike traditional web applications where every user interaction results in a complete page reload, AJAX enables web apps to send and retrieve data from a server asynchronously. This allows for dynamic updates to specific parts of a web page without disrupting the overall page display and behavior.
Key Highlights:
XMLHttpRequest, though fetch() is now the preferred choice for modern web development.XMLHttpRequest APIExample:
let xhr = new XMLHttpRequest();xhr.onreadystatechange = function () {if (xhr.readyState === XMLHttpRequest.DONE) {if (xhr.status === 200) {console.log(xhr.responseText);} else {console.error('Request failed: ' + xhr.status);}}};xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos/1', true);xhr.send();
XMLHttpRequest, assigns a callback to handle state changes, opens a connection to a specified URL, and sends the request.fetch() APIExample:
fetch('https://jsonplaceholder.typicode.com/todos/1').then((response) => {if (!response.ok) {throw new Error('Network response was not ok');}return response.json();}).then((data) => console.log(data)).catch((error) => console.error('Fetch error:', error));
.then() to parse JSON data, and handles errors using .catch().fetchfetch() starts an asynchronous request to obtain a resource from a given URL.
Example:
fetch('https://api.example.com/data', {method: 'GET', // or 'POST', 'PUT', 'DELETE', etc.headers: {'Content-Type': 'application/json',},});
fetch() returns a Promise that resolves to a Response object representing the server's reply.The Response object provides methods to handle the content, such as .json(), .text(), and .blob().
Example:
fetch('https://api.example.com/data').then((response) => response.json()).then((data) => console.log(data)).catch((error) => console.error('Error:', error));
fetch() operates asynchronously, allowing the browser to perform other tasks while awaiting the server's response..then(), .catch()) are processed in the microtask queue as part of the event loop.fetch() allows configuration of various request settings, including HTTP method, headers, body, credentials, and caching behavior..catch() or try/catch with async/await.Learn how to explain AJAX in detail on GreatFrontEnd
AJAX (Asynchronous JavaScript and XML) facilitates the asynchronous exchange of data between web pages and servers, enabling dynamic content updates without necessitating full page reloads.
Explore the benefits and drawbacks of using AJAX on GreatFrontEnd
XMLHttpRequest and fetch() Differ?Both XMLHttpRequest (XHR) and fetch() facilitate asynchronous HTTP requests in JavaScript, but they vary in syntax, handling mechanisms, and features.
setRequestHeader method.send method.body property within the options parameter is used to include the request body.responseType property to manage different response formats.Response object with .then methods for accessing data.onerror event..catch method.abort() method.AbortController for canceling requests.onprogress event.Choosing Between Them: fetch() is generally favored for its cleaner syntax and Promise-based handling, though XMLHttpRequest remains useful for specific scenarios like progress tracking.
Discover the distinctions between XMLHttpRequest and fetch() on GreatFrontEnd
JavaScript encompasses a variety of data types, which are categorized into two main groups: primitive and non-primitive (reference) types.
true or false.Identifying Data Types: JavaScript is dynamically typed, meaning variables can hold different types of data at various times. The typeof operator is used to determine a variable's type.
Explore the variety of data types in JavaScript on GreatFrontEnd
Looping through object properties and array items is a fundamental task in JavaScript, and there are multiple methods to accomplish this. Below are some of the common approaches:
for...in LoopIterates over all enumerable properties of an object, including inherited ones.
for (const property in obj) {if (Object.hasOwn(obj, property)) {console.log(property);}}
Object.keys()Returns an array containing the object's own enumerable property names.
Object.keys(obj).forEach((property) => console.log(property));
Object.entries()Provides an array of the object's own enumerable string-keyed [key, value] pairs.
Object.entries(obj).forEach(([key, value]) => console.log(`${key}: ${value}`));
Object.getOwnPropertyNames()Returns an array of all properties (including non-enumerable ones) directly found on the object.
Object.getOwnPropertyNames(obj).forEach((property) => console.log(property));
for LoopA traditional loop for iterating over array elements.
for (let i = 0; i < arr.length; i++) {console.log(arr[i]);}
Array.prototype.forEach()Executes a provided function once for each array element.
arr.forEach((element, index) => console.log(element, index));
for...of LoopIterates over iterable objects like arrays.
for (let element of arr) {console.log(element);}
Array.prototype.entries()Provides both the index and value of each array element within a for...of loop.
for (let [index, elem] of arr.entries()) {console.log(index, ': ', elem);}
Introduced in ES2015, the spread syntax (...) is a powerful feature for copying and merging arrays and objects without altering the originals. It's widely used in functional programming, Redux, and RxJS.
Cloning Arrays/Objects: Creates shallow copies.
const array = [1, 2, 3];const newArray = [...array]; // [1, 2, 3]const obj = { name: 'John', age: 30 };const newObj = { ...obj, city: 'New York' }; // { name: 'John', age: 30, city: 'New York' }
Combining Arrays/Objects: Merges them into a new entity.
const arr1 = [1, 2, 3];const arr2 = [4, 5, 6];const mergedArray = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]const obj1 = { foo: 'bar' };const obj2 = { qux: 'baz' };const mergedObj = { ...obj1, ...obj2 }; // { foo: 'bar', qux: 'baz' }
Passing Function Arguments: Spreads array elements as individual arguments.
const numbers = [1, 2, 3];Math.max(...numbers); // Equivalent to Math.max(1, 2, 3)
Array vs. Object Spreads: Only iterables can be spread into arrays, while arrays can also be spread into objects.
const array = [1, 2, 3];const obj = { ...array }; // { 0: 1, 1: 2, 2: 3 }
The rest syntax (...) collects multiple elements into an array or object, functioning as the opposite of spread syntax.
Function Parameters: Gathers remaining arguments into an array.
function addFiveToNumbers(...numbers) {return numbers.map((x) => x + 5);}const result = addFiveToNumbers(4, 5, 6, 7); // [9, 10, 11, 12]
Array Destructuring: Collects remaining elements into a new array.
const [first, second, ...remaining] = [1, 2, 3, 4, 5];// first: 1, second: 2, remaining: [3, 4, 5]
Object Destructuring: Gathers remaining properties into a new object.
const { e, f, ...others } = { e: 1, f: 2, g: 3, h: 4 };// e: 1, f: 2, others: { g: 3, h: 4 }
Rest Parameter Rules: Must be the final parameter in a function.
function addFiveToNumbers(arg1, ...numbers, arg2) {// Error: Rest element must be last element.}
Understand the benefits of spread syntax and how it differs from rest syntax on GreatFrontEnd
Map Object Differ from a Plain Object in JavaScript?size property to easily determine the number of key-value pairs.forEach, keys(), values(), and entries().Object.keys(), Object.values(), or Object.entries() to iterate.// Mapconst map = new Map();map.set('key1', 'value1');map.set({ key: 'key2' }, 'value2');console.log(map.size); // 2// Plain Objectconst obj = { key1: 'value1' };obj[{ key: 'key2' }] = 'value2';console.log(Object.keys(obj).length); // 1 (keys are strings)
Discover the differences between a Map object and a plain object in JavaScript on GreatFrontEnd
Map/Set and WeakMap/WeakSet?The primary distinctions between Map/Set and WeakMap/WeakSet in JavaScript are outlined below:
Key Types:
Map and Set accept keys of any type, including objects, primitives, and functions.WeakMap and WeakSet exclusively use objects as keys, disallowing primitive values like strings or numbers.Memory Management:
Map and Set maintain strong references to their keys and values, preventing their garbage collection.WeakMap and WeakSet use weak references for keys (objects), allowing garbage collection if there are no other strong references.Key Enumeration:
Map and Set have enumerable keys that can be iterated over.WeakMap and WeakSet do not allow enumeration of keys, making it impossible to retrieve lists of keys or values directly.Size Property:
Map and Set provide a size property indicating the number of elements.WeakMap and WeakSet lack a size property since their size can change due to garbage collection.Use Cases:
Map and Set are suitable for general-purpose data storage and caching.WeakMap and WeakSet are ideal for storing metadata or additional object-related information without preventing the objects from being garbage collected when they are no longer needed.Learn about the differences between Map/Set and WeakMap/WeakSet on GreatFrontEnd
=> Function Syntax?One effective application of JavaScript's arrow function syntax is streamlining callback functions, especially when concise, inline function definitions are needed. Consider the following example:
Scenario: Doubling Array Elements with map
Imagine you have an array of numbers and you want to double each number using the map method.
// Traditional function syntaxconst numbers = [1, 2, 3, 4, 5];const doubledNumbers = numbers.map(function (number) {return number * 2;});console.log(doubledNumbers); // Output: [2, 4, 6, 8, 10]
By utilizing arrow function syntax, the same outcome can be achieved more succinctly:
// Arrow function syntaxconst numbers = [1, 2, 3, 4, 5];const doubledNumbers = numbers.map((number) => number * 2);console.log(doubledNumbers); // Output: [2, 4, 6, 8, 10]
Explore a use case for the new arrow => function syntax on GreatFrontEnd
In the realm of asynchronous programming, a callback function is passed as an argument to another function and is executed once a particular task completes, such as data retrieval or handling input/output operations. Here's a straightforward explanation:
function fetchData(callback) {setTimeout(() => {const data = { name: 'John', age: 30 };callback(data);}, 1000);}fetchData((data) => {console.log(data); // { name: 'John', age: 30 }});
Explore the concept of a callback function in asynchronous operations on GreatFrontEnd
Debouncing and throttling are techniques used to control the rate at which functions are executed, optimizing performance and managing event-driven behaviors in JavaScript applications.
Debouncing: Delays the execution of a function until a specified period has elapsed since its last invocation. This is particularly useful for scenarios like handling search input where you want to wait until the user has finished typing before executing a function.
function debounce(func, delay) {let timeoutId;return (...args) => {clearTimeout(timeoutId);timeoutId = setTimeout(() => func.apply(this, args), delay);};}
Throttling: Restricts a function to be executed no more than once within a given timeframe. This is beneficial for handling events that fire frequently, such as window resizing or scrolling.
function throttle(func, limit) {let inThrottle;return (...args) => {if (!inThrottle) {func.apply(this, args);inThrottle = true;setTimeout(() => (inThrottle = false), limit);}};}
These strategies help in enhancing application performance by preventing excessive function calls.
Explore the concept of debouncing and throttling on GreatFrontEnd
Destructuring assignment in JavaScript provides a concise way to extract values from arrays or properties from objects into individual variables.
// Array destructuringconst [a, b] = [1, 2];// Object destructuringconst { name, age } = { name: 'John', age: 30 };
This syntax employs square brackets for arrays and curly braces for objects, allowing for streamlined variable assignments directly from data structures.
Explore the concept of destructuring assignment for objects and arrays on GreatFrontEnd
Hoisting in JavaScript refers to the behavior where function declarations are moved to the top of their containing scope during the compilation phase. This allows functions to be invoked before their actual definition in the code. Conversely, function expressions and arrow functions must be defined prior to their invocation to avoid errors.
// Function declarationhoistedFunction(); // Works finefunction hoistedFunction() {console.log('This function is hoisted');}// Function expressionnonHoistedFunction(); // Throws an errorvar nonHoistedFunction = function () {console.log('This function is not hoisted');};
Explore the concept of hoisting with regards to functions on GreatFrontEnd
In ES2015, JavaScript introduces the class syntax with the extends keyword, enabling one class to inherit properties and methods from another. The super keyword is used to access the parent class's constructor and methods.
class Animal {constructor(name) {this.name = name;}speak() {console.log(`${this.name} makes a noise.`);}}class Dog extends Animal {constructor(name, breed) {super(name);this.breed = breed;}speak() {console.log(`${this.name} barks.`);}}const dog = new Dog('Rex', 'German Shepherd');dog.speak(); // Output: Rex barks.
In this example, the Dog class inherits from the Animal class, demonstrating how classes facilitate inheritance and method overriding in JavaScript.
Explore the concept of inheritance in ES2015 classes on GreatFrontEnd
Lexical scoping in JavaScript determines how variable names are resolved based on their location within the source code. Nested functions have access to variables from their parent scopes, enabling them to utilize and manipulate these variables.
function outerFunction() {let outerVariable = 'I am outside!';function innerFunction() {console.log(outerVariable); // 'I am outside!'}innerFunction();}outerFunction();
In this scenario, innerFunction can access outerVariable because of lexical scoping rules, which allow inner functions to access variables defined in their outer scope.
Explore the concept of lexical scoping on GreatFrontEnd
Scope in JavaScript defines the accessibility of variables and functions in different parts of the code. There are three primary types of scope:
let or const within a block (e.g., within {}) are accessible only within that block.// Global scopevar globalVar = 'I am global';function myFunction() {// Function scopevar functionVar = 'I am in a function';if (true) {// Block scopelet blockVar = 'I am in a block';console.log(blockVar); // Accessible here}// console.log(blockVar); // Throws an error}console.log(globalVar); // Accessible here// console.log(functionVar); // Throws an error
In this example, globalVar is accessible globally, functionVar is confined to myFunction, and blockVar is restricted to the if block.
Explore the concept of scope in JavaScript on GreatFrontEnd
The spread operator (...) in JavaScript allows iterable elements (like arrays or objects) to be expanded into individual elements. It's versatile and can be used for copying, merging, and passing array elements as function arguments.
// Copying an arrayconst arr1 = [1, 2, 3];const arr2 = [...arr1];// Merging arraysconst arr3 = [4, 5, 6];const mergedArray = [...arr1, ...arr3];// Copying an objectconst obj1 = { a: 1, b: 2 };const obj2 = { ...obj1 };// Merging objectsconst obj3 = { c: 3, d: 4 };const mergedObject = { ...obj1, ...obj3 };// Passing array elements as function argumentsconst sum = (x, y, z) => x + y + z;const numbers = [1, 2, 3];console.log(sum(...numbers)); // Output: 6
The spread operator simplifies operations such as copying arrays or objects, merging multiple arrays or objects into one, and spreading elements of an array as individual arguments to functions.
Explore the concept of the spread operator and its uses on GreatFrontEnd
this Binding Work in Event Handlers?In JavaScript, the this keyword refers to the object that is executing the current piece of code. Within event handlers, this typically points to the DOM element that triggered the event. However, its value can change depending on how the handler is defined and invoked. To ensure this references the intended context, techniques like using bind(), arrow functions, or explicitly setting the context are employed.
These methods help maintain the correct reference for this within event handling functions, ensuring consistent and predictable behavior across various event-driven scenarios in JavaScript applications.
Explore the concept of this binding in event handlers on GreatFrontEnd
A Block Formatting Context (BFC) is a pivotal concept in CSS that influences how block-level elements are rendered and interact on a webpage. It creates an isolated environment where block boxes are laid out, ensuring that elements like floats, absolutely positioned elements, inline-blocks, table-cells, table-captions, and those with an overflow value other than visible (except when propagated to the viewport) establish a new BFC.
Grasping how to initiate a BFC is essential because, without it, the containing box might fail to encompass floated child elements. This issue is akin to collapsing margins but is often more deceptive, causing entire boxes to collapse unexpectedly.
A BFC is formed when an HTML box satisfies at least one of the following criteria:
float property is set to a value other than none.position property is assigned a value that is neither static nor relative.display property is set to table-cell, table-caption, inline-block, flex, inline-flex, grid, or inline-grid.overflow property is set to a value other than visible.Within a BFC, each box's left outer edge aligns with the left edge of its containing block (or the right edge in right-to-left layouts). Additionally, vertical margins between adjacent block-level boxes within a BFC collapse into a single margin.
Discover Block Formatting Context (BFC) and its Operation on GreatFrontEnd
z-index and How is a Stacking Context Created?The z-index property in CSS manages the vertical stacking order of overlapping elements. It only influences positioned elements—those with a position value other than static—and their descendants or flex items.
In the absence of a z-index value, elements stack based on their order in the Document Object Model (DOM), with elements appearing later in the HTML markup rendered on top of earlier ones at the same hierarchy level. Positioned elements (those with non-static positioning) and their children will always overlay elements with default static positioning, regardless of their order in the HTML structure.
A stacking context is essentially a group of elements that share a common stacking order. Within a local stacking context, the z-index values of child elements are relative to that context rather than the entire document. Elements outside of this context—such as sibling elements of a local stacking context—cannot interpose between layers within it. For instance, if element B overlays element A, a child of element A, element C, cannot surpass element B in the stacking order even if it has a higher z-index than element B.
Each stacking context operates independently; after stacking its child elements, the entire context is treated as a single entity within the parent stacking context's order. Certain CSS properties, like an opacity less than 1, a filter that isn't none, or a transform that isn't none, can trigger the creation of a new stacking context.
Learn about z-index and Stacking Contexts on GreatFrontEnd
This topic relates to writing efficient CSS, specifically how browsers interpret and apply CSS selectors. Browsers process selectors from right to left, starting with the most specific (the key selector) and moving outward. They first identify all elements that match the rightmost part of the selector and then traverse up the DOM tree to verify if those elements meet the remaining parts of the selector.
For example, consider the selector p span. Browsers will first locate all <span> elements and then check each span's ancestor chain to determine if it is within a <p> element. Once a <p> ancestor is found for a given <span>, the browser confirms that the <span> matches the selector and ceases further traversal for that element.
The efficiency of selector matching is influenced by the length of the selector chain—the shorter the chain, the quicker the browser can verify matches.
Understand How Browsers Match CSS Selectors on GreatFrontEnd
The CSS box model is a fundamental concept that describes the rectangular boxes generated for elements in the document tree, determining how they are laid out and displayed. Each box comprises a content area (such as text or images) surrounded by optional padding, border, and margin areas.
The box model is responsible for calculating:
width, height, padding, and border.height is specified, a block element's height adjusts to its content plus padding (unless floats are involved).width is set, a non-floated block element expands to fit its parent's width minus padding, unless a max-width is specified.
table, figure, and input have inherent width values and may not expand fully.span do not have a default width and will not expand to fit.height and width are determined by its content.box-sizing: content-box), padding and border are not included in an element's width and height.Note: Margins do not contribute to the actual size of the box; they affect the space outside the box. The box's area is confined to the border and does not extend into the margin.
Understanding the box-sizing property is crucial as it alters how an element's height and width are calculated.
box-sizing: content-box: The default behavior where only the content size is considered.box-sizing: border-box: Includes padding and border in the element's total width and height, excluding margin.Many CSS frameworks adopt box-sizing: border-box globally for a more intuitive sizing approach.
Explore the Box Model and Its Control in CSS on GreatFrontEnd
display Property? Provide Examples.The display property in CSS dictates how an element is rendered in the document flow. Common values include none, block, inline, inline-block, flex, grid, table, table-row, table-cell, and list-item.
Description:
none
Hides the element; it does not occupy any space in the layout. All child elements are also hidden. The element is treated as if it does not exist in the DOM.
block
The element occupies the full width available, starting on a new line.
inline
The element does not start on a new line and only occupies as much width as necessary.
inline-block
Combines characteristics of both inline and block. The element flows with text but can have width and height set.
flex
Defines the element as a flex container, enabling the use of flexbox layout for its children.
grid
Defines the element as a grid container, allowing for grid-based layout of its children.
table
Makes the element behave like a <table> element.
table-row
Makes the element behave like a <tr> (table row) element.
table-cell
Makes the element behave like a <td> (table cell) element.
list-item
Makes the element behave like a <li> (list item) element, enabling list-specific styling such as list-style-type and list-style-position.
For a comprehensive list of display property values, refer to the CSS Display | MDN.
Understand the CSS display Property with Examples on GreatFrontEnd
relative, fixed, absolute, sticky, and static Positioning Differ?In CSS, an element's positioning is determined by its position property, which can be set to relative, fixed, absolute, sticky, or static. Here's how each behaves:
static: The default positioning. Elements flow naturally within the document. The top, right, bottom, left, and z-index properties have no effect.
relative: The element is positioned relative to its normal position. Adjustments using top, right, bottom, or left move the element without affecting the layout of surrounding elements, leaving a gap where it would have been.
absolute: The element is removed from the normal document flow and positioned relative to its nearest positioned ancestor (an ancestor with a position other than static). If no such ancestor exists, it positions relative to the initial containing block. Absolutely positioned elements do not affect the position of other elements and can have width and height specified.
fixed: Similar to absolute, but the element is positioned relative to the viewport, meaning it stays in the same place even when the page is scrolled.
sticky: A hybrid of relative and fixed. The element behaves like relative until it crosses a specified threshold (e.g., scroll position), after which it behaves like fixed, sticking to its position within its parent container.
Understanding these positioning schemes is vital for controlling the layout and behavior of elements, especially in responsive and dynamic designs.
Learn About Positioning Schemes in CSS on GreatFrontEnd
Designing and developing for multilingual websites involves various considerations to ensure accessibility and usability across different languages and cultures. This process is part of internationalization (i18n).
lang attribute on the <html> tag to specify the page's language.en_US, zh_CN).<link rel="alternate" hreflang="other_locale" href="url_for_other_locale"> to inform search engines about alternate language versions of the page.<link rel="alternate" href="url_for_fallback" hreflang="x-default" />.en-US vs. en-GB, zh-CN vs. zh-TW).Accept-Language headers and IP addresses.Dynamic Content: Instead of concatenating strings (e.g., "The date today is " + date), use template strings with parameter substitution to accommodate different grammar structures across languages.
Example:
// Englishconst message = `I will travel on ${date}`;// Chineseconst message = `我会在${date}出发`;
Understand Multilingual Design Considerations on GreatFrontEnd
block, inline, and inline-block Display Types Differ?The display property in CSS determines how elements are rendered on the page. The block, inline, and inline-block values have distinct behaviors and use cases:
| Property | block | inline-block | inline |
|---|---|---|---|
| Size | Fills up the width of its parent container. | Depends on content. | Depends on content. |
| Positioning | Start on a new line and tolerates no HTML elements next to it (except when you add float) | Flows along with other content and allows other elements beside it. | Flows along with other content and allows other elements beside it. |
Can specify width and height | Yes | Yes | No. Will ignore if being set. |
Can be aligned with vertical-align | No | Yes | Yes |
| Margins and paddings | All sides respected. | All sides respected. | Only horizontal sides respected. Vertical sides, if specified, do not affect layout. Vertical space it takes up depends on line-height, even though the border and padding appear visually around the content. |
| Float | - | - | Becomes like a block element where you can set vertical margins and paddings. |
| Use Cases | Layout elements like <div>, <p>, <section>. | Used for buttons, images, and form fields that need custom sizes but stay in line with text. | Links <a>, text formatting <span>, text styling - bold <b>, italics <i>. |
Learn the Differences Between block, inline, and inline-block on GreatFrontEnd
translate() Over Absolute Positioning, or Vice Versa?The translate() function is a part of the CSS transform property and offers a different approach to positioning compared to absolute positioning. Here's why you might choose one over the other:
Using translate():
position: relative.transform or opacity does not trigger browser reflows or repaints; instead, it initiates a composition layer. This results in smoother and more efficient animations, as translate() leverages the GPU for rendering..element {transform: translateX(50px);}
Using absolute Positioning:
.element {position: absolute;top: 20px;left: 30px;}
Why Choose translate()?
For animations and dynamic movements where performance and smoothness are critical, translate() is more efficient. It avoids the costly reflows associated with changing layout-affecting properties like top and left.
Why Choose absolute Positioning?
When you need to position elements precisely without regard to their original place in the document flow, absolute positioning is the way to go. It's essential for creating overlays, modals, and tooltips that need to appear in specific locations on the screen.
Understand When to Use translate() vs. Absolute Positioning on GreatFrontEnd
* { box-sizing: border-box; } Do and What Are Its Advantages?Applying * { box-sizing: border-box; } in your CSS ensures that all elements on the page use the border-box model for calculating their width and height.
By default, elements use box-sizing: content-box, where the width and height only account for the content area. When you set box-sizing: border-box, the width and height properties include the element's padding and border, but not the margin.
| Property | box-sizing: content-box (default) | box-sizing: border-box |
|---|---|---|
| content | Yes | Yes |
padding | No | Yes |
border | No | Yes |
margin | No | No |
padding and border within the width and height makes it easier to calculate the size of elements, aligning more closely with designers' expectations.padding or border to elements, as it doesn't alter the total size.box-sizing: border-box globally to maintain consistency and predictability in element sizing.Explore What * { box-sizing: border-box; } Does and Its Benefits on GreatFrontEnd
Congratulations on reaching the end of our comprehensive collection of HTML, CSS, and JavaScript interview questions and answers! We hope this resource has equipped you with the confidence and skills needed to excel in your upcoming interviews. Remember, consistent practice is essential, so keep coding and revisiting these concepts until they become second nature.

For experienced frontend developers, React interviews often focus on advanced concepts that evaluate both problem-solving abilities and architectural knowledge. With over a decade of React experience, you're expected to have a strong grasp of the framework's core principles, as well as its more sophisticated patterns and performance optimization strategies.
To help you shine in these interviews, we've compiled a list of 50 React JS interview questions. These questions cover everything from React's lifecycle and hooks to context management, error boundaries, and performance bottlenecks, ensuring you're well-prepared to demonstrate your expertise in handling complex challenges and building scalable applications.
If you're looking for more in-depth React interview preparation materials, also check out these resources:
The Virtual DOM (VDOM) is a lightweight, in-memory representation of the real DOM. When a React component's state or props change, React first updates the VDOM instead of the actual DOM. Then, React performs a "diffing" algorithm to calculate the minimum number of changes required to update the real DOM, ensuring efficient UI rendering.
function Counter() {const [count, setCount] = React.useState(0);return (<button onClick={() => setCount(count + 1)}>Clicked {count} times</button>);}
React's reconciliation algorithm identifies changes in the VDOM tree and updates only the affected parts of the real DOM. It uses the following steps:
key props to identify stable elements across renders.componentDidMountuseState, useEffect, etc.) for state and lifecycleThe Fiber architecture is a re-implementation of React's reconciliation algorithm designed to improve rendering. It breaks rendering into units of work that can be paused and resumed, allowing React to prioritize high-priority tasks (e.g., user input).
React Fragments allow grouping child elements without adding an extra DOM node.
<div> elements in the DOM, which can cause layout or CSS issuesfunction List() {return (<><li>Item 1</li><li>Item 2</li></>);}
// Parent component passes propsconst Parent = () => {return <Child name="John" />;};// Child component receives propsconst Child = ({ name }) => {return <h1>Hello, {name}</h1>;};
Lifting state up means moving the state to the nearest common ancestor of the components that need to access it. This is necessary for sharing state between sibling components.
Example:
// Lifting state upconst Parent = () => {const [counter, setCounter] = useState(0);return (<div><Child1 counter={counter} /><Child2 setCounter={setCounter} /></div>);};const Child1 = ({ counter }) => <h1>{counter}</h1>;const Child2 = ({ setCounter }) => (<button onClick={() => setCounter((prev) => prev + 1)}>Increment</button>);
In this example, the state is managed in the Parent component, and both child components access it via props.
To manage deeply nested state, you can use one of the following techniques:
useState: For simpler state updates in nested structures.useReducer: More useful for complex or deeply nested state, where actions and reducers help keep state changes predictable.const initialState = { user: { name: 'John', age: 30 } };function reducer(state, action) {switch (action.type) {case 'UPDATE_NAME':return { ...state, user: { ...state.user, name: action.payload } };default:return state;}}const Component = () => {const [state, dispatch] = useReducer(reducer, initialState);return (<button onClick={() => dispatch({ type: 'UPDATE_NAME', payload: 'Jane' })}>Update Name</button>);};
// Controlled componentconst ControlledInput = () => {const [value, setValue] = useState('');return <input value={value} onChange={(e) => setValue(e.target.value)} />;};// Uncontrolled componentconst UncontrolledInput = () => {const inputRef = useRef();return <input ref={inputRef} />;};
Here are some ways to prevent unnecessary re-renders:
// Using React.memoconst MyComponent = React.memo(({ name }) => {return <h1>{name}</h1>;});// Using useMemoconst computedValue = useMemo(() => expensiveComputation(a, b), [a, b]);// Using useCallbackconst memoizedCallback = useCallback(() => {console.log('This function is memoized');}, []);
Hooks allow using state and lifecycle features in functional components, making them concise, reusable, and easier to test.
useEffect hook, and how do you manage dependencies in it?The useEffect hook handles side effects like fetching data, updating the DOM, or setting up subscriptions in functional components. Dependencies, specified as the second argument ([]), control when the effect runs:
[]): Runs only after the initial render[dep1, dep2]): Runs when any dependency changesuseMemo and useCallback?The key difference between useMemo and useCallback lies in what they cache:
useMemo: Caches the result of a computation to avoid re-computing it unless its dependencies change. Useful for optimizing expensive calculations.useCallback: Caches a function instance to avoid unnecessary re-creation unless its dependencies change. Useful for preventing child components from re-rendering when passed as props.const memoizedValue = useMemo(() => computeValue(a, b), [a, b]);const memoizedCallback = useCallback(() => callback(a, b), [a, b]);
useReducer hook in React and when should it be used?The useReducer hook manages complex state logic in functional components, serving as an alternative to useState. It's ideal when state has multiple sub-values or when the next state relies on the previous one. It accepts a reducer function and an initial state.
const [state, dispatch] = useReducer(reducer, initialState);
useEffect and useLayoutEffect in React?useEffect and useLayoutEffect are both used for handling side effects in React functional components but differ in timing:
useEffect runs asynchronously after the DOM has painted, ideal for tasks like data fetching or subscriptions.useLayoutEffect runs synchronously after DOM mutations but before the browser paints, useful for tasks like measuring DOM elements or synchronizing the UI with the DOM.Example:
import React, { useEffect, useLayoutEffect, useRef } from 'react';function Example() {const ref = useRef();useEffect(() => {console.log('useEffect: Runs after DOM paint');});useLayoutEffect(() => {console.log('useLayoutEffect: Runs before DOM paint');console.log('Element width:', ref.current.offsetWidth);});return <div ref={ref}>Hello</div>;}
React class components have lifecycle methods for different phases:
constructor: Initializes state or binds methodscomponentDidMount: Runs after the component mounts, useful for API calls or subscriptionscomponentDidMount() {console.log('Component mounted');}
shouldComponentUpdate: Determines if the component should re-rendercomponentDidUpdate: Runs after updates, useful for side effectscomponentWillUnmount: Cleans up (e.g., removing event listeners).componentWillUnmount() {console.log('Component will unmount');}
These methods allow you to manage component behavior throughout its lifecycle.
shouldComponentUpdate and React.memo optimize re-renders?shouldComponentUpdate: A lifecycle method in class components. Return false to prevent unnecessary renders.React.memo: A higher-order component for functional components to prevent re-renders unless props change.const MemoizedComponent = React.memo(({ prop }) => <div>{prop}</div>);
key prop in lists, and how does React use it to optimize rendering?The key prop uniquely identifies elements in a list, enabling React to efficiently update the DOM by matching keys during the reconciliation process. Without unique keys, React may unnecessarily re-render elements, leading to performance issues and bugs.
{items.map((item) => <ListItem key={item.id} value={item.value} />);}
React.PureComponent and when it should be used?React.PureComponent is a class component that performs a shallow comparison of props and state to determine if the component should re-render. Use it when the component's render output depends solely on its props and state.
Code Splitting: Use React.lazy and Suspense to load components only when needed, improving initial load time
Memoization: Use useMemo and React.memo to memoize expensive calculations and components, preventing unnecessary re-renders
Debouncing/Throttling: Limit frequent updates (like search inputs) with debouncing or throttling to optimize performance
Pagination and Infinite Scroll: Load data in chunks with pagination or infinite scroll to reduce rendering large datasets at once
Higher-order components (HOCs) are functions that take a component and return a new one with added props or behavior, facilitating logic reuse across components.
const withExtraProps = (WrappedComponent) => {return (props) => <WrappedComponent {...props} extraProp="value" />;};const EnhancedComponent = withExtraProps(MyComponent);
The Context API allows sharing state across the component tree without prop drilling. You create a context with React.createContext(), wrap your app with a Provider to supply the state, and access it with useContext() in any component.
Use the Context API for global state (like themes, user data) that needs to be accessed by multiple components, avoiding prop drilling. Example:
const ThemeContext = React.createContext();const App = () => {const [theme, setTheme] = useState('light');return (<ThemeContext.Provider value={{ theme, setTheme }}><SomeComponent /></ThemeContext.Provider>);};
React.lazy and Suspense?React.lazy dynamically loads a component only when needed. Suspense displays a fallback UI while loading.
const LazyComponent = React.lazy(() => import('./LazyComponent'));function App() {return (<Suspense fallback={<div>Loading...</div>}><LazyComponent /></Suspense>);}
Custom hooks are functions that encapsulate reusable logic.
function useCounter(initialValue = 0) {const [count, setCount] = React.useState(initialValue);const increment = () => setCount((prev) => prev + 1);return { count, increment };}
Use useState to manage form data, where the form elements' values are controlled by React state. This allows for easy validation and manipulation of form inputs.
const [value, setValue] = useState('');const handleChange = (e) => setValue(e.target.value);return <input value={value} onChange={handleChange} />;
Use useRef to directly access DOM elements and their values, which can be useful for simple forms or when you want to avoid managing state.
const inputRef = useRef();const handleSubmit = () => console.log(inputRef.current.value);return <input ref={inputRef} />;
Controlled components offer more flexibility and control, while uncontrolled components can be simpler and more efficient for certain use cases.
useRef in form validation, and how would you implement it?useRef allows direct access to DOM elements, which is useful for form validation in uncontrolled components or when you need to interact with form fields without managing their state. It can also be used to store values or functions without causing re-renders.
Implementation: You can use useRef to reference input elements and then access their values for validation when the form is submitted.
const inputRef = useRef();function handleSubmit() {const value = inputRef.current.value;if (value.trim() === '') {console.log('Input is required!');} else {console.log('Input value:', value);}}return <input ref={inputRef} />;
For robust form validation, use libraries like Formik or React Hook Form, which handle validation and error management efficiently.
For simple forms, use useState to track input values and validation errors:
const [value, setValue] = useState('');const [error, setError] = useState('');const handleSubmit = () => {if (!value) setError('Field is required');else console.log('Form submitted:', value);};
Use libraries for complex forms, and useState for basic validation.
To handle dynamic form fields, use state arrays to manage and update the fields as they are added or removed.
Example:
const [fields, setFields] = useState(['']);const addField = () => setFields([...fields, '']);const removeField = (index) => setFields(fields.filter((_, i) => i !== index));return (<>{fields.map((_, index) => (<input key={index} />))}<button onClick={addField}>Add Field</button></>);
This approach allows you to dynamically add or remove form fields while keeping the state updated.
Debouncing delays execution of an action (e.g., API call) until a specified time has elapsed since the last input.
const handleInput = debounce((value) => console.log(value), 300);
render, fireEvent, and screen for assertions.test('renders button', () => {render(<button>Click Me</button>);expect(screen.getByText('Click Me')).toBeInTheDocument();});
Snapshot testing captures a component's rendered output (DOM structure) at a specific point in time and compares it to a saved baseline to detect unintended changes. It helps ensure UI consistency by alerting you to unexpected modifications.
Use async/await and waitFor from React Testing Library to wait for updates after async operations.
Example:
import { render, screen, waitFor } from '@testing-library/react';import MyComponent from './MyComponent';test('displays data after fetching', async () => {render(<MyComponent />);await waitFor(() =>expect(screen.getByText(/fetched data/i)).toBeInTheDocument(),);});
This ensures the test waits for async updates before making assertions.
Mock API calls using Jest's jest.mock or libraries like Axios Mock Adapter to simulate responses and test components in isolation.
Example:
import axios from 'axios';import { render, screen, waitFor } from '@testing-library/react';import MyComponent from './MyComponent';jest.mock('axios');test('displays fetched data', async () => {axios.get.mockResolvedValue({ data: { message: 'Hello, World!' } });render(<MyComponent />);await waitFor(() =>expect(screen.getByText('Hello, World!')).toBeInTheDocument(),);});
Key Points: -jest.mock: Replaces the actual module with a mock. 0 Mock Responses: Use mockResolvedValue or mockRejectedValue to simulate API behavior.
React Router maps URL paths to components, enabling navigation in single-page apps. Dynamic routing allows you to use URL parameters to render components based on dynamic values.
Example:
import { BrowserRouter, Routes, Route, useParams } from 'react-router-dom';function UserPage() {const { id } = useParams(); // Access dynamic parameterreturn <h1>User ID: {id}</h1>;}export default function App() {return (<BrowserRouter><Routes><Route path="/user/:id" element={<UserPage />} /> {/* Dynamic path */}</Routes></BrowserRouter>);}
Key Features:
:id captures dynamic data from the URL.useParams Hook: Accesses these dynamic values for rendering.Nested routes allow you to create hierarchies of components, and useParams helps access dynamic route parameters.
<Outlet>: Renders child routes within a parent layoutuseParams: Retrieves route parameters for dynamic routingimport {BrowserRouter,Routes,Route,Outlet,useParams,} from 'react-router-dom';function UserProfile() {const { userId } = useParams();return <h2>User ID: {userId}</h2>;}function App() {return (<BrowserRouter><Routes><Route path="user/:userId" element={<Outlet />}><Route path="profile" element={<UserProfile />} /></Route></Routes></BrowserRouter>);}
BrowserRouter: Uses the HTML5 History API to manage navigation, enabling clean URLs without the hash (#). It requires server-side configuration to handle routes correctly, especially for deep linking.
HashRouter: Uses the hash (#) portion of the URL to simulate navigation. It doesn't require server-side configuration, as the hash is never sent to the server. This makes it suitable for environments where server-side routing isn't possible (e.g., static hosting).
To implement private routes, create a component that checks if the user is authenticated before rendering the desired route.
Example:
import { Navigate } from 'react-router-dom';function PrivateRoute({ children }) {return isAuthenticated ? children : <Navigate to="/login" />;}
PrivateRoute: Checks authentication and either renders the children (protected routes) or redirects to the login page.<Navigate>: Replaces the deprecated <Redirect> for redirecting in React Router v6+.Use the useLocation hook to get the current route, and conditionally apply styles for the active state.
Example:
import { useLocation } from 'react-router-dom';function NavBar() {const location = useLocation();return (<nav><ul><li className={location.pathname === '/home' ? 'active' : ''}>Home</li><li className={location.pathname === '/about' ? 'active' : ''}>About</li></ul></nav>);}
Error boundaries are React components that catch JavaScript errors anywhere in the component tree and log those errors, preventing the entire app from crashing. They display a fallback UI instead of the component tree that crashed.
class ErrorBoundary extends React.Component {state = { hasError: false };static getDerivedStateFromError() {return { hasError: true };}componentDidCatch(error, info) {console.log(error, info);}render() {if (this.state.hasError) {return <h1>Something went wrong!</h1>;}return this.props.children;}}
To implement global error handling, you can use Error Boundaries at a top level in your application (such as wrapping your entire app or specific routes). This ensures that even if a component crashes, the rest of the app continues to function.
For asynchronous errors (like API calls), use try-catch within async functions, and handle them using state to show error messages. You can also integrate error boundaries to catch these errors in components.
const fetchData = async () => {try {const data = await fetch('/api/data');const result = await data.json();} catch (error) {setError(error.message);}};
Use React Suspense for async components, and display a fallback UI such as a loading spinner or error message when the component or data is still loading.
<Suspense fallback={<Loading />}><MyComponent /></Suspense>
You can also combine error boundaries with loading states to handle network failures.
For large-scale applications, use external error logging services such as Sentry or LogRocket. Integrate these tools into your app to automatically capture and monitor errors in production. Additionally, maintain custom error boundaries and provide meaningful error messages for debugging.
React portals provide a way to render children outside the parent component's DOM hierarchy. They are useful for modals, tooltips, or any UI element that needs to break out of the regular DOM flow but remain part of the React tree.
ReactDOM.createPortal(<Modal />, document.getElementById('modal-root'));
React Suspense is a feature that allows components to "wait" for something (like data or a code-split chunk) before rendering. It improves user experience by showing a fallback UI until the data is ready.
<Suspense fallback={<Loading />}><DataFetchingComponent /></Suspense>
Concurrent Mode allows React to work on multiple tasks simultaneously without blocking the main UI thread. It enables React to prioritize updates and provide smoother rendering for complex applications.
React uses the priority system in Concurrent Mode to schedule updates. It can break up large updates into smaller chunks and give priority to user interactions (like clicks or input) to ensure the app remains responsive.
To avoid blocking the UI, use Web Workers, setTimeout, or requestIdleCallback for offloading heavy computations. Alternatively, break tasks into smaller parts and use React's Suspense or useMemo to only recompute when necessary.
Example using setTimeout for deferring computation:
const [data, setData] = useState(null);useEffect(() => {setTimeout(() => {const result = computeExpensiveData();setData(result);}, 0);}, []);

React hooks have transformed the way developers build components, offering a simpler and more maintainable approach to managing state and side effects. With React's widespread adoption, understanding hooks has become a key focus in developer interviews. Whether you're gearing up for an interview or brushing up on your skills, these 30 essential React hooks questions will guide you through both foundational concepts and advanced use cases.
If you're looking for more in-depth React interview preparation materials, also check out these resources:
React hooks are functions that allow developers to use state and other React features in functional components. Prior to hooks, these features were only available in class components. Hooks like useState, useEffect, and useContext are now central to modern React development.
Hooks allow you to use state and other React features in functional components, eliminating the need for classes. They simplify code by reducing reliance on lifecycle methods, improve code readability, and make it easier to reuse stateful logic across components. Common hooks like useState and useEffect help manage state and side effects.
React hooks must be called at the top level of a function, never inside loops, conditions, or nested functions. They should only be called from React function components or custom hooks. These rules help maintain correct state and lifecycle behavior.
useState?The useState hook allows you to add state to functional components. It returns an array with two elements: the current state value and a function to update it.
useState trigger re-renders?When the state update function from useState is called, React schedules the component to re-render. This re-render updates the Virtual DOM, compares it with the previous version, and applies the necessary changes to the actual DOM, ensuring the UI reflects the new state efficiently.
setState in useState?Yes, you can pass a function to setState in useState. The function receives the previous state value and returns the new state.
setState((prevState) => prevState + 1);
useEffect worksThe useEffect hook handles side effects like data fetching or DOM updates. It runs after rendering and is controlled by a dependencies array:
useEffect(() => {console.log('Effect ran');return () => console.log('Cleanup logic'); // Runs on unmount or dependency change}, [dependency]);
useEffect?[]): The effect runs only once, after the initial render.[a, b]): The effect runs whenever the specified dependencies change.useEffect?You can use useEffect to fetch data by calling an async function within the effect. Remember to handle loading and error states.
useEffect(() => {const fetchData = async () => {const response = await fetch('url');const data = await response.json();setData(data);};fetchData();}, []);
useEffect?To handle cleanup, return a function from the useEffect callback. This cleanup function is called when the component unmounts or when the dependencies change.
useEffect(() => {// Setup codereturn () => {// Cleanup code};}, [dependencies]);
useRef?The useRef hook creates a mutable object that persists through renders, allowing direct access to DOM elements, storing mutable values without causing re-renders, and maintaining references to values. For instance, useRef can be utilized to focus on an input element:
import React, { useRef, useEffect } from 'react';function TextInputWithFocusButton() {const inputEl = useRef(null);useEffect(() => {inputEl.current.focus();}, []);return <input ref={inputEl} type="text" />;}
useEffect and componentDidMount?useEffect runs after the initial render, while componentDidMount runs once after the component is mounted. However, useEffect is more flexible as it can handle dependencies.
useContext?The useContext hook allows you to access the value of a context in a functional component. It eliminates the need to pass props down multiple layers.
useId hook in React and when should it be used?The useId hook generates unique IDs for elements within a component, which is crucial for accessibility by linking form inputs with labels. It guarantees unique IDs across the application even if the component renders multiple times.
import { useId } from 'react';function MyComponent() {const id = useId();return (<div><label htmlFor={id}>Name:</label><input id={id} type="text" /></div>);}
Custom hooks are JavaScript functions that allow you to reuse stateful logic across multiple components. They can use built-in hooks like useState, useEffect, etc., and encapsulate logic that can be shared.
useReducer over useState?useReducer is helpful when managing complex state logic, especially when the state depends on previous values or when state updates are tied to specific actions.
useMemo work?The useMemo hook is used to memoize expensive calculations so they only rerun when specific dependencies change. This can improve performance, especially in large applications.
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
useMemo and useCallback?useMemo is used to memoize the result of a function, while useCallback is used to memoize the function itself. Both are useful for preventing unnecessary re-renders.
useMemo over useCallback?Use useMemo to memoize the results of calculations, and use useCallback to memoize the function itself. useMemo is used for computations, while useCallback is used for functions passed down as props.
useImperativeHandle hook used for?useImperativeHandle customizes the instance value that is exposed to parent components when using ref. This allows you to control which methods or properties are available when the parent component accesses the ref.
useDeferredValue improve UI responsiveness?useDeferredValue defers updates to a value, prioritizing smoother UI interactions.
useTransition?useTransition manages state transitions with a lower priority, useful for rendering smooth updates.
useEffect and useLayoutEffect?useEffect and useLayoutEffect are both used for handling side effects in React functional components but differ in timing:
useEffect runs asynchronously after the DOM has painted, ideal for tasks like data fetching or subscriptions.useLayoutEffect runs synchronously after DOM mutations but before the browser paints, useful for tasks like measuring DOM elements or synchronizing the UI with the DOM.Code Example:
import React, { useEffect, useLayoutEffect, useRef } from 'react';function Example() {const ref = useRef();useEffect(() => {console.log('useEffect: Runs after DOM paint');});useLayoutEffect(() => {console.log('useLayoutEffect: Runs before DOM paint');console.log('Element width:', ref.current.offsetWidth);});return <div ref={ref}>Hello</div>;}
useCallback?useCallback is used to memoize functions so that they don't get recreated on every render. This is especially useful when passing callbacks to child components to prevent unnecessary re-renders.
const memoizedCallback = useCallback(() => {doSomething(a, b);}, [a, b]);
useReducer work?The useReducer hook is an alternative to useState for handling more complex state logic. It works similarly to Redux reducers, where an action is dispatched to update the state.
const [state, dispatch] = useReducer(reducer, initialState);
useLayoutEffect?useLayoutEffect is used for DOM mutations that need to be applied before the paint, ensuring that the component is fully updated before the browser renders the changes.
To prevent unnecessary re-renders:
React.memo to memoize functional components.useMemo to memoize calculations.useCallback to memoize event handlers.Custom hooks allow you to encapsulate and reuse stateful logic. For example, a useFetch hook could handle data fetching logic for multiple components.
const useFetch = (url) => {const [data, setData] = useState(null);useEffect(() => {fetch(url).then((res) => res.json()).then(setData);}, [url]);return data;};
setState() in React and when should it be used?The callback function format of setState() in React ensures that state updates are based on the most current state and props. This is essential when the new state depends on the previous state. Instead of passing an object directly to setState(), you provide a function that takes the previous state and props as arguments, returning the updated state.
this.setState((prevState, props) => ({counter: prevState.counter + props.increment,}));
Using this approach helps avoid issues related to asynchronous updates, ensuring that your state reflects the latest values accurately.
To continue your preparation beyond Hooks, explore our Top ReactJS Interview Questions GitHub repo - featuring 50 frequently asked questions covering core concepts, components, and advanced topics.

React has become a cornerstone of modern web development, and acing a React interview requires more than just surface-level knowledge. Interviewers often look for candidates who can confidently navigate concepts like hooks, the Virtual DOM, state management, and performance optimizations, while also demonstrating a deep understanding of React's core principles and advanced patterns.
To help you stand out, we've compiled 50 essential React JS interview questions that cover everything from foundational topics to intricate real-world scenarios.
If you're looking for more in-depth React interview preparation materials, also check out these resources:
React is a JavaScript library by Facebook for building user interfaces, especially in single-page apps. It allows reusable components with their own state. Key benefits include a component-based structure, efficient updates with the virtual DOM, declarative UI for readability, and strong community support.

A React Node is any renderable unit in React, like an element, string, number, or null. A React Element is an immutable object describing what to render, created with JSX or React.createElement. A React Component is a function or class that returns React Elements, allowing for reusable UI pieces.
JSX stands for JavaScript XML and is a syntax extension for JavaScript that lets you write HTML-like code within JavaScript. It simplifies creating React components. JSX is transformed into JavaScript function calls, usually by Babel. For example, <div>Hello, world!</div> becomes React.createElement('div', null, 'Hello, world!').

In React, state is local data managed within a component that can change over time, while props are read-only attributes passed from a parent to a child component. state is used for data that changes within a component, whereas props are used to pass data and event handlers to child components.

key Prop in React?The key prop in React uniquely identifies elements in a list, helping React optimize rendering by efficiently updating and reordering items. Without unique keys, React may unnecessarily re-render elements, leading to performance issues and bugs.
{items.map((item) => <ListItem key={item.id} value={item.value} />);}
Using array indices as keys in React can cause performance issues and bugs. When the order of items changes, React may fail to correctly identify which items have changed, leading to unnecessary re-renders or incorrect updates. It's better to use unique identifiers for keys to ensure efficient DOM management.
In controlled components, form data is managed by the component's state, making it the single source of truth. Changes to input values are handled via event handlers. In uncontrolled components, the form state is internal and accessed through refs. Controlled components offer more control and are easier to test, while uncontrolled components are simpler to implement for basic cases.
Example of controlled component:
function ControlledInput() {const [value, setValue] = React.useState('');return (<inputtype="text"value={value}onChange={(e) => setValue(e.target.value)}/>);}
Example of uncontrolled component:
function UncontrolledInput() {const inputRef = React.useRef();return <input type="text" ref={inputRef} />;}
Context in React can lead to performance issues if not handled carefully, causing unnecessary re-renders of components that consume the context, even if only part of the context changes. Overusing context for state management can also make the code harder to maintain and understand. It's best to use context sparingly and consider other state management solutions like Redux or Zustand for more complex scenarios.
Hooks allow you to use state and other React features in functional components, eliminating the need for classes. They simplify code by reducing reliance on lifecycle methods, improve code readability, and make it easier to reuse stateful logic across components. Common hooks like useState and useEffect help manage state and side effects.
React hooks must be called at the top level of a function, never inside loops, conditions, or nested functions. They should only be called from React function components or custom hooks. These rules help maintain correct state and lifecycle behavior.
useEffect and useLayoutEffect are both used for handling side effects in React functional components but differ in timing:
useEffect runs asynchronously after the DOM has painted, ideal for tasks like data fetching or subscriptions.useLayoutEffect runs synchronously after DOM mutations but before the browser paints, useful for tasks like measuring DOM elements or synchronizing the UI with the DOM.Code Example:
import React, { useEffect, useLayoutEffect, useRef } from 'react';function Example() {const ref = useRef();useEffect(() => {console.log('useEffect: Runs after DOM paint');});useLayoutEffect(() => {console.log('useLayoutEffect: Runs before DOM paint');console.log('Element width:', ref.current.offsetWidth);});return <div ref={ref}>Hello</div>;}
useEffect affect?The dependency array of useEffect controls when the effect re-runs:
useRef hook in React and when should it be used?The useRef hook creates a mutable object that persists through renders, allowing direct access to DOM elements, storing mutable values without causing re-renders, and maintaining references to values. For instance, useRef can be utilized to focus on an input element:
import React, { useRef, useEffect } from 'react';function TextInputWithFocusButton() {const inputEl = useRef(null);useEffect(() => {inputEl.current.focus();}, []);return <input ref={inputEl} type="text" />;}
setState() in React and when should it be used?The callback function format of setState() in React ensures that state updates are based on the most current state and props. This is essential when the new state depends on the previous state. Instead of passing an object directly to setState(), you provide a function that takes the previous state and props as arguments, returning the updated state.
this.setState((prevState, props) => ({counter: prevState.counter + props.increment,}));
Using this approach helps avoid issues related to asynchronous updates, ensuring that your state reflects the latest values accurately.
useCallback hook in React and when should it be used?The useCallback hook memoizes functions to prevent their recreation on every render. This is especially beneficial when passing callbacks to optimized child components that depend on reference equality to avoid unnecessary renders. Use it when a function is passed as a prop to a child component.
const memoizedCallback = useCallback(() => {doSomething(a, b);}, [a, b]);
useMemo hook in React and when should it be used?The useMemo hook memoizes costly calculations, recomputing them only when dependencies change. This enhances performance by avoiding unnecessary recalculations. It should be used for computationally intensive functions that don't need to run on every render.
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
useReducer hook in React and when should it be used?The useReducer hook manages complex state logic in functional components, serving as an alternative to useState. It's ideal when state has multiple sub-values or when the next state relies on the previous one. It accepts a reducer function and an initial state.
const [state, dispatch] = useReducer(reducer, initialState);
useId hook in React and when should it be used?The useId hook generates unique IDs for elements within a component, which is crucial for accessibility by linking form inputs with labels. It guarantees unique IDs across the application even if the component renders multiple times.
import { useId } from 'react';function MyComponent() {const id = useId();return (<div><label htmlFor={id}>Name:</label><input id={id} type="text" /></div>);}
Re-rendering refers to updating a component's output in the DOM due to changes in state or props. When these changes occur, React triggers a re-render to ensure the UI reflects current data by calling the render method again.

React Fragments group multiple elements without adding extra nodes to the DOM. This allows returning multiple elements from a component's render method without wrapping them in an additional HTML element. You can utilize shorthand syntax <>...</> or React.Fragment.
return (<><ChildComponent1 /><ChildComponent2 /></>);
forwardRef() in React used for?forwardRef() allows passing a ref through a component to one of its children. This is useful for accessing a DOM element or child component's instance directly from a parent.
import React, { forwardRef } from 'react';const MyComponent = forwardRef((props, ref) => <input ref={ref} {...props} />);
To reset state in React, set it back to its initial value using the setState function. For example:
const [state, setState] = useState(initialState);setState(initialState);
React advises against mutating state as it can lead to unexpected behaviors and bugs. State immutability helps efficiently determine when components need re-rendering; direct mutations may prevent React from detecting changes.
Error boundaries catch JavaScript errors in their child components, log them, and display fallback UI instead of crashing the application. They utilize componentDidCatch and static getDerivedStateFromError methods but do not catch errors in event handlers or asynchronous code.
Testing React applications can be done using Jest and React Testing Library. Jest serves as the testing framework while React Testing Library provides utilities for testing components similarly to user interactions.
Hydration involves attaching event listeners and making server-rendered HTML interactive on the client side. After server-side rendering, React initializes dynamic behavior by attaching event handlers.

React Portals allow rendering children into a DOM node outside the parent component's hierarchy. This is useful for modals or tooltips that need to escape parent overflow or z-index constraints.
Debugging can be done using the React Developer Tools extension for inspecting component hierarchies and states along with console.log statements for logging data and errors.
React strict mode helps identify potential issues by activating additional checks and warnings without affecting production builds. Benefits include highlighting unsafe lifecycle methods and detecting unexpected side effects.
Localization typically involves libraries like react-i18next or react-intl. Set up translation files for different languages and configure the library within your app using provided hooks or components.
// Example using react-i18nextimport { useTranslation } from 'react-i18next';const MyComponent = () => {const { t } = useTranslation();return <p>{t('welcome_message')}</p>;};
Code splitting enhances performance by dividing code into smaller chunks loaded on demand, thereby reducing initial load times. This can be achieved through dynamic import() statements or using React's React.lazy and Suspense.
// Using React.lazy and Suspenseconst LazyComponent = React.lazy(() => import('./LazyComponent'));function App() {return (<React.Suspense fallback={<div>Loading...</div>}><LazyComponent /></React.Suspense>);}
Optimizing context performance involves memoizing context values with useMemo, splitting contexts for isolated state changes, and employing selectors to rerender only necessary components.
const value = useMemo(() => ({ state, dispatch }), [state, dispatch]);
Higher-order components (HOCs) are functions that take a component and return a new one with added props or behavior, facilitating logic reuse across components.
const withExtraProps = (WrappedComponent) => {return (props) => <WrappedComponent {...props} extraProp="value" />;};const EnhancedComponent = withExtraProps(MyComponent);
The Flux pattern manages application state through unidirectional data flow, simplifying debugging and enhancing maintainability with clear separation of concerns between Dispatcher, Stores, Actions, and Views.

One-way data flow means data moves from parent to child components only, making it predictable and easier to debug while enhancing maintainability and performance.

Asynchronous data loading uses useEffect alongside useState hooks; fetching data inside useEffect updates state with fetched results ensuring re-renders occur with new data.
import React, { useState, useEffect } from 'react';const FetchAndDisplayData = () => {const [info, updateInfo] = useState(null);const [isLoading, toggleLoading] = useState(true);useEffect(() => {const retrieveData = async () => {try {const res = await fetch('https://api.example.com/data');const data = await res.json();updateInfo(data);} catch (err) {console.error('Error fetching data:', err);} finally {toggleLoading(false);}};retrieveData();}, []);return (<div>{isLoading ? (<p>Fetching data, please wait...</p>) : (<pre>{JSON.stringify(info, null, 2)}</pre>)}</div>);};export default FetchAndDisplayData;
Server-side rendering (SSR) involves rendering components on the server before sending fully rendered HTML to clients, improving initial load times and SEO through efficient hydration processes.

Static generation pre-renders HTML at build time instead of runtime; this approach enhances performance by delivering static content quickly while improving SEO outcomes.
In React, the presentational vs container component pattern distinguishes between components that focus on appearance (presentational components) and those that manage logic and state (container components). Presentational components render HTML and CSS, while container components handle data and behavior. This separation leads to a cleaner and more organized codebase.
Common pitfalls in data fetching with React include failing to handle loading and error states, neglecting to clean up subscriptions which can cause memory leaks, and improperly using lifecycle methods or hooks. Always ensure proper handling of these states, clean up after components, and utilize useEffect for side effects in functional components.
Render props in React allow code sharing between components through a prop that is a function. This function returns a React element, enabling data to be passed to child components. This technique facilitates logic reuse without relying on higher-order components or hooks.
class DataFetcher extends React.Component {state = { data: null };componentDidMount() {fetch(this.props.url).then((response) => response.json()).then((data) => this.setState({ data }));}render() {return this.props.render(this.state.data);}}// Usage<DataFetcherurl="/api/data"render={(data) => <div>{data ? data.name : 'Loading...'}</div>}/>;
React anti-patterns are practices that can lead to inefficient or hard-to-maintain code. Common examples include:
Choosing between React state, context, and external state managers depends on your application's complexity. Use React state for local component state, context for global state shared across multiple components, and external managers like Redux or MobX for complex state management requiring advanced features.
The composition pattern in React involves building components by combining smaller, reusable ones instead of using inheritance. This encourages creating complex UIs by passing components as children or props.
function WelcomeDialog() {return (<Dialog><h1>Welcome</h1><p>Thank you for visiting our spacecraft!</p></Dialog>);}function Dialog(props) {return <div className="dialog">{props.children}</div>;}
The virtual DOM is a lightweight representation of the actual DOM used by React. It enables efficient UI updates by comparing the virtual DOM with the real DOM and applying only necessary changes through a process called reconciliation.

The virtual DOM works by creating a new tree whenever a component's state changes and comparing it with the previous tree through "reconciliation." This allows only the differences to be updated in the actual DOM, enhancing performance. Benefits include improved efficiency and a declarative UI management style, while downsides may include added complexity for simple applications.

React Fiber is a complete rewrite of React's reconciliation algorithm introduced in version 16. It enhances rendering by breaking tasks into smaller units, allowing React to pause and resume work, which improves UI responsiveness. This enables features like time slicing and suspense that weren't possible before.
Reconciliation is the process where React updates the DOM to match changes in the virtual DOM. When a component's state or props change, a new virtual DOM tree is created and compared with the previous one through "diffing," allowing efficient updates to only changed parts of the actual DOM.

React Suspense allows handling asynchronous operations more elegantly within components. It provides fallback content while waiting for resources like data or code to load. You can use it alongside React.lazy for code splitting.
const LazyComponent = React.lazy(() => import('./LazyComponent'));function MyComponent() {return (<React.Suspense fallback={<div>Loading...</div>}><LazyComponent /></React.Suspense>);}
setState is called in ReactWhen setState is invoked, it schedules an update to the component's state object. React merges the new state with the current one and triggers a re-render of the component. This process is asynchronous; thus, changes may not occur immediately, and multiple setState calls can be batched for performance optimization.
If you're looking for more in-depth React interview preparation materials, also check out these resources:
You can also explore our Top ReactJS Interview Questions repository - a curated list of around 50 real-world React interview questions compiled from actual company interviews and continuously updated for 2025.

React has become the go-to library for building modern web applications, and mastering it requires more than just reading documentation—you need hands-on practice. Whether you're a beginner or an experienced developer, coding questions can help sharpen your React skills and reinforce core concepts.
GreatFrontEnd offers a curated list of interactive React coding questions designed to test your UI development skills. These questions range from simple UI components to complex interactive elements, all helping you level up as a React developer.
If you're looking for more in-depth React interview preparation materials, also check out these resources:
React coding interview questions are possibly the most important round in the front end developer job interview process across every seniority. Interviewers want to see candidates with the following skills:
Here's a sneak peek at some of the most popular React coding questions available on our platform:
Build an accordion component that a displays a list of vertically stacked sections with each containing a title and content snippet.
Practice question on GreatFrontEnd ->
Build a contact form which submits user feedback and contact details to a back end API.
Practice question on GreatFrontEnd ->
Build the famous holy grail layout consisting of a header, 3 columns, and a footer.
Practice question on GreatFrontEnd ->
Build a list of progress bars that fill up gradually when they are added to the page.
Practice question on GreatFrontEnd ->
Build a calculator that computes the monthly mortgage for a loan.
Practice question on GreatFrontEnd ->
Build a component that books a flight for specified dates.
Practice question on GreatFrontEnd ->
Generate a table of numbers given the rows and columns.
Practice question on GreatFrontEnd ->
Build a progress bar component that shows the percentage completion of an operation.
Practice question on GreatFrontEnd ->
Build a temperature converter widget that converts temperature values between Celsius and Fahrenheit.
Practice question on GreatFrontEnd ->
Build a component that resembles a Tweet from Twitter.
Practice question on GreatFrontEnd ->
Build a tabs component that displays a list of tab elements and one associated panel of content at a time.
Practice question on GreatFrontEnd ->
Build a users data table with pagination features.
Practice question on GreatFrontEnd ->
Build a dice roller app that simulates the results of rolling 6-sided dice.
Practice question on GreatFrontEnd ->
Build a file explorer component to navigate files and directories in a tree-like hierarchical viewer.
Practice question on GreatFrontEnd ->
Build a Like button that changes appearance based on the states.
Practice question on GreatFrontEnd ->
Build a reusable modal dialog component that can be opened and closed.
Practice question on GreatFrontEnd ->
Build a star rating component that shows a row of star icons for users to select the number of filled stars corresponding to the rating.
Practice question on GreatFrontEnd ->
Build a Todo list that lets users add new tasks and delete existing tasks.
Practice question on GreatFrontEnd ->
Build a traffic light where the lights switch from green to yellow to red after predetermined intervals and loop indefinitely.
Practice question on GreatFrontEnd ->
Build a 7-segment digital clock that shows the current time.
Practice question on GreatFrontEnd ->
Build a tic-tac-toe game that is playable by two players.
Practice question on GreatFrontEnd ->
Build an image carousel that displays a sequence of images.
Practice question on GreatFrontEnd ->
Build a job board that displays the latest job postings from Hacker News.
Practice question on GreatFrontEnd ->
Build a stopwatch widget that can measure how much time has passed.
Practice question on GreatFrontEnd ->
Build a component that allows transferring of items between two lists.
Practice question on GreatFrontEnd ->
Build an accessible accordion component that has the right ARIA roles, states, and properties.
Practice question on GreatFrontEnd ->
Build a fully accessible accordion component that has keyboard support according to ARIA specifications.
Practice question on GreatFrontEnd ->
Build an analog clock where the hands update and move like a real clock.
Practice question on GreatFrontEnd ->
Build a users data table with sorting features.
Practice question on GreatFrontEnd ->
Build a semi-accessible file explorer component that has the right ARIA roles, states, and properties.
Practice question on GreatFrontEnd ->
Build a file explorer component using a flat DOM structure.
Practice question on GreatFrontEnd ->
Build a grid of lights where the lights deactivate in the reverse order they were activated.
Practice question on GreatFrontEnd ->
Build a semi-accessible modal dialog component that has the right ARIA roles, states, and properties.
Practice question on GreatFrontEnd ->
Build a moderately-accessible modal dialog component that supports common ways to close the dialog.
Practice question on GreatFrontEnd ->
Build a list of progress bars that fill up gradually in sequence, one at a time.
Practice question on GreatFrontEnd ->
Build a semi-accessible tabs component that has the right ARIA roles, states, and properties.
Practice question on GreatFrontEnd ->
Build a fully accessible tabs component that has keyboard support according to ARIA specifications.
Practice question on GreatFrontEnd ->
Build a list of progress bars that fill up gradually concurrently, up to a limit of 3.
Practice question on GreatFrontEnd ->
Build a widget that fetches birth year data from an API and plot it on a histogram.
Practice question on GreatFrontEnd ->
Build a game for two players who take turns to drop colored discs from the top into a vertically suspended board/grid.
Practice question on GreatFrontEnd ->
Build an image carousel that smoothly transitions between images.
Practice question on GreatFrontEnd ->
Build a nested checkboxes component with parent-child selection logic.
Practice question on GreatFrontEnd ->
Build an auth code input component that allows users to enter a 6-digit authorization code.
Practice question on GreatFrontEnd ->
Build a list of progress bars that fill up gradually concurrently, up to a limit of 3 and allows for pausing and resuming.
Practice question on GreatFrontEnd ->
Build a generalized data table with pagination and sorting features.
Practice question on GreatFrontEnd ->
Build a fully-accessible modal dialog component that supports all required keyboard interactions.
Practice question on GreatFrontEnd ->
Build an interface where users can drag to select multiple cells within a grid.
Practice question on GreatFrontEnd ->
Build Wordle, the word-guessing game that took the world by storm.
Practice question on GreatFrontEnd ->
Build an N x N tic-tac-toe game that requires M consecutive marks to win.
Practice question on GreatFrontEnd ->
Build an image carousel that smoothly transitions between images that has a minimal DOM footprint.
Practice question on GreatFrontEnd ->
If you found these 50 questions useful, you'll also like our Top ReactJS Interview Questions repository - a focused set of 50 real interview questions, curated to help you go even deeper.

For seasoned frontend engineers with over a decade of experience, interviews delve into sophisticated topics that test problem-solving skills and architectural expertise. To help you excel in these interviews, we've curated a definitive list of 20 advanced JavaScript questions. These questions cover intricate concepts like microtask queues, closures, async/await, and more, designed to showcase your deep understanding and ability to navigate complex challenges.
If you're looking for additional JavaScript interview preparation materials, also check out these resources:
The microtask queue in JavaScript is where tasks like promise callbacks (then and catch), async functions, and certain APIs like MutationObserver are queued for execution. It's separate from the regular task queue and has higher priority, ensuring microtasks are processed immediately after the current execution context is clear. This queue follows FIFO (First In, First Out) order, ensuring predictable handling of asynchronous operations in JavaScript applications.
Explore the concept of a microtask queue on GreatFrontEnd
Potential pitfalls of using closures in JavaScript include:
Explore the potential pitfalls of using closures on GreatFrontEnd
Anonymous functions offer a concise way to define functions, especially for simple operations or callbacks. They are commonly used in Immediately Invoked Function Expressions (IIFEs) to encapsulate code within a local scope, preventing variables from leaking into the global scope:
(function () {var x = 10;console.log(x); // 10})();// x is not accessible hereconsole.log(typeof x); // undefined
Anonymous functions are also effective as callbacks, enhancing code readability by defining handlers inline:
setTimeout(() => {console.log('Hello world!');}, 1000);
Moreover, they are utilized with higher-order functions like map(), filter(), and reduce() in functional programming:
const arr = [1, 2, 3];const double = arr.map((el) => el * 2);console.log(double); // [2, 4, 6]
In event handling, anonymous functions are widely employed in frameworks like React to define inline callback functions:
function App() {return <button onClick={() => console.log('Clicked!')}>Click Me</button>;}
These uses showcase how anonymous functions streamline code by keeping logic concise and scoped appropriately.
Explore the typical use case for anonymous functions on GreatFrontEnd
Languages that compile to JavaScript, like TypeScript or CoffeeScript, offer advantages such as improved syntax, type safety, and better tooling. These languages enhance code readability, provide robust error checking, and support advanced IDE features.
However, using such languages also introduces challenges. Developers may face additional build steps and increased complexity in their workflow. There could be potential performance overhead compared to writing directly in JavaScript. Moreover, adapting to new syntax and learning the intricacies of these languages can pose a learning curve initially.
The event loop in JavaScript manages asynchronous operations to prevent blocking the single-threaded execution:
This cycle ensures JavaScript remains responsive by handling both synchronous and asynchronous tasks efficiently.
Explore the Event Loop and how it handles asynchronous operations on GreatFrontEnd
Data binding in JavaScript automates the synchronization of data between the model (data source) and the view (UI). It ensures changes in one are immediately reflected in the other, enhancing application interactivity and reducing manual updates. There are two types:
Explore the concept of data binding in JavaScript on GreatFrontEnd
Hoisting in JavaScript can cause unexpected outcomes because variable and function declarations are lifted to the top of their scope during compilation. This behavior can lead to variables being accessed before their declaration, resulting in undefined values. It can also create confusion between function declarations and expressions. For instance:
console.log(a); // undefinedvar a = 5;console.log(b); // ReferenceError: b is not definedlet b = 10;
In the example above, a is hoisted and initialized as undefined before it's assigned 5. However, b throws a ReferenceError because let variables aren't hoisted like var variables are.
Explore the potential issues caused by hoisting on GreatFrontEnd
async/await is a contemporary feature in JavaScript designed to streamline the handling of promises. When you declare a function with the async keyword, you can utilize the await keyword within that function to halt execution until a promise resolves. This approach aligns asynchronous code structure more closely with synchronous code, enhancing readability and maintainability.
Example usage:
async function fetchData() {try {const response = await fetch('https://api.example.com/data');const data = await response.json();console.log(data);} catch (error) {console.error('Error fetching data:', error);}}
In this example:
Explore async/await and how it simplifies asynchronous code on GreatFrontEnd
In JavaScript, iterators and generators offer flexible ways to manage data sequences and control execution flow.
Iterators define a sequence and terminate with a potential return value. They require a next() method that returns an object with value (next sequence value) and done (boolean indicating completion) properties.
Example of an iterator:
const iterator = {current: 0,last: 5,next() {if (this.current <= this.last) {return { value: this.current++, done: false };} else {return { value: undefined, done: true };}},};let result = iterator.next();while (!result.done) {console.log(result.value); // Logs 0, 1, 2, 3, 4, 5result = iterator.next();}
Generators are special functions using function* syntax and yield keyword to control execution flow. They return an iterator object, allowing pausing and resuming execution.
Example of a generator:
function* numberGenerator() {let num = 0;while (num <= 5) {yield num++;}}const gen = numberGenerator();console.log(gen.next()); // { value: 0, done: false }console.log(gen.next()); // { value: 1, done: false }console.log(gen.next()); // { value: 2, done: false }console.log(gen.next()); // { value: 3, done: false }console.log(gen.next()); // { value: 4, done: false }console.log(gen.next()); // { value: 5, done: false }console.log(gen.next()); // { value: undefined, done: true }
Generators are efficient for creating iterators on-demand, useful for lazy evaluation, custom data structures, and asynchronous data handling.
Explore iterators and generators in JavaScript and their uses on GreatFrontEnd
Web Workers enable JavaScript code to run in the background, separate from the main execution thread of a web application. They handle intensive computations without freezing the user interface. Here's a concise example:
main.js:
const worker = new Worker('worker.js');worker.postMessage('Hello, worker!');worker.onmessage = (event) => console.log('Message from worker:', event.data);
worker.js:
onmessage = (event) => {console.log('Message from main script:', event.data);postMessage('Hello, main script!');};
Web Workers boost performance by offloading heavy tasks, ensuring smoother user interaction in web applications.
Explore Web Workers and how they improve performance on GreatFrontEnd
Memoization in JavaScript is a technique used to optimize functions by caching the results of expensive function calls and returning the cached result when the same inputs occur again. This can significantly improve performance by avoiding redundant calculations.
It is particularly useful for functions that are computationally expensive but deterministic—meaning they always produce the same output for the same input.
Here's a concise implementation example using a Fibonacci function:
function memoize(fn) {const cache = {};return function (...args) {const key = JSON.stringify(args);return cache[key] || (cache[key] = fn.apply(this, args));};}function fibonacci(n) {if (n <= 1) return n;return fibonacci(n - 1) + fibonacci(n - 2);}const memoizedFibonacci = memoize(fibonacci);console.log(memoizedFibonacci(6)); // Output: 8console.log(memoizedFibonacci(7)); // Output: 13console.log(memoizedFibonacci(6)); // Output: 8 (retrieved from cache)
To optimize performance and reduce reflows and repaints, follow these strategies:
DocumentFragment or innerHTML to insert multiple DOM nodes at once.requestAnimationFrame: Schedule animations and layout changes using requestAnimationFrame for smoother rendering.will-change: Mark elements that will undergo frequent changes with the will-change CSS property to optimize rendering.Implementing these practices helps ensure that your web application performs efficiently, maintaining smooth user interactions and responsive UI updates.
Explore how to optimize DOM manipulation for better performance on GreatFrontEnd
JavaScript polyfills are code snippets designed to replicate the behavior of modern JavaScript features on browsers that do not natively support them. They detect the absence of a specific feature and provide an alternative implementation using existing JavaScript capabilities.
For instance, consider the Array.prototype.includes() method, which verifies if an array contains a particular element. This method isn't supported in older browsers such as Internet Explorer 11. To address this gap, a polyfill for Array.prototype.includes() can be implemented as follows:
// Polyfill for Array.prototype.includes()if (!Array.prototype.includes) {Array.prototype.includes = function (searchElement) {for (var i = 0; i < this.length; i++) {if (this[i] === searchElement) {return true;}}return false;};}
typeof, in, or window.import 'core-js/actual/array/flat-map'; // Example: polyfill for Array.prototype.flatMap[1, 2].flatMap((it) => [it, it]); // Output: [1, 1, 2, 2]
<script src="https://polyfill.io/v3/polyfill.min.js"></script>
JavaScript polyfills play a crucial role in ensuring cross-browser compatibility and enabling the adoption of modern JavaScript features in environments with varying levels of browser support.
Explore what JavaScript polyfills are for on GreatFrontEnd
Module bundlers like Webpack, Parcel, and Rollup offer key benefits for web development:
Module bundlers streamline code organization, enhance performance, ensure compatibility, and integrate seamlessly with development tools, essential for modern web development.
Explore the benefits of using a module bundler on GreatFrontEnd
Tree shaking is a module bundling technique that removes dead code — code that's never used or executed — from the final bundle. This optimization reduces bundle size and enhances application performance. Tools like Webpack and Rollup support tree shaking primarily with ES6 module syntax (import/export), analyzing the code's dependency graph to eliminate unused exports efficiently.
Explore the concept of tree shaking in module bundling on GreatFrontEnd
Common performance bottlenecks in JavaScript applications often stem from inefficient DOM manipulation, excessive global variables, blocking the main thread with heavy computations, memory leaks, and improper use of asynchronous operations.
To address these challenges, employing techniques such as debouncing and throttling for event handling, optimizing DOM updates with batch processing, and utilizing web workers for offloading heavy computations can significantly enhance application responsiveness and efficiency. These approaches help mitigate the impact of these bottlenecks on user experience and overall application performance.
Explore common performance bottlenecks in JavaScript applications on GreatFrontEnd
Each type of testing plays a crucial role in ensuring software quality across different levels of application functionality and integration.
Using these tools and techniques helps ensure JavaScript applications are secure against common vulnerabilities.
Content Security Policy (CSP) is a critical security feature designed to mitigate vulnerabilities like Cross-Site Scripting (XSS) and data injection attacks. By defining a whitelist of trusted sources for content such as scripts, stylesheets, and images, CSP restricts which resources a browser can load and execute on a webpage. This is typically set using HTTP headers or <meta> tags in HTML. For instance, the Content-Security-Policy header can specify that only scripts from the same origin ('self') are allowed to execute:
content-security-policy: script-src 'self';
This approach ensures that only trusted scripts from specified sources can run, enhancing the security of web applications by preventing unauthorized script execution and protecting against malicious code injection attempts.
Explore the concept of Content Security Policy (CSP) and how it enhances security on GreatFrontEnd
document.write()?document.write() is rarely used in modern web development because if called after the page has loaded, it can overwrite the entire document. It's typically reserved for simple tasks during initial page load, such as for educational purposes or quick debugging. Instead, it's generally recommended to use safer methods like innerHTML, appendChild(), or modern frameworks/libraries for more controlled and secure DOM manipulation.
Explore when to use document.write() on GreatFrontEnd
Well done, you've reached the end! These questions serve as a comprehensive guide to showcasing your breadth and depth of knowledge in JavaScript. If you're already familiar with all of them, that's fantastic! If not, don't be disheartened; view this as an opportunity to dive deeper into these intricate topics. Mastering these concepts will not only prepare you for advanced JavaScript interviews but also strengthen your overall technical expertise.

As a seasoned JavaScript developer with 5+ years of experience, you're likely no stranger to the intricacies of JavaScript. However, even the most experienced developers can benefit from a refresher on the most critical concepts and nuances of the language. In this article, we'll cover the top 20 JavaScript interview questions that can help you prepare effectively for your next interview.
If you're looking for additional JavaScript interview preparation materials, also check out these resources:
Anonymous functions provide a concise way to define functions, especially useful for simple operations or callbacks. They are commonly used in:
map(), filter(), and reduce().Checkout the below code example-
// Encapsulating Code using IIFE(function () {// Some code here.})();// CallbackssetTimeout(function () {console.log('Hello world!');}, 1000);// Functional programming constructsconst arr = [1, 2, 3];const double = arr.map(function (el) {return el * 2;});console.log(double); // [2, 4, 6]
Explore a typical use case for anonymous functions in JavaScript on GreatFrontEnd
A closure is a function that retains access to these variables even after the outer function has finished executing. This is like the function has a memory of its original environment.
function outerFunction() {const outerVar = 'I am outside of innerFunction';function innerFunction() {console.log(outerVar); // `innerFunction` can still access `outerVar`.}return innerFunction;}const inner = outerFunction(); // `inner` now holds a reference to `innerFunction`.inner(); // "I am outside of innerFunction"// Even though `outerFunction` has completed execution, `inner` still has access to variables defined inside `outerFunction`.
Closures are useful for:
Explore what a closure is in JavaScript, and why you would use one on GreatFrontEnd
Pros:
Avoid callback hell: Promises simplify nested callbacks.
// Callback hellgetData1((data) => {getData2(data, (data) => {getData3(data, (result) => {console.log(result);});});
Sequential code: Easier to write and read using .then().
Parallel code: Simplifies managing multiple promises with Promise.all().
Promise.all([getData1(), getData2(), getData3()]).then((results) => {console.log(results);}).catch((error) => {console.error('Error:', error);});
Cons:
Explore the pros and cons of using Promises instead of callbacks in JavaScript on GreatFrontEnd
AbortController in JavaScript?AbortController allows you to cancel ongoing asynchronous operations like fetch requests. To use it:
const controller = new AbortController();
2.Pass the signal: Add the signal to the fetch request options.controller.abort() to cancel the request.Here is an example of how to use AbortControllers with the fetch() API:
const controller = new AbortController();const signal = controller.signal;fetch('YOUR API', { signal }).then((response) => {// Handle response}).catch((error) => {if (error.name === 'AbortError') {console.log('Request aborted');} else {console.error('Error:', error);}});// Call abort() to abort the requestcontroller.abort();
Some of its use cases can be:
fetch() request on a user actionExplore how to abort a web request using AbortController in JavaScript on GreatFrontEnd
In JavaScript it's very easy to extend a built-in/native object. You can simply extend a built-in object by adding properties and functions to its prototype.
String.prototype.reverseString = function () {return this.split('').reverse().join('');};console.log('hello world'.reverseString()); // Outputs 'dlrow olleh'// Instead of extending the built-in object, write a pure utility function to do it.function reverseString(str) {return str.split('').reverse().join('');}console.log(reverseString('hello world')); // Outputs 'dlrow olleh'
While this may seem like a good idea at first, it is dangerous in practice. Imagine your code uses a few libraries that both extend the Array.prototype by adding the same contains method, the implementations will overwrite each other and your code will have unpredictable behavior if these two methods do not work the same way.
Extending built-in objects can lead to issues such as:
Explore why extending built-in JavaScript objects is not a good idea on GreatFrontEnd
In the browser, the global scope refers to the top-level context where variables, functions, and objects are accessible throughout the code. This scope is represented by the window object. Variables and functions declared outside of any function or block (excluding modules) are added to the window object, making them globally accessible.
For example:
// This runs in the global scope, not within a module.let globalVar = 'Hello, world!';function greet() {console.log('Greetings from the global scope!');}console.log(window.globalVar); // 'Hello, world!'window.greet(); // 'Greetings from the global scope!'
In this example, globalVar and greet are attached to the window object and can be accessed from anywhere in the global scope.
Generally, it's advisable to avoid polluting the global namespace unless necessary. Key reasons include:
In JavaScript, modules are reusable pieces of code that encapsulate functionality, making it easier to manage, maintain, and structure your applications. Modules allow you to break down your code into smaller, manageable parts, each with its own scope.
CommonJS is an older module system that was initially designed for server-side JavaScript development with Node.js. It uses the require() function to load modules and the module.exports or exports object to define the exports of a module.
// my-module.jsconst value = 42;module.exports = { value };// main.jsconst myModule = require('./my-module.js');console.log(myModule.value); // 42
ES Modules (ECMAScript Modules) are the standardized module system introduced in ES6 (ECMAScript 2015). They use the import and export statements to handle module dependencies.
// my-module.jsexport const value = 42;// main.jsimport { value } from './my-module.js';console.log(value); // 42
Explore the differences between CommonJS modules and ES modules in JavaScript on GreatFrontEnd
Immutability is a core principle in functional programming but it has lots to offer to object-oriented programs as well.
Mutable objects in JavaScript allow for modifications to their properties and values after creation. This behavior is default for most objects.
let mutableObject = {name: 'John',age: 30,};// Modify the objectmutableObject.name = 'Jane';console.log(mutableObject); // Output: { name: 'Jane', age: 30 }
Mutable objects like mutableObject above can have their properties changed directly, making them flexible for dynamic updates.
In contrast, immutable objects cannot be modified once created. Any attempt to change their content results in the creation of a new object with the updated values.
const immutableObject = Object.freeze({name: 'John',age: 30,});// Attempting to modify the objectimmutableObject.name = 'Jane'; // This change won't affect the objectconsole.log(immutableObject); // Output: { name: 'John', age: 30 }
Here, immutableObject remains unchanged after creation due to Object.freeze(), which prevents modifications to its properties.
The primary difference lies in modifiability. Mutable objects allow changes to their properties directly, while immutable objects ensure the integrity of their initial state by disallowing direct modifications.
const vs immutable objectsA common confusion is that declaring a variable using const makes the value immutable, which is not true at all.
Using const prevents the reassignment of variables but doesn't make non-primitive values immutable.
// Using constconst person = { name: 'John' };person = { name: 'Jane' }; // Error: Assignment to constant variableperson.name = 'Jane'; // Allowed, person.name is now 'Jane'// Using Object.freeze() to create an immutable objectconst frozenPerson = Object.freeze({ name: 'John' });frozenPerson.name = 'Jane'; // Fails silently (no error, but no change)frozenPerson = { name: 'Jane' }; // Error: Assignment to constant variable
In the first example with const, reassigning a new object to person is not allowed, but modifying the name property is permitted. In the second example, Object.freeze() makes the frozenPerson object immutable, preventing any changes to its properties.
Explore the difference between mutable and immutable objects in JavaScript on GreatFrontEnd
Static class members in JavaScript, denoted by the static keyword, are accessed directly on the class itself, not on instances. They serve multiple purposes:
class Config {static API_KEY = 'your-api-key';static FEATURE_FLAG = true;}console.log(Config.API_KEY); // Output: 'your-api-key'console.log(Config.FEATURE_FLAG); // Output: true
class Arithmetic {static add(a, b) {return a + b;}static subtract(a, b) {return a - b;}}console.log(Arithmetic.add(2, 3)); // Output: 5console.log(Arithmetic.subtract(5, 2)); // Output: 3
class Singleton {static instance;static getInstance() {if (!this.instance) {this.instance = new Singleton();}return this.instance;}}const singleton1 = Singleton.getInstance();const singleton2 = Singleton.getInstance();console.log(singleton1 === singleton2); // Output: true
Explore why you might want to create static class members in JavaScript on GreatFrontEnd
Symbols used for in JavaScript?Symbols in JavaScript, introduced in ES6, are unique and immutable identifiers primarily used as object property keys to avoid name collisions. They can be created using the Symbol() function, ensuring each Symbol value is unique even if descriptions are identical. Symbol properties are non-enumerable, making them suitable for private object state.
const sym1 = Symbol();const sym2 = Symbol('uniqueKey');console.log(typeof sym1); // "symbol"console.log(sym1 === sym2); // false, each symbol is uniqueconst obj = {};const sym = Symbol('uniqueKey');obj[sym] = 'value';console.log(obj[sym]); // "value"
Key characteristics include:
for...in loops or Object.keys().Global Symbols can be created using Symbol.for('key'), allowing reuse across different parts of codebases:
const globalSym1 = Symbol.for('globalKey');const globalSym2 = Symbol.for('globalKey');console.log(globalSym1 === globalSym2); // trueconst key = Symbol.keyFor(globalSym1);console.log(key); // "globalKey"
There are some well known Symbol in JavaScript like:
Symbol.iterator: Defines the default iterator for an object.Symbol.toStringTag: Used to create a string description for an object.Symbol.hasInstance: Used to determine if an object is an instance of a constructor.Explore what Symbols are used for in JavaScript on GreatFrontEnd
JavaScript object getters and setters are essential for controlling access to object properties, offering customization when getting or setting values.
const user = {_firstName: 'John',_lastName: 'Doe',get fullName() {return `${this._firstName} ${this._lastName}`;},set fullName(value) {const parts = value.split(' ');this._firstName = parts[0];this._lastName = parts[1];},};console.log(user.fullName); // Output: 'John Doe'user.fullName = 'Jane Smith';console.log(user.fullName); // Output: 'Jane Smith'
Getters (fullName) compute values based on internal properties (_firstName and _lastName), while setters (fullName) update these properties based on assigned values ('Jane Smith'). These mechanisms enhance data encapsulation and allow for custom data handling in JavaScript objects.
Explore what JavaScript object getters and setters are for on GreatFrontEnd
Tools and techniques for debugging JavaScript code vary depending on the context:
debugger statement: Inserting debugger; in code triggers breakpoints when Devtools are open, pausing execution for inspection.console.log() debugging: Using console.log() statements to output variable values and debug messages.Explore what tools and techniques are used for debugging JavaScript code on GreatFrontEnd
Currying in JavaScript is a functional programming technique where a function with multiple arguments is transformed into a sequence of nested functions, each taking a single argument. This allows for partial application of the function's arguments, meaning you can fix some arguments ahead of time and then apply the remaining arguments later.
Here's a simple example of a curry function and why this syntax offers an advantage:
// Example of a curry functionfunction curry(fn) {return function curried(...args) {if (args.length >= fn.length) {return fn(...args);} else {return function (...moreArgs) {return curried(...args, ...moreArgs);};}};}// Example function to be curriedfunction multiply(a, b, c) {return a * b * c;}// Currying the multiply functionconst curriedMultiply = curry(multiply);// Applying curried functionsconst step1 = curriedMultiply(2); // partially apply 2const step2 = step1(3); // partially apply 3const result = step2(4); // apply the final argumentconsole.log(result); // Output: 24
Advantages of Curry Syntax:
Currying enhances the functional programming paradigm in JavaScript by enabling concise, composable, and reusable functions, promoting cleaner and more modular code.
Explore examples of a curry function and why this syntax offers an advantage on GreatFrontEnd
load event and the document DOMContentLoaded event?The DOMContentLoaded event is triggered once the initial HTML document has been fully loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading.
In contrast, the window's load event is fired only after the DOM and all dependent resources, such as stylesheets, images, and subframes, have completely loaded.
JSONP (JSON with Padding) is a technique used to circumvent cross-domain restrictions in web browsers, as standard Ajax requests to different domains are generally blocked.
Instead of using Ajax, JSONP makes a request to a cross-origin domain by dynamically creating a <script> tag with a callback query parameter, such as: https://example.com?callback=handleResponse. The server wraps the data in a function named handleResponse and returns it.
<script>function handleResponse(data) {console.log(`User: ${data.username}`);}</script><script src="<https://example.com?callback=handleResponse>"></script>
For this to work, the client must define the handleResponse function in the global scope, which will be invoked when the response is received.
JSONP poses security risks because it executes JavaScript from external sources. Therefore, it's crucial to trust the JSONP provider.
Nowadays, CORS is the preferred method, making JSONP largely obsolete.
Explore how JSONP works (and how it's not really Ajax) on GreatFrontEnd
The same-origin policy restricts JavaScript from making requests to different domains. An origin is specified by the combination of the URI scheme, hostname, and port number. This policy is crucial for security, as it prevents a malicious script on one page from accessing sensitive data on another page's Document Object Model (DOM). This ensures that data remains secure within its designated origin, blocking unauthorized cross-origin interactions.
Explore how JSONP works (and how it's not really Ajax) on GreatFrontEnd
Single Page Apps (SPAs) are highly interactive web applications that load a single HTML page and dynamically update content as the user interacts with the app. Unlike traditional server-side rendering, SPAs use client-side rendering, fetching new data via AJAX without full-page refreshes. This approach makes the app more responsive and reduces the number of HTTP requests.
Pros:
Cons:
To enhance SEO for SPAs, consider:
Explore what a single page app is and how to make one SEO-friendly on GreatFrontEnd
In the past, developers often used Backbone for models, promoting an OOP approach by creating Backbone models and attaching methods to them.
While the module pattern remains useful, modern development often favors React/Redux, which employs a single-directional data flow based on the Flux architecture. Here, app data models are typically represented using plain objects, with utility pure functions to manipulate these objects. State changes are handled using actions and reducers, following Redux principles.
Avoid classical inheritance when possible. If you must use it, adhere to best practices and guidelines.
Explore how to organize your code on GreatFrontEnd
Possess working knowledge of it. Promises can be fulfilled, rejected, or pending, and allow users to attach callbacks for handling outcomes.
Common polyfills include $.deferred, Q, and Bluebird. However, with ES2015 providing native support, polyfills are typically unnecessary.
Explore what promises are on GreatFrontEnd
Attributes: Defined in HTML tags, they provide initial info for the browser (like "Hello" in <input type="text" value="Hello">).
Properties: Belong to the DOM (JavaScript's view of the page), allowing you to access and change element info after the page loads (like updating the text field value).
Explore the difference between an "attribute" and a "property" on GreatFrontEnd
You made it till the end! I hope you found these JavaScript questions helpful in preparing for your next interview. As an experienced developer, mastering both foundational and advanced concepts is key to showcasing your expertise and acing your next interview. But the most important thing to remember is that it's okay to not know everything - it's all about being willing to learn and improve.

As a JavaScript developer with 2 years of experience, you've already demonstrated your skills in building robust and scalable applications. However, the interview process can still be daunting, especially when faced with tricky technical questions.
To help you prepare and showcase your expertise, we've curated a list of 30 JavaScript interview questions that are tailored to your level of experience. These questions cover advanced topics such as performance optimization, design patterns, and more, and are designed to help you demonstrate your skills and confidence in your next interviews.
If you're looking for additional JavaScript interview preparation materials, also check out these resources:
Caching involves storing copies of files or data temporarily to speed up access times. It enhances performance by minimizing the frequency of fetching data from its original source. In web development, caching techniques include utilizing browser caches, service workers, and HTTP headers such as Cache-Control to effectively implement this optimization.
Explore the concept of caching and how it can be used to improve performance on GreatFrontEnd
Lazy loading is a design approach that defers the loading of resources until they are required. This can notably enhance performance by decreasing initial load times and conserving bandwidth. For instance, in web development, images can be lazily loaded, ensuring they are fetched only when they enter the viewport. This is facilitated using techniques like the HTML loading="lazy" attribute or through JavaScript libraries designed for this purpose.
<img src="image.jpg" loading="lazy" alt="Lazy loaded image" />
Explore the concept of lazy loading and how it can improve performance on GreatFrontEnd
Design patterns offer reusable solutions to typical software design challenges, serving as a blueprint for solving problems across various contexts. They are beneficial as they guide developers in sidestepping common issues, enhancing code clarity, and simplifying the maintenance and scalability of applications.
Explore what design patterns are and why they are useful on GreatFrontEnd
The Prototype pattern is a creational pattern used to create new objects by copying an existing object, known as the prototype. This pattern is advantageous when creating a new object is more resource-intensive than cloning an existing one. In JavaScript, you can implement this pattern using methods like Object.create or by utilizing the prototype property of a constructor functions.
const prototypeObject = {greet() {console.log('Hello, world!');},};const newObject = Object.create(prototypeObject);newObject.greet(); // Outputs: Hello, world!
This pattern allows objects to inherit properties and methods from a prototype, promoting code reuse and maintaining a clear structure in object-oriented programming.
Explore the concept of the Prototype pattern on GreatFrontEnd
The Singleton pattern ensures that a class has only one instance and provides a global access point to that instance. It is beneficial when you need precisely one object to manage tasks or resources system-wide. In JavaScript, you can implement the Singleton pattern using closures or ES6 classes to ensure there is only one instance of a class.
class Singleton {constructor() {if (!Singleton.instance) {Singleton.instance = this;}return Singleton.instance;}}const instance1 = new Singleton();const instance2 = new Singleton();console.log(instance1 === instance2); // true
This pattern is useful in scenarios like managing configurations, logging, and resource sharing across an application, ensuring consistency and preventing multiple instances from being created unnecessarily.
Explore the concept of the Singleton pattern on GreatFrontEnd
The Factory pattern in software design enables object creation without specifying their exact class upfront. It encapsulates complex instantiation logic and is ideal for situations where object types are determined dynamically at runtime. In JavaScript, this pattern can be implemented using a factory function to create various objects based on conditions:
function createAnimal(type) {if (type === 'dog') {return { sound: 'woof' };} else if (type === 'cat') {return { sound: 'meow' };}}const dog = createAnimal('dog');const cat = createAnimal('cat');
This approach promotes code flexibility and modularity by centralizing object creation logic.
Explore the Factory pattern and how it is used on GreatFrontEnd
The Observer pattern is a design pattern where an object, called the subject, maintains a list of its dependents, known as observers, and notifies them of any state changes. This pattern facilitates loose coupling between objects, making it useful for implementing event-driven architectures, real-time updates in user interfaces, and data synchronization across different parts of an application. It enables components to react dynamically to changes without explicitly knowing each other, promoting flexibility and maintainability in software design.
Explore the Observer pattern and its use cases on GreatFrontEnd
The Decorator pattern is a structural design pattern that allows behavior to be added to objects without affecting other instances of the same class. It wraps objects with additional functionality, extending their capabilities. For example:
class Car {drive() {return 'Driving';}}class CarDecorator {constructor(car) {this.car = car;}drive() {return this.car.drive();}}class GPSDecorator extends CarDecorator {drive() {return `${super.drive()} with GPS`;}}const myCar = new Car();const myCarWithGPS = new GPSDecorator(myCar);console.log(myCarWithGPS.drive()); // Outputs: "Driving with GPS"
Here, CarDecorator and GPSDecorator dynamically add features like GPS to a basic Car object, demonstrating how decorators can extend object functionalities.
Explore the Decorator pattern and how is it used on GreatFrontEnd
The Strategy pattern is a behavioral design pattern that allows you to encapsulate different algorithms into separate classes that are interchangeable. It enables the selection of algorithms at runtime without modifying client code. Here’s a concise example:
class Context {constructor(strategy) {this.strategy = strategy;}execute(data) {return this.strategy.algorithm(data);}}class ConcreteStrategyA {algorithm(data) {// Implementation of algorithm Areturn data.sort(); // Example: sorting algorithm}}class ConcreteStrategyB {algorithm(data) {// Implementation of algorithm Breturn data.reverse(); // Example: reverse algorithm}}// Usageconst context = new Context(new ConcreteStrategyA());const data = [3, 1, 2];console.log(context.execute(data)); // Outputs: [1, 2, 3]context.strategy = new ConcreteStrategyB();console.log(context.execute(data)); // Outputs: [3, 2, 1]
In this pattern, Context manages the selected strategy object, which performs its specific algorithm on data. This approach allows flexible algorithm switching and enhances code maintainability by separating algorithms from client code.
Explore the concept of the Strategy pattern on GreatFrontEnd
The Command pattern is a behavioral design pattern that turns a request into a stand-alone object containing all information about the request. This transformation allows for parameterization of methods with different requests, queuing of requests, and logging of the requests. It also supports undoable operations. In JavaScript, it can be implemented by creating command objects with execute and undo methods.
class Command {execute() {}undo() {}}class LightOnCommand extends Command {constructor(light) {super();this.light = light;}execute() {this.light.on();}undo() {this.light.off();}}class Light {on() {console.log('Light is on');}off() {console.log('Light is off');}}const light = new Light();const lightOnCommand = new LightOnCommand(light);lightOnCommand.execute(); // Light is onlightOnCommand.undo(); // Light is off
Explore the Command pattern and how it is used on GreatFrontEnd
The Module pattern in JavaScript is a design pattern used to create self-contained modules of code. It helps with encapsulation by allowing you to define private and public members within a module. Private members are not accessible from outside the module, while public members are exposed through a returned object. This pattern helps in organizing code, avoiding global namespace pollution, and maintaining a clean separation of concerns.
var myModule = (function () {var privateVar = 'I am private';function privateMethod() {console.log(privateVar);}return {publicMethod: function () {privateMethod();},};})();myModule.publicMethod(); // Logs: I am private
Explore the Module pattern and how it helps with encapsulation on GreatFrontEnd
To avoid issues related to hoisting in JavaScript, use let or const to declare variables instead of var. Unlike var, let and const are block-scoped, meaning they are only accessible within the block they are defined in and are not hoisted to the top of the scope. Additionally, ensure functions are declared before they are called to prevent any unexpected behavior due to function hoisting.
// Use let or constlet x = 10;const y = 20;// Declare functions before calling themfunction myFunction() {console.log('Hello, world!');}myFunction();
Explore how you can avoid problems related to hoisting on GreatFrontEnd
To share code between JavaScript files, you can use modules. In modern JavaScript, ES6 modules with export and import statements are commonly used. Here's how you can export a function from one file and import it into another:
Using ES6 Modules:
// file1.jsexport function greet() {console.log('Hello, world!');}// file2.jsimport { greet } from './file1.js';greet();
Alternatively, in Node.js, you can use module.exports and require:
Using CommonJS Modules (Node.js):
// file1.jsmodule.exports = function greet() {console.log('Hello, world!');};// file2.jsconst greet = require('./file1.js');greet();
Explore you can share code between JavaScript files on GreatFrontEnd
To retrieve query string values from the current page's URL in JavaScript using URLSearchParams, you can follow these steps:
// Assuming the URL is: http://example.com/page?key=value&foo=bar// Create a URLSearchParams object from the current page's query stringconst params = new URLSearchParams(window.location.search);// Retrieve specific query parameter valuesconst keyValue = params.get('key'); // 'value'const fooValue = params.get('foo'); // 'bar'// Example usageconsole.log(keyValue); // Outputs: 'value'console.log(fooValue); // Outputs: 'bar'
This approach allows you to easily access and manipulate query string parameters directly from the browser's URL.
Explore how to get the query string values of the current page in JavaScript on GreatFrontEnd
Handling errors in asynchronous operations can be done effectively with both async/await and Promises:
async/await with try...catch:javascriptCopy codeasync function fetchData() {try {const response = await fetch('https://api.example.com/data');if (!response.ok) throw new Error('Failed to fetch data');const data = await response.json();console.log(data);} catch (error) {console.error('Error fetching data:', error.message);}}
.catch() method:javascriptCopy codefetch('https://api.example.com/data').then(response => {if (!response.ok) throw new Error('Failed to fetch data');return response.json();}).then(data => console.log(data)).catch(error => console.error('Error fetching data:', error.message));
These methods ensure that errors, such as network failures or failed requests, are caught and handled appropriately, maintaining robust error management in your JavaScript applications.
Explore how to handle errors in asynchronous operations on GreatFrontEnd
You can manipulate CSS styles in JavaScript by directly accessing an element's style property for specific changes like background color or font size:
// Changing background colordocument.getElementById('myDiv').style.backgroundColor = 'blue';
You can also add, remove, or toggle CSS classes using the classList property:
document.getElementById('myDiv').classList.add('newClass');document.getElementById('myDiv').classList.remove('oldClass');document.getElementById('myDiv').classList.toggle('toggleClass');
Explore how to manipulate CSS styles using JavaScript on GreatFrontEnd
this keyword?Using the this keyword can be tricky because its value depends on the function's invocation context. Common pitfalls include losing this context when passing methods as callbacks, using this inside nested functions, and misunderstanding this in arrow functions. To address these issues, developers often use methods like .bind(), arrow functions, or store this context in a variable.
Explore common pitfalls of using the this keyword on GreatFrontEnd
The DOM, or Document Object Model, is a programming interface for web documents. It represents the page so that programs can change the document structure, style, and content. The DOM is structured as a tree of objects, where each node represents part of the document, such as elements, attributes, and text nodes.
Explore what the DOM is and how it is structured on GreatFrontEnd
AMD (Asynchronous Module Definition) and CommonJS are JavaScript module systems. AMD focuses on asynchronous loading, ideal for browsers, using define() and require(). CommonJS, geared towards server-side environments like Node.js, employs module.exports and require() for synchronous module loading.
Explore AMD vs CommonJS on GreatFrontEnd
In JavaScript, there are several methods to make API calls. The traditional way is using XMLHttpRequest, which is more verbose. fetch is a modern approach that returns promises, making it easier to handle responses. Alternatively, Axios is a widely-used third-party library that simplifies API calls and offers additional features.
Explore different ways to make an API call in JavaScript on GreatFrontEnd
For JavaScript testing, tools like Jest, Mocha, Jasmine, and Cypress are commonly used. Jest is praised for its simplicity and built-in functionalities. Mocha offers flexibility and can be integrated with various libraries. Jasmine is known for its straightforward setup and behavior-driven development (BDD) approach. Cypress excels in end-to-end testing, emphasizing real browser interactions.
Explore some tools that can be used for JavaScript testing on GreatFrontEnd
event.preventDefault() and event.stopPropagation()?event.preventDefault() prevents the default action of an event, like stopping a form submission whereas event.stopPropagation() prevents the event from bubbling up to parent elements.
Explore the difference between event.preventDefault() and event.stopPropagation() on GreatFrontEnd
innerHTML and textContent?innerHTML returns or sets the HTML markup inside an element, allowing HTML tags to be parsed and rendered whereas textContent retrieves or sets the text content inside an element, rendering HTML tags as plain text.
// Example of innerHTMLelement.innerHTML = '<strong>Bold Text</strong>'; // Renders as bold text// Example of textContentelement.textContent = '<strong>Bold Text</strong>'; // Renders as plain text: <strong>Bold Text</strong>
Explore the difference between innerHTML and textContent on GreatFrontEnd
window object and the document object?The window object represents the browser window, offering methods to control it (e.g., opening new windows, or accessing browser history). The document object represents the web page's content within the window, providing methods to manipulate the DOM (e.g., selecting elements, and modifying content).
Explore the difference between the window object and the document object on GreatFrontEnd
setTimeout(), setImmediate(), and process.nextTick()?setTimeout() schedules a callback to run after a minimum delay. setImmediate() schedules a callback to run after the current event loop completes. process.nextTick() schedules a callback to run before the next event loop iteration begins.
setTimeout(() => console.log('setTimeout'), 0);setImmediate(() => console.log('setImmediate'));process.nextTick(() => console.log('nextTick'));
In this example, process.nextTick() executes first, followed by either setTimeout() or setImmediate() depending on the environment.
Explore the difference between setTimeout(), setImmediate(), and process.nextTick() on GreatFrontEnd
window.history API?The window.history API allows you to manipulate the browser's session history. You can use history.pushState() to add a new entry to the history stack, history.replaceState() to modify the current entry, and history.back(), history.forward(), and history.go() to navigate through the history.
// Add a new entry to the historyhistory.pushState({ page: 1 }, 'title 1', '?page=1');// Replace the current history entryhistory.replaceState({ page: 2 }, 'title 2', '?page=2');// Navigate back, forward, or to a specific point in historyhistory.back(); // Go back one stephistory.forward(); // Go forward one stephistory.go(-2); // Go back two steps
Explore how to use window.history API on GreatFrontEnd
Pros of Promises over Callbacks:
.then(), improving readability and maintainability.Promise.all() for parallel asynchronous operations, handling multiple promises concisely.Cons:
Explore the pros and cons of using Promises instead of callbacks in JavaScript on GreatFrontEnd
Metadata fields of a module often include the module's name, version, description, author, license, and dependencies. These fields are commonly found in a package.json file in JavaScript projects.
Example:
{"name": "my-module","version": "1.0.0","description": "A sample module","author": "John Doe","license": "MIT","dependencies": {"express": "^4.17.1"}}
These fields provide essential information about the module and its requirements.
Explore what the metadata fields of a module are on GreatFrontEnd
In JavaScript, there are three main types of errors:
undefined.Explore different types of errors in JavaScript on GreatFrontEnd
Error propagation in JavaScript refers to the process of passing errors up the call stack. When an error occurs in a function, it can be caught and handled with try...catch blocks. If not caught, the error moves up the call stack until it is either caught or causes the program to terminate. For example:
function a() {throw new Error('An error occurred');}function b() {a();}try {b();} catch (e) {console.error(e.message); // Outputs: An error occurred}
In this example, the error thrown in function a propagates to function b and is caught in the try...catch block.
Explore the concept of error propagation in JavaScript on GreatFrontEnd
You've reached the end of our list of 30 JavaScript interview questions! We hope these questions have helped you identify areas for improvement and solidify your understanding of advanced JavaScript concepts. Remember, the key to acing an interview is not just about knowing the answers, but also about demonstrating your thought process, problem-solving skills, and ability to communicate complex ideas simply.

JavaScript is a fundamental skill for any aspiring web developer, and landing a job in this field can be a challenging task, especially for freshers. One of the most crucial steps in the interview process is the technical interview, where your JavaScript skills are put to the test.
To help you prepare and boost your confidence, we've compiled a list of the top 50 basic JavaScript interview questions and answers that are commonly asked in interviews.
If you're looking for additional JavaScript interview preparation materials, also check out these resources:
Hoisting describes the behavior of variable declarations in JavaScript. Declarations using var are "moved" to the top of their scope during compilation. Only the declaration is hoisted, not the initialization.
Example with var:
console.log(foo); // undefinedvar foo = 1;console.log(foo); // 1
Visualized as:
var foo;console.log(foo); // undefinedfoo = 1;console.log(foo); // 1
Variables with let, const, and class:
These are also hoisted but not initialized. Accessing them before declaration results in a ReferenceError.
console.log(y); // ReferenceError: Cannot access 'y' before initializationlet y = 'local';console.log(z); // ReferenceError: Cannot access 'z' before initializationconst z = 'local';console.log(Foo); // ReferenceError: Cannot access 'Foo' before initializationclass Foo {constructor() {}}
Function Expressions:
Only the declaration is hoisted.
console.log(bar); // undefinedbar(); // Uncaught TypeError: bar is not a functionvar bar = function () {console.log('BARRRR');};
Function Declarations:
Both declaration and definition are hoisted.
console.log(foo); // [Function: foo]foo(); // 'FOOOOO'function foo() {console.log('FOOOOO');}
Import Statements:
Imports are hoisted, making them available throughout the module, with side effects occurring before other code runs.
foo.doSomething(); // Works normally.import foo from './modules/foo';
Explore the concept of "hoisting" in JavaScript on GreatFrontEnd
let, var or const?var: Function-scoped or globally scoped.let and const: Block-scoped (only accessible within the nearest set of curly braces).Example:
function foo() {var bar = 1;let baz = 2;const qux = 3;console.log(bar); // 1console.log(baz); // 2console.log(qux); // 3}console.log(bar); // ReferenceErrorconsole.log(baz); // ReferenceErrorconsole.log(qux); // ReferenceError
if (true) {var bar = 1;let baz = 2;const qux = 3;}console.log(bar); // 1console.log(baz); // ReferenceErrorconsole.log(qux); // ReferenceError
var and let: Can be declared without an initial value.const: Must be initialized at the time of declaration.Example:
var foo; // Oklet bar; // Okconst baz; // SyntaxError
var: Allows redeclaration.let and const: Do not allow redeclaration.Example:
var foo = 1;var foo = 2; // Oklet baz = 3;let baz = 4; // SyntaxError
var and let: Allow reassignment.const: Does not allow reassignment.Example:
var foo = 1;foo = 2; // Oklet bar = 3;bar = 4; // Okconst baz = 5;baz = 6; // TypeError
var: Variables are hoisted and initialized to undefined.let and const: Variables are hoisted but not initialized, causing a ReferenceError if accessed before declaration.Example:
console.log(foo); // undefinedvar foo = 'foo';console.log(baz); // ReferenceErrorlet baz = 'baz';console.log(bar); // ReferenceErrorconst bar = 'bar';
== and === in JavaScript?==)Examples:
42 == '42'; // true0 == false; // truenull == undefined; // true[] == false; // true'' == false; // true
###Strict Equality Operator (===)
true.Examples:
42 === '42'; // false0 === false; // falsenull === undefined; // false[] === false; // false'' === false; // false
Use == only when comparing against null or undefined for convenience.
var a = null;console.log(a == null); // trueconsole.log(a == undefined); // true
Prefer === for all other comparisons to avoid pitfalls of type coercion and ensure both value and type are the same.
Explore the difference between == and === in JavaScript on GreatFrontEnd
The event loop is crucial for handling asynchronous operations in JavaScript, allowing single-threaded execution without blocking.
1. Call Stack:
2. Web APIs/Node.js APIs:
setTimeout(), HTTP requests) on separate threads.3. Task Queue (Macrotask Queue):
setTimeout(), setInterval(), and UI events.4. Microtask Queue:
Promise callbacks).console.log('Start');setTimeout(() => {console.log('Timeout 1');}, 0);Promise.resolve().then(() => {console.log('Promise 1');});setTimeout(() => {console.log('Timeout 2');}, 0);console.log('End');
Console Output:
StartEndPromise 1Timeout 1Timeout 2
Explanation:
Start and End are logged first (synchronous).Promise 1 is logged next (microtask).Timeout 1 and Timeout 2 are logged last (macrotasks).Understanding the event loop helps write efficient, non-blocking JavaScript code.
Explore the event loop in JavaScript on GreatFrontEnd
Event delegation is an efficient way to handle events on multiple child elements by attaching a single event listener to a common parent element. This is useful for managing events on many similar elements, like list items.
event.target to identify the actual element that triggered the event.// HTML:// <ul id="item-list">// <li>Item 1</li>// <li>Item 2</li>// <li>Item 3</li>// </ul>const itemList = document.getElementById('item-list');itemList.addEventListener('click', (event) => {if (event.target.tagName === 'LI') {console.log(`Clicked on ${event.target.textContent}`);}});
A single click listener on <ul> handles clicks on any <li> due to event bubbling.
Dynamic Content:
const buttonContainer = document.getElementById('button-container');const addButton = document.getElementById('add-button');buttonContainer.addEventListener('click', (event) => {if (event.target.tagName === 'BUTTON') {console.log(`Clicked on ${event.target.textContent}`);}});addButton.addEventListener('click', () => {const newButton = document.createElement('button');newButton.textContent = `Button ${buttonContainer.children.length + 1}`;buttonContainer.appendChild(newButton);});
Simplifying Code:
const userForm = document.getElementById('user-form');userForm.addEventListener('input', (event) => {const { name, value } = event.target;console.log(`Changed ${name}: ${value}`);});
this works in JavaScriptThe this keyword in JavaScript can be quite confusing as its value depends on how a function is called. Here are the main rules that determine the value of this:
new KeywordCreates a new object and sets this to that object.
function Person(name) {this.name = name;}const person = new Person('Alice');console.log(person.name); // 'Alice
apply, call, or bindExplicitly sets this to a specified object.
javascriptCopy codefunction greet() {console.log(this.name);}const person = { name: 'Alice' };greet.call(person); // 'Alice'
this is bound to the object the method is called on.
const obj = {name: 'Alice',greet: function () {console.log(this.name);},};obj.greet(); // 'Alice'
In non-strict mode, defaults to the global object (window in browsers); in strict mode, defaults to undefined.
function greet() {console.log(this); // global object or undefined}greet();
Inherit this from their lexical enclosing scope.
const obj = {name: 'Alice',greet: () => {console.log(this.name); // `this` refers to the enclosing scope},};obj.greet(); // undefined
thisES2015 introduced arrow functions which capture this from their lexical scope. This can simplify code but requires caution when integrating with libraries expecting traditional function context.
Example:
function Timer() {this.seconds = 0;setInterval(() => {this.seconds++; // `this` refers to the Timer instanceconsole.log(this.seconds);}, 1000);}const timer = new Timer();
Explore how this works in JavaScript on GreatFrontEnd
sessionStorage and localStorage.Cookies, localStorage, and sessionStorage are key client-side storage mechanisms in web applications, each serving distinct purposes:
Purpose: Stores small data pieces sent to the server with HTTP requests.
Capacity: Limited to around 4KB per domain.
Lifespan: Can have expiration dates; session cookies are cleared when the browser closes.
Access: Domain-specific; accessible across pages and subdomains.
Security: Supports HttpOnly and Secure flags to restrict JavaScript access and ensure HTTPS transmission.
Example Usage:
// Set a cookie with an expirydocument.cookie ='auth_token=abc123def; expires=Fri, 31 Dec 2024 23:59:59 GMT; path=/';// Read all cookies (no direct method for specific cookies)console.log(document.cookie);// Delete a cookiedocument.cookie ='auth_token=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';
localStoragePurpose: Stores data persistently on the client-side.
Capacity: Around 5MB per origin.
Lifespan: Data remains until explicitly cleared.
Access: Available across all tabs and windows within the same origin.
Security: All JavaScript on the page can access localStorage values.
Example Usage:
// Set an item in localStoragelocalStorage.setItem('key', 'value');// Get an item from localStorageconsole.log(localStorage.getItem('key'));// Remove an item from localStoragelocalStorage.removeItem('key');// Clear all data in localStoragelocalStorage.clear();
sessionStoragePurpose: Stores session-specific data that persists until the browser or tab is closed.
Capacity: Similar to localStorage, around 5MB per origin.
Lifespan: Cleared when the tab or browser closes; reloading the page retains data.
Access: Limited to the current tab or window.
Security: All JavaScript on the page can access sessionStorage values.
Example Usage:
// Set an item in sessionStoragesessionStorage.setItem('key', 'value');// Get an item from sessionStorageconsole.log(sessionStorage.getItem('key'));// Remove an item from sessionStoragesessionStorage.removeItem('key');// Clear all data in sessionStoragesessionStorage.clear();
Explore the difference between a cookie, sessionStorage and localStorage on GreatFrontEnd
<script>, <script async> and <script defer><script> TagThe <script> tag is used to include JavaScript in a web page. When used without async or defer attributes:
Example:
<!doctype html><html><head><title>Regular Script</title></head><body><h1>Regular Script Example</h1><p>This content appears before the script executes.</p><script src="regular.js"></script><p>This content appears after the script executes.</p></body></html>
<script async> TagExample:
<!doctype html><html><head><title>Async Script</title></head><body><h1>Async Script Example</h1><p>This content appears before the async script executes.</p><script async src="async.js"></script><p>This content may appear before or after the async script executes.</p></body></html>
<script defer> Tag:
DOMContentLoaded.Example:
<!doctype html><html><head><title>Deferred Script</title></head><body><h1>Deferred Script Example</h1><p>This content appears before the deferred script executes.</p><script defer src="deferred.js"></script><p>This content appears before the deferred script executes.</p></body></html>
Explore the difference between <script>, <script async> and <script defer> on GreatFrontEnd
null, undefined or undeclared?Undeclared: A variable that is not declared using var, let, or const will be created globally and can cause errors. Avoid them by using try/catch blocks to detect them.
undefined: A declared variable without an assigned value is undefined. Use === or typeof to check for undefined. Note that == will also return true for null.
null: A variable explicitly assigned null represents no value. Use === to check for null. Don't use == as it will also return true for undefined.
Best Practices:
null to variables if you don't intend to use them yet.Explore the difference between a variable that is: null, undefined or undeclared on GreatFrontEnd
.call and .apply in JavaScript?.call and .apply are used to invoke functions, setting this within the function. The difference lies in how they handle arguments:
Memory Aid:
Example:
javascriptCopy codefunction add(a, b) {return a + b;}console.log(add.call(null, 1, 2)); // 3console.log(add.apply(null, [1, 2])); // 3// ES6 with spread operatorconsole.log(add.call(null, ...[1, 2])); // 3
Explore the difference between .call and .apply in JavaScript on GreatFrontEnd
Function.prototype.bindFunction.prototype.bind creates a new function with a specific this context and optionally preset arguments. It's useful for maintaining the correct this value in methods passed to other functions.
Example:
const john = {age: 42,getAge: function () {return this.age;},};console.log(john.getAge()); // 42const unboundGetAge = john.getAge;console.log(unboundGetAge()); // undefinedconst boundGetAge = john.getAge.bind(john);console.log(boundGetAge()); // 42const mary = { age: 21 };const boundGetAgeMary = john.getAge.bind(mary);console.log(boundGetAgeMary()); // 21
Its main purposes are:
this to preserve context: The primary function of bind is to attach the this value of a function to a specific object. When you use func.bind(thisArg), it generates a new function with the same code as func, but with this permanently set to thisArg.bind also enables you to pre-set arguments for the new function. Any arguments provided to bind after thisArg will be prepended to the argument list when the new function is invoked.bind allows you to borrow methods from one object and use them on another object, even if the methods were not initially designed for that object.Explore Function.prototype.bind on GreatFrontEnd
The advantage of using the arrow syntax for a method in a constructor is that it automatically binds the this value to the constructor's this context. This means that when the method is called, it will always refer to the constructor's this context, rather than the global scope or some other unexpected context.
In traditional function expressions, the this value is determined by how the function is called, which can lead to unexpected behavior if not properly bound. By using an arrow function, you can ensure that the this value is always bound to the constructor's this context, making your code more predictable and easier to maintain.
For example, in the code snippet:
const Person = function (name) {this.name = name;this.sayName1 = function () {console.log(this.name);};this.sayName2 = () => {console.log(this.name);};};const john = new Person('John');const dave = new Person('Dave');john.sayName1(); // Johnjohn.sayName2(); // John// `this` can change for regular functions but not for arrow functionsjohn.sayName1.call(dave); // Davejohn.sayName2.call(dave); // John
The sayName1 method uses a traditional function expression, which means its this value is determined by how it's called. If you call john.sayName1.call(dave), the this value will be dave, and the method will log Dave to the console.
On the other hand, the sayName2 method uses an arrow function, which means its this value is automatically bound to the constructor's this context. If you call john.sayName2.call(dave), the this value will still be john, and the method will log John to the console.
This can be particularly helpful in React class components, where you often need to pass methods as props to child components. By using arrow functions, you can ensure that the methods always refer to the correct this context, without having to manually bind this in the constructor.
Explore the advantage for using the arrow syntax for a method in a constructor on GreatFrontEnd
Prototypical inheritance allows objects to inherit properties and methods from other objects using a prototype-based model.
Object.getPrototypeOf() and Object.setPrototypeOf().function Person(name, age) {this.name = name;this.age = age;}Person.prototype.sayHello = function () {console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`,);};let john = new Person('John', 30);john.sayHello(); // "Hello, my name is John and I am 30 years old."
JavaScript looks for properties/methods on the object, then its prototype, and so on up the chain until null.
Functions used with new to create objects, setting their prototype to the constructor's prototype.
function Animal(name) {this.name = name;}Animal.prototype.sayName = function () {console.log(`My name is ${this.name}`);};function Dog(name, breed) {Animal.call(this, name);this.breed = breed;}Dog.prototype = Object.create(Animal.prototype);Dog.prototype.bark = function () {console.log('Woof!');};let fido = new Dog('Fido', 'Labrador');fido.bark(); // "Woof!"fido.sayName(); // "My name is Fido"
Object.create()Creates a new object with a specified prototype.
let proto = {greet: function () {console.log(`Hello, my name is ${this.name}`);},};let person = Object.create(proto);person.name = 'John';person.greet(); // "Hello, my name is John"
Explore how prototypal inheritance works on GreatFrontEnd
function Person(){}, const person = Person(), and const person = new Person()?function Person(){} is a standard function declaration in JavaScript. When written in PascalCase, it follows the convention for functions intended to be used as constructors.
const person = Person() simply calls the function and executes its code. If no return value is specified, person will be undefined. This is not a constructor call and does not create a new object.
const person = new Person() creates a new object using the Person constructor function. The new keyword creates a new object and sets its prototype to Person.prototype. The this keyword inside the constructor function refers to the new object being created.
foo between function foo() {} and var foo = function() {}Syntax: function foo() {}
Description: Defines a named function that can be called throughout the enclosing scope.
Example:
function foo() {console.log('FOOOOO');}
Syntax: var foo = function() {}
Description: Defines a function and assigns it to a variable, often used in specific contexts.
Example:
var foo = function () {console.log('FOOOOO');};
Hoisting:
Function Declarations: The entire function is hoisted; can be called before its definition.
foo(); // 'FOOOOO'function foo() {console.log('FOOOOO');}
Function Expressions: Only the variable is hoisted, not the function body; calling it before definition results in an error.
foo(); // Uncaught TypeError: foo is not a functionvar foo = function () {console.log('FOOOOO');};
Name Scope:
Function Expressions: These can be named internally, but the name is only accessible within the function.
const myFunc = function namedFunc() {console.log(namedFunc); // Works};console.log(namedFunc); // undefined
Function Declarations:
Function Expressions:
Here are the various ways to create objects in JavaScript:
Object Literals ({}): Simplest way to create objects using key-value pairs within curly braces.
const person = {firstName: 'John',lastName: 'Doe',};
Object() Constructor: Using the new keyword with the built-in Object constructor to create an object.
const person = new Object();person.firstName = 'John';person.lastName = 'Doe';
Object.create() Method: Creating a new object using an existing object as a prototype.
const personPrototype = {greet() {console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`,);},};const person = Object.create(personPrototype);person.name = 'John';person.age = 30;person.greet(); // Output: Hello, my name is John and I'm 30 years old.
ES2015 Classes: Defining a blueprint for objects using classes, similar to other programming languages.
class Person {constructor(name, age) {this.name = name;this.age = age;}greet = function () {console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`,);};}const person = new Person('John', 30);person.greet(); // Output: Hello, my name is John and I'm 30 years old.
Constructor Functions: Reusable blueprints for objects, using the new keyword to create instances.
// Constructor functionfunction Person(name, age) {this.name = name;this.age = age;this.greet = function () {console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`,);};}const person = new Person('John', 30);person.greet(); // Output: Hello, my name is John and I'm 30 years old.
Note: Constructor functions are less commonly used now that ES2015 classes are widely supported.
Explore various ways to create objects in JavaScript on GreatFrontEnd
A higher-order function is a function that:
Takes another function as an argument: A function that accepts another function as a parameter.
function greet(name) {return `Hello, ${name}!`;}function greetName(greeter, name) {console.log(greeter(name));}greetName(greet, 'Alice'); // Output: Hello, Alice!
Returns a function as its result: A function that returns another function as its output.
function multiplier(factor) {return function (num) {return num * factor;};}const double = multiplier(2);console.log(double(5)); // Output: 10
In other words, a higher-order function is a function that operates on other functions, either by taking them as input or by producing them as output.
Explore the definition of a higher-order function on GreatFrontEnd
Uses function constructors and prototypes.
Example:
function Person(name, age) {this.name = name;this.age = age;}Person.prototype.greet = function () {console.log('Hello, my name is ' +this.name +' and I am ' +this.age +' years old.',);};var person1 = new Person('John', 30);person1.greet(); // Hello, my name is John and I am 30 years old.
Uses the class syntax, making code more readable and adding features.
Example:
class Person {constructor(name, age) {this.name = name;this.age = age;}greet() {console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`,);}}const person1 = new Person('John', 30);person1.greet(); // Hello, my name is John and I am 30 years old.
class keyword, more concise and easier to understandstatic keywordObject.create() and manual prototype chain settingextends keyword, simpler and more intuitivesuper keyword to call parent class's constructor and methodsExplore differences between ES2015 classes and ES5 function constructors on GreatFrontEnd
Event bubbling is a mechanism in the DOM (Document Object Model) where an event, such as a click, is first triggered on the target element and then propagates upward through the DOM tree to the root of the document.
Bubbling Phase:
Description: During the bubbling phase, the event starts at the target element and bubbles up through its ancestors in the DOM hierarchy. Event handlers attached to the target element and its ancestors can all potentially receive and respond to the event.
Example:
// HTML:// <div id="parent">// <button id="child">Click me!</button>// </div>const parent = document.getElementById('parent');const child = document.getElementById('child');parent.addEventListener('click', () => {console.log('Parent element clicked');});child.addEventListener('click', () => {console.log('Child element clicked');});
When you click the Click me! button, both the child and parent event handlers will be triggered due to event bubbling.
Stopping Event Bubbling:
Method: Use stopPropagation() to stop the event from bubbling up the DOM tree.
Example:
child.addEventListener('click', (event) => {console.log('Child element clicked');event.stopPropagation();});
Explore event bubbling on GreatFrontEnd
Event capturing is a propagation mechanism in the DOM where an event, such as a click, is first triggered at the root of the document and then flows down through the DOM tree to the target element.
Event Propagation Phases:
Enabling Event Capturing:
{ capture: true } as the third argument to addEventListener().Example:
// HTML:// <div id="parent">// <button id="child">Click me!</button>// </div>const parent = document.getElementById('parent');const child = document.getElementById('child');parent.addEventListener('click',() => {console.log('Parent element clicked (capturing)');},true, // Enable capturing phase);child.addEventListener('click', () => {console.log('Child element clicked');});
When you click the Click me! button, the parent element's capturing handler will be triggered before the child element's handler.
Stopping Propagation:
Use stopPropagation() to prevent the event from traveling further down the DOM tree during the capturing phase.
Example:
parent.addEventListener('click',(event) => {console.log('Parent element clicked (capturing)');event.stopPropagation(); // Stop event propagation},true,);child.addEventListener('click', () => {console.log('Child element clicked');});
In this example, only the parent event listener will be called when you click the "Click me!" button, as the event propagation is stopped at the parent element.
Explore event capturing on GreatFrontEnd
mouseenter and mouseover event in JavaScript and browsers?mouseentermouseoverExample:
const fs = require('fs');const data = fs.readFileSync('large-file.txt', 'utf8');console.log(data); // Blocks until file is readconsole.log('End of the program');
Example:
console.log('Start of the program');fetch('https://api.example.com/data').then((response) => response.json()).then((data) => console.log(data)) // Non-blocking.catch((error) => console.error(error));console.log('End of program');
Explore the difference between synchronous and asynchronous functions on GreatFrontEnd
AJAX is a set of web development techniques using various web technologies on the client side to create asynchronous web applications. Unlike traditional web applications where each user interaction triggers a full page reload, AJAX allows web applications to send data to and retrieve data from a server asynchronously without interfering with the display and behavior of the existing page. This enables dynamic updates to the web page without the need to reload it.
Key Points:
XMLHttpRequest, but fetch() is now preferred for modern web applications.XMLHttpRequest APIExample:
let xhr = new XMLHttpRequest();xhr.onreadystatechange = function () {if (xhr.readyState === XMLHttpRequest.DONE) {if (xhr.status === 200) {console.log(xhr.responseText);} else {console.error('Request failed: ' + xhr.status);}}};xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos/1', true);xhr.send();
XMLHttpRequest, sets up a callback function to handle state changes, opens a request to a URL, and sends the request.fetch() APIExample:
fetch('https://jsonplaceholder.typicode.com/todos/1').then((response) => {if (!response.ok) {throw new Error('Network response was not ok');}return response.json();}).then((data) => console.log(data)).catch((error) => console.error('Fetch error:', error));
.then() to parse JSON data, and manages errors with .catch().fetchfetch() initiates an asynchronous request to fetch a resource from a URL.
Example:
fetch('https://api.example.com/data', {method: 'GET', // or 'POST', 'PUT', 'DELETE', etc.headers: {'Content-Type': 'application/json',},});
fetch() returns a Promise that resolves to a Response object representing the server's response.The Response object offers methods to handle the body content, such as .json(), .text(), .blob().
Example:
fetch('https://api.example.com/data').then((response) => response.json()).then((data) => console.log(data)).catch((error) => console.error('Error:', error));
fetch() is asynchronous, allowing the browser to continue executing other tasks while waiting for the server response..then(), .catch()) are handled in the microtask queue as part of the event loop.fetch() configures various request aspects, such as HTTP method, headers, body, credentials, and caching..catch() or try/catch with async/await.Explore how to explain AJAX in as much detail as possible on GreatFrontEnd
AJAX (Asynchronous JavaScript and XML) enables web pages to send and retrieve data asynchronously, allowing for dynamic updates without full page reloads.
Explore the advantages and disadvantages of using AJAX on GreatFrontEnd
XMLHttpRequest and fetch()?Both XMLHttpRequest (XHR) and fetch() enable asynchronous HTTP requests in JavaScript, but differ in syntax, handling, and features.
setRequestHeader method.send method.body property in the options parameter.responseType to handle different formats..then for accessing data.onerror event..catch method.abort() method.AbortController for request cancellation.onprogress event.Choosing Between Them: fetch() is generally preferred due to its cleaner syntax and promise-based handling, but XMLHttpRequest may still be useful for specific cases like progress tracking.
Explore the differences between XMLHttpRequest and fetch() on GreatFrontEnd
JavaScript has various data types categorized into two groups: primitive and non-primitive (reference) types.
true or false.Determining Data Types: JavaScript is dynamically typed, meaning variables can hold different data types over time. Use the typeof operator to determine a variable's type.
Explore the various data types in JavaScript on GreatFrontEnd
Iterating over object properties and arrays is very common in JavaScript and we have various ways to achieve this. Here are some of the ways to do it:
for...in StatementLoops over all enumerable properties of an object, including inherited ones.
for (const property in obj) {if (Object.hasOwn(obj, property)) {console.log(property);}}
Object.keys()Returns an array of an object's own enumerable property names.
Object.keys(obj).forEach((property) => console.log(property));
Object.entries()Returns an array of a given object's own enumerable string-keyed property [key, value] pairs.
Object.entries(obj).forEach(([key, value]) =>console.log(`${key}: ${value}`),);
Object.getOwnPropertyNames()Returns an array of all properties (including non-enumerable ones) found directly upon a given object.
Object.getOwnPropertyNames(obj).forEach((property) =>console.log(property),);
for LoopTraditional loop over array elements.
for (let i = 0; i < arr.length; i++) {console.log(arr[i]);}
Array.prototype.forEach()Executes a provided function once for each array element.
arr.forEach((element, index) => console.log(element, index));
for...of StatementLoops over iterable objects like arrays.
for (let element of arr) {console.log(element);}
Array.prototype.entries()Provides both the index and value of each array element in a for...of loop.
for (let [index, elem] of arr.entries()) {console.log(index, ': ', elem);}
Introduced in ES2015, the spread syntax (...) is useful for copying and merging arrays and objects without modifying the originals. It's commonly used in functional programming, Redux, and RxJS.
Copying Arrays/Objects: Creates shallow copies.
const array = [1, 2, 3];const newArray = [...array]; // [1, 2, 3]const obj = { name: 'John', age: 30 };const newObj = { ...obj, city: 'New York' }; // { name: 'John', age: 30, city: 'New York' }
Merging Arrays/Objects: Merges them into a new one.
const arr1 = [1, 2, 3];const arr2 = [4, 5, 6];const mergedArray = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]const obj1 = { foo: 'bar' };const obj2 = { qux: 'baz' };const mergedObj = { ...obj1, ...obj2 }; // { foo: 'bar', qux: 'baz' }
Function Arguments: Passes array elements as individual arguments.
const numbers = [1, 2, 3];Math.max(...numbers); // Same as Math.max(1, 2, 3)
Array vs. Object Spreads: Only iterables can be spread into arrays; arrays can be spread into objects.
const array = [1, 2, 3];const obj = { ...array }; // { 0: 1, 1: 2, 2: 3 }
The rest syntax (...) gathers multiple elements into an array or object, the inverse of the spread syntax.
Function Parameters: Collects remaining arguments into an array.
function addFiveToNumbers(...numbers) {return numbers.map((x) => x + 5);}const result = addFiveToNumbers(4, 5, 6, 7); // [9, 10, 11, 12]
Array Destructuring: Collects remaining elements into a new array.
const [first, second, ...remaining] = [1, 2, 3, 4, 5];// first: 1, second: 2, remaining: [3, 4, 5]
Object Destructuring: Collects remaining properties into a new object.
const { e, f, ...others } = { e: 1, f: 2, g: 3, h: 4 };// e: 1, f: 2, others: { g: 3, h: 4 }
Rest Parameter Rules: Must be the last parameter.
function addFiveToNumbers(arg1, ...numbers, arg2) {// Error: Rest element must be last element.}
Map object and a plain object in JavaScript?size property to get the number of key-value pairs.forEach, keys(), values(), and entries().Object.keys(), Object.values(), or Object.entries() for iteration.// Mapconst map = new Map();map.set('key1', 'value1');map.set({ key: 'key2' }, 'value2');console.log(map.size); // 2// Plain Objectconst obj = { key1: 'value1' };obj[{ key: 'key2' }] = 'value2';console.log(Object.keys(obj).length); // 1 (keys are strings)
Explore the difference between a Map object and a plain object in JavaScript on GreatFrontEnd
Map/Set vs WeakMap/WeakSet?The main distinctions between Map/Set and WeakMap/WeakSet in JavaScript are as follows:
Map and Set accept keys of any type (objects, primitive values), whereas WeakMap and WeakSet exclusively use objects as keys, excluding primitive values like strings or numbers.Map and Set retain strong references to their keys and values, preventing their disposal by garbage collection. In contrast, WeakMap and WeakSet employ weak references for keys (objects), allowing these objects to be collected by garbage collection if no other strong references persist.Map and Set are enumerable and can be iterated over, while those in WeakMap and WeakSet are non-enumerable, precluding retrieval of key or value lists directly from them.Map and Set possess a size property that indicates the number of elements they contain. In contrast, WeakMap and WeakSet lack a size property because their size may vary as a result of garbage collection.Map and Set serve well as general-purpose data structures and for caching purposes. Conversely, WeakMap and WeakSet are primarily suited for storing metadata or additional object-related data without impeding the object's potential garbage collection when no longer needed.Explore the differences between Map/Set vs WeakMap/WeakSet on GreatFrontEnd
One practical use case for the arrow function syntax in JavaScript is simplifying callback functions, particularly in scenarios where you need concise, inline function definitions. Here's an example:
Use Case: Mapping an Array
Suppose you have an array of numbers and you want to double each number using the map function.
// Traditional function syntaxconst numbers = [1, 2, 3, 4, 5];const doubledNumbers = numbers.map(function (number) {return number * 2;});console.log(doubledNumbers); // Output: [2, 4, 6, 8, 10]
Using arrow function syntax, you can achieve the same result more succinctly:
// Arrow function syntaxconst numbers = [1, 2, 3, 4, 5];const doubledNumbers = numbers.map((number) => number * 2);console.log(doubledNumbers); // Output: [2, 4, 6, 8, 10]
Explore a use case for the new arrow => function syntax on GreatFrontEnd
In asynchronous programming, a callback function is passed as an argument to another function and invoked when a task completes, such as fetching data or handling I/O operations. Here's a concise explanation:
function fetchData(callback) {setTimeout(() => {const data = { name: