What's new

Here you'll find curated collection of our most insightful and engaging blog content, neatly organized into series for your convenience. Each series focuses on a unique theme or topic providing deep dive subject.
  • CSS Interview Questions Guide for 2026Complete guide to CSS interview questions for your next interview. Covers core concepts, layouts, responsive design, modern features, and Tailwind CSS essentials.
    Author
    GreatFrontEnd Team
    31 min read
    Dec 9, 2025
    CSS Interview Questions Guide for 2026

    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:

    • Core concepts that appear in 90% of CSS interviews
    • Decision-making frameworks for choosing between layout approaches
    • Scenario-based questions that test real-world problem-solving
    • Modern CSS features that separate junior from senior candidates
    • Tailwind CSS essentials for companies using utility-first approaches

    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.


    Core CSS concepts

    Box model

    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 width
    • border-box includes padding and border within the specified width
    • Most modern CSS resets use box-sizing: border-box globally
    • Margin is always outside the box, regardless of box-sizing

    Pro tip: Explain that border-box makes responsive layouts more predictable because percentage widths behave intuitively.


    Display property

    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?"

    PropertySpace OccupiedAccessible to Screen ReadersEvents TriggeredUse Case
    display: noneNoNoNoCompletely remove from layout
    visibility: hiddenYesNoNoHide but maintain layout space
    opacity: 0YesYesYesFade 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 variables

    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:

    FeatureCSS VariablesSass/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 variable
    const primary = getComputedStyle(document.documentElement).getPropertyValue(
    '--primary-color',
    );
    // Set CSS variable
    document.documentElement.style.setProperty('--primary-color', '#ff0000');

    Specificity & cascade

    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:

    • Inline styles: 1-0-0-0
    • IDs: 0-1-0-0
    • Classes, attributes, pseudo-classes: 0-0-1-0
    • Elements, pseudo-elements: 0-0-0-1

    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) */

    Positioning

    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:

    PositionUse CaseExample
    staticDefault flowRegular content
    relativeMinor adjustments, positioning contextOffset badges, anchor for absolute children
    absoluteOverlays, tooltipsDropdown menus, modals
    fixedPersistent UINavigation bars, chat widgets
    stickyScroll-aware headersTable 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

    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)
    • Each stacking context is independent
    • Children can't escape their parent's stacking context
    • Common stacking context creators: opacity, transform, filter, position + z-index

    Pseudo-classes & pseudo-elements

    Pseudo-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-classesPseudo-elements
    Select elements in a specific stateStyle specific parts of elements
    Single colon : (or ::)Double colon ::
    :hover, :focus, :nth-child()::before, ::after, ::first-line

    Units (em, rem, %, viewport)

    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:

    UnitBest ForExample
    pxBorders, shadows, precise controlborder: 1px solid
    emSpacing relative to font-sizepadding: 0.5em 1em
    remFont sizes, consistent spacingfont-size: 1.125rem
    %Responsive widths, fluid layoutswidth: 50%
    vw/vhFull-screen sections, responsive typographyheight: 100vh

    Layout fundamentals

    Flexbox essentials

    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 */
    }

    Grid basics

    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;
    }

    Flexbox vs Grid decision tree

    Interview question: "How do you decide between Flexbox and Grid?"

    Use Flexbox for:

    • One-dimensional layouts (row OR column)
    • Navigation menus
    • Centering content
    • Content-driven layouts (size based on content)

    Use Grid for:

    • Two-dimensional layouts (rows AND columns)
    • Page layouts (header, sidebar, content, footer)
    • Card grids
    • Layout-driven designs (size based on container)

    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 centering question

    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:

    MethodUse CaseProsCons
    FlexboxMost situationsSimple, flexibleRequires parent styling
    GridGrid layoutsVery conciseOverkill for simple cases
    Absolute + TransformOverlays, modalsWorks without knowing sizeRemoves from flow
    Margin autoBlock elementsSimple for horizontalVertical requires height

    Responsive & transforms

    Media queries essentials

    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:

    • Mobile-first is preferred because it's easier to progressively enhance
    • Always consider accessibility features like prefers-reduced-motion
    • Container queries are the future for component-based responsive design

    Transforms & transitions

    Transforms 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:

    • Only transform and opacity are GPU-accelerated
    • Avoid animating width, height, top, left - use transform instead
    • will-change hints to browser but use sparingly (memory cost)

    Responsive images

    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 -->
    <img
    src="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 features

    CSS functions (clamp, min, max)

    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

    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:

    • Components are truly reusable across different contexts
    • No need to know where a component will be placed
    • Better for component libraries and design systems
    • The future of responsive component design

    :has() parent selector

    The :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:

    • First true "parent selector" in CSS
    • Eliminates need for JavaScript in many cases
    • Enables conditional styling based on content

    :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 specificity

    Logical properties

    Logical 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:

    PhysicalLogical
    margin-leftmargin-inline-start
    margin-rightmargin-inline-end
    margin-topmargin-block-start
    margin-bottommargin-block-end
    widthinline-size
    heightblock-size

    Why logical properties matter:

    • Internationalization (i18n) support built-in
    • No need for separate RTL stylesheets
    • Future-proof for vertical writing modes

    Cascade layers

    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:

    • Explicit control over cascade without !important
    • Better than specificity hacks
    • Perfect for design systems and component libraries

    aspect-ratio

    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;
    }

    Tailwind CSS

    Why utility-first CSS comes up in interviews

    Interviewers aren't testing if you've memorized Tailwind classes. They want to understand:

    1. Your reasoning - Can you explain why utility-first might be better (or worse) for a project?
    2. Trade-offs - Do you understand the pros and cons?
    3. When to use it - Can you identify appropriate use cases?

    Utility-first pros:

    • ✅ Faster development (no context switching)
    • ✅ Consistent design system
    • ✅ No CSS bloat (unused styles purged)
    • ✅ No naming fatigue

    Utility-first cons:

    • ❌ HTML can look cluttered
    • ❌ Learning curve for class names
    • ❌ Harder to read for non-Tailwind developers
    • ❌ Tight coupling of styles to markup

    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.


    Common layout patterns in Tailwind

    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>

    @apply

    When 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>
    );
    }

    JIT and arbitrary values

    Just-In-Time (JIT) compilation:

    JIT generates styles on-demand as you write them, enabling:

    • Arbitrary values
    • All variants enabled by default
    • Faster build times

    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.


    Responsive prefixes (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: 640px
    md: 768px
    lg: 1024px
    xl: 1280px
    2xl: 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.


    Scenario-based questions

    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.

    Scenario 1: The navbar that won't stick

    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 using vh units - Safari's viewport height includes the address bar, so I'd switch to dvh (dynamic viewport height) or fixed pixel values.


    Scenario 2: The disappearing z-index

    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 are transform, opacity < 1, filter, or will-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.


    Scenario 3: The flexbox that won't shrink

    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: auto by default, which prevents them from shrinking below their content size. I'd set min-width: 0 on the flex item to allow it to shrink. Then I'd handle the text overflow with either text-overflow: ellipsis for truncation or word-break for wrapping.


    Scenario 4: The grid that breaks on mobile

    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 */
    }
    }

    Scenario 5: The performance problem

    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:

    • ✅ Only animate transform and opacity
    • ✅ Use will-change sparingly (memory cost)
    • ✅ Avoid animating width, height, top, left
    • ✅ Use CSS containment for isolated animations

    Scenario 6: The centering challenge

    Question: "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 */
    }

    Scenario 7: The dark mode dilemma

    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 toggle
    const theme = localStorage.getItem('theme') || 'auto';
    if (theme === 'auto') {
    document.documentElement.removeAttribute('data-theme');
    } else {
    document.documentElement.setAttribute('data-theme', theme);
    }

    Interview pro tips

    DevTools techniques

    When you're asked to debug CSS during a live coding interview, your DevTools skills reveal your experience level.

    Essential DevTools skills:

    1. Inspect Element - Right-click → Inspect (Cmd/Ctrl + Shift + C)
    2. Computed Styles - Check which styles actually apply
    3. Box Model Visualization - Hover to see margins/padding
    4. Grid/Flex overlays - Click grid/flex badge to visualize
    5. Performance tab - Record interactions, identify layout thrashing

    Other techniques:

    • Force element state (right-click → Force state → :hover, :focus)
    • Edit styles live (up/down arrows to increment values)
    • Copy computed styles
    • Screenshot elements (Cmd/Ctrl + Shift + P → "Capture node screenshot")
    • Check accessibility (Elements tab → Accessibility pane)

    How to think aloud

    Interviewers want to understand your thought process. Silent coding makes them nervous.

    The framework:

    1. Restate the problem - "So we need to center this modal and ensure it's scrollable..."
    2. Identify constraints - "The modal needs to work on all screen sizes..."
    3. Consider approaches - "I could use Flexbox or Grid for centering..."
    4. Explain trade-offs - "Flexbox gives more control over overflow..."
    5. Implement and verify - "Let me try this approach... I'll test by resizing..."
    6. Reflect on the solution - "This works, but I'd also add focus trapping..."

    Common interview mistakes to avoid

    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:

    • ✅ Very long text (does it overflow?)
    • ✅ Very short text (does layout break?)
    • ✅ No content (does it collapse?)
    • ✅ Mobile viewport (does it scroll?)
    • ✅ Keyboard navigation (can you tab through?)

    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:

    • "What browsers do we need to support?"
    • "Should this work on mobile?"
    • "Are there any accessibility requirements?"
    • "Is there a design system I should follow?"

    Conclusion

    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:

    • Explain their reasoning clearly and confidently
    • Debug systematically using DevTools and logical thinking
    • Make trade-offs between different approaches based on requirements
    • Write maintainable code that others can understand and extend
    • Consider accessibility and performance from the start

    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:

    1. Practice real scenarios instead of memorizing Q&A. Build actual components, debug real issues, and explain your decisions out loud.

    2. Master DevTools. Spend time exploring the Layout panel, Performance tab, and Accessibility inspector. These tools are your best friends in interviews.

    3. Build a mental framework for approaching CSS problems:

      • What's the layout pattern? (Flexbox, Grid, or positioning?)
      • What are the responsive requirements?
      • What are the accessibility considerations?
      • What are the performance implications?
    4. Stay curious. CSS is evolving rapidly. Follow blogs, experiment with new features, and understand browser compatibility.

    5. 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.

  • TypeScript for React Developers: 12 Common Mistakes and Best PracticesMaster TypeScript React best practices by avoiding these 12 common mistakes. Learn proper component typing, hooks patterns, and API integration techniques.
    Author
    GreatFrontEnd Team
    26 min read
    Dec 8, 2025
    TypeScript for React Developers: 12 Common Mistakes and Best Practices

    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.

    Why TypeScript matters for frontend developers?

    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.

    Before and After: Type safety in action

    // ❌ Without TypeScript - Runtime error waiting to happen
    function UserProfile({ user }) {
    return <div>{user.profile.name}</div>;
    }
    // ✅ With TypeScript - Error caught at compile time
    type 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.


    Component typing mistakes

    Mistake 1: Using React.FC incorrectly

    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:

    • Automatically includes children in props (even when you don't want it)
    • Provides implicit return type
    • Adds displayName, propTypes, and other legacy properties

    Why explicit typing is clearer:

    // ❌ Using React.FC - children included even when not needed
    const 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 intentional
    type 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:

    • When components don't accept children
    • When you need precise control over prop types
    • In modern codebases (React 18+)

    Mistake 2: Not typing component props properly

    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 autocomplete
    function 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 variants
    type CardProps = {
    title: string;
    description?: string; // Optional prop
    variant?: 'primary' | 'secondary' | 'danger'; // Union type for variants
    onClose?: () => 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.


    Mistake 3: Incorrect event handler typing

    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 safety
    function SearchInput({ onChange }: { onChange: Function }) {
    return <input onChange={onChange} />;
    }
    // ❌ Using any - defeats the purpose
    function SearchInput({ onChange }: { onChange: any }) {
    return <input onChange={onChange} />;
    }

    Correct event type patterns:

    // ✅ Proper event typing
    type SearchInputProps = {
    onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
    };
    function SearchInput({ onChange }: SearchInputProps) {
    return <input type="text" onChange={onChange} />;
    }
    // Usage with full type safety
    function 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 typeEvent typeCommon 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>
    );
    }

    Mistake 4: forwardRef typing confusion

    forwardRef 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 wrong
    const 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 typing
    type InputProps = {
    placeholder?: string;
    error?: boolean;
    };
    const Input = forwardRef<HTMLInputElement, InputProps>(
    ({ placeholder, error }, ref) => {
    return (
    <input
    ref={ref}
    placeholder={placeholder}
    className={error ? 'input--error' : 'input'}
    />
    );
    },
    );
    Input.displayName = 'Input';

    Common error messages and fixes:

    Error messageFix
    Type 'ForwardedRef<unknown>' is not assignableAdd generic types: forwardRef<ElementType, PropsType>
    Property 'displayName' does not existAdd ComponentName.displayName = 'Name' after definition
    Type instantiation is excessively deepSimplify prop spreading or use ComponentPropsWithoutRef

    Hooks typing mistakes

    Mistake 5: Not typing useState with complex states

    TypeScript 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 later
    const [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 union
    type User = {
    id: number;
    name: string;
    email: string;
    avatar?: string;
    };
    const [user, setUser] = useState<User | null>(null);
    // ✅ TypeScript knows user can be null
    if (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 };
    }

    Mistake 6: Incorrect useRef typing

    Refs 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 HTMLInputElement
    const inputRef = useRef<HTMLInputElement>();
    // Later...
    inputRef.current.focus(); // Error: Object is possibly 'undefined'

    Matching element types:

    // ✅ Correct - ref can be null initially
    const inputRef = useRef<HTMLInputElement>(null);
    // ✅ Safe access with optional chaining
    const focusInput = () => {
    inputRef.current?.focus();
    };
    return <input ref={inputRef} />;

    Mutable value refs vs DOM refs:

    // ✅ DOM ref - starts as null
    const buttonRef = useRef<HTMLButtonElement>(null);
    // ✅ Mutable value ref - doesn't need null
    const renderCount = useRef<number>(0);
    useEffect(() => {
    renderCount.current += 1;
    });
    // ✅ Storing previous value
    const prevValue = useRef<string>();
    useEffect(() => {
    prevValue.current = value;
    }, [value]);

    Mistake 7: useReducer without discriminated unions

    String-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 runtime
    function reducer(state, action) {
    switch (action.type) {
    case 'LOAD_START':
    return { ...state, loading: true };
    case 'LOAD_SUCESS': // Typo! This case never matches
    return { ...state, loading: false, data: action.payload };
    }
    }

    Discriminated unions enforce strictness:

    // ✅ Type-safe reducer with discriminated unions
    type 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>
    );
    }

    Mistake 8: useContext without proper type guards

    Context 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 undefined
    const 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 defined
    const 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 defined
    function Profile() {
    const user = useUser(); // TypeScript knows user is User, not User | undefined
    return <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;
    }

    Mistake 9: useMemo and useCallback type inference issues

    TypeScript 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 types
    const processedData = useMemo(() => {
    return items.map((item) => ({
    ...item,
    computed: expensiveCalculation(item),
    }));
    }, [items]);
    // TypeScript might infer too broad a type

    Generic parameters:

    // ✅ Explicit typing for clarity
    type 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 types
    const handleSubmit = useCallback(
    (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    onSubmit(formData);
    },
    [formData, onSubmit], // TypeScript checks these dependencies
    );

    Props and Component pattern mistakes

    Mistake 10: Not using utility types for props

    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 properties
    type UserPreview = Pick<User, 'id' | 'name'>;
    // ✅ Omit - exclude properties
    type PublicUser = Omit<User, 'password'>;
    // ✅ Partial - make all properties optional
    type UserUpdate = Partial<User>;
    // ✅ Required - make all properties required
    type CompleteUser = Required<User>;
    // ✅ Readonly - make all properties readonly
    type ImmutableUser = Readonly<User>;
    // ✅ Record - create object type with specific keys
    type UserRoles = Record<'admin' | 'user' | 'guest', boolean>;

    Design system usage:

    // ✅ Base button props
    type BaseButtonProps = {
    variant: 'primary' | 'secondary' | 'danger';
    size: 'sm' | 'md' | 'lg';
    disabled?: boolean;
    loading?: boolean;
    };
    // ✅ Icon button omits size, adds icon
    type IconButtonProps = Omit<BaseButtonProps, 'size'> & {
    icon: React.ReactNode;
    };
    // ✅ Link button picks variant, adds href
    type LinkButtonProps = Pick<BaseButtonProps, 'variant'> & {
    href: string;
    external?: boolean;
    };

    Mistake 11: Incorrect children prop typing

    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 renderable
    type ContainerProps = {
    children: React.ReactNode; // string, number, JSX, array, null, etc.
    };
    function Container({ children }: ContainerProps) {
    return <div className="container">{children}</div>;
    }
    // ✅ ReactElement - only accepts JSX elements
    type WrapperProps = {
    children: React.ReactElement; // Must be a single JSX element
    };
    function Wrapper({ children }: WrapperProps) {
    return <div className="wrapper">{children}</div>;
    }
    // ✅ JSX.Element - similar to ReactElement
    type LayoutProps = {
    children: JSX.Element;
    };
    // ✅ string - only accepts strings
    type LabelProps = {
    children: string;
    };
    function Label({ children }: LabelProps) {
    return <label>{children.toUpperCase()}</label>;
    }

    Render prop patterns:

    // ✅ Render prop with function type
    type 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} />} />;

    Mistake 12: Not typing prop spreading

    Extending native HTML attributes improves autocomplete and type safety when spreading props.

    Extending HTML attributes:

    // ❌ Props don't extend native attributes
    type 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 attributes
    type 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 ref
    type 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 ref
    type ButtonProps = React.ComponentPropsWithRef<'button'> & {
    variant: 'primary' | 'secondary';
    };
    const Button = forwardRef<HTMLButtonElement, ButtonProps>(
    ({ variant, ...props }, ref) => {
    return <button ref={ref} {...props} className={`btn btn--${variant}`} />;
    },
    );

    Mistake 13: Poor generic component typing

    Generic components are powerful but easy to mistype. Proper constraints and polymorphic patterns make them type-safe.

    Common pitfalls:

    // ❌ No constraints - T could be anything
    function 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 id
    type 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 component
    type 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>
    );
    }

    Mistake 14: Type narrowing and type guards

    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 type
    type 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 function
    function 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 narrowing
    type 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 available
    case 'error':
    return <Error message={state.error} />; // ✅ error is available
    }
    }

    The "in" operator and typeof checks:

    // ✅ Using "in" operator
    type 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 typeof
    function 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
    }
    }

    API and external integration mistakes

    Mistake 15: Not typing API responses

    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 happen
    async 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 responses
    type 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 Zod
    import { 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
    }

    Mistake 16: Missing return type annotations for async functions

    Explicit Promise<T> return types improve clarity and help catch errors early.

    Why explicit Promise<T> helps:

    // ❌ Implicit return type - unclear what's returned
    async function loadData() {
    const response = await fetch('/api/data');
    return response.json();
    }
    // ✅ Explicit return type - clear contract
    async function loadData(): Promise<{ items: string[] }> {
    const response = await fetch('/api/data');
    return response.json();
    }

    Impact on debugging:

    // ✅ TypeScript catches mismatches immediately
    async 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
    }

    Mistake 17: Working with poorly-typed third-party libraries

    Not all libraries have good TypeScript support. Learn to augment types when needed.

    Understanding @types/* packages:

    # Install type definitions for libraries without built-in types
    npm install --save-dev @types/lodash
    npm install --save-dev @types/react-router-dom

    Module augmentation:

    // ✅ Augment existing module types
    import '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 library
    declare module 'some-untyped-library' {
    export function doSomething(value: string): number;
    export interface Config {
    apiKey: string;
    timeout?: number;
    }
    }

    The declare module pattern:

    // types/custom.d.ts
    declare module '*.svg' {
    const content: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
    export default content;
    }
    declare module '*.png' {
    const value: string;
    export default value;
    }

    Best practices summary

    Components checklist

    • ✅ Use explicit prop typing instead of React.FC
    • ✅ Never use any for props
    • ✅ Type event handlers with specific event types
    • ✅ Extend native HTML attributes with ComponentPropsWithoutRef

    Hooks checklist

    • ✅ Explicitly type useState for complex states
    • ✅ Initialize useRef with null for DOM refs
    • ✅ Use discriminated unions in useReducer
    • ✅ Add type guards to useContext hooks
    • ✅ Type useMemo and useCallback when inference fails

    Patterns checklist

    • ✅ Use utility types (Pick, Omit, Partial) to reduce duplication
    • ✅ Choose correct children type (ReactNode, ReactElement, string)
    • ✅ Type generic components with proper constraints
    • ✅ Create custom type guards for union types
    • ✅ Use discriminated unions for state machines

    API & external checklist

    • ✅ Type all API responses
    • ✅ Add explicit return types to async functions
    • ✅ Consider runtime validation with Zod or Yup
    • ✅ Augment third-party library types when needed
    • ✅ Use @types/* packages for untyped libraries

    TypeScript configuration for React projects

    A 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 errors
    • strictFunctionTypes - ensures function parameter safety
    • strictBindCallApply - types bind/call/apply correctly
    • noImplicitAny - requires explicit types
    • noImplicitThis - prevents this confusion

    Key flags explained:

    • jsx: "react-jsx" - Uses the new JSX transform (React 17+), no need to import React
    • jsx: "react" - Classic JSX transform, requires import React
    • noUncheckedIndexedAccess - Makes array access return T | undefined, preventing index errors
    • moduleResolution: "bundler" - Modern resolution for Vite/webpack (use "node" for older setups)

    Troubleshooting common TypeScript errors

    Top 5 confusing error messages:

    1. "Type 'X' is not assignable to type 'Y'"

    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 type
    const age: number = parseInt('25');

    2. "Object is possibly 'undefined'"

    What it means: You're accessing a property that might not exist.

    Quick fix:

    // Error
    const name = user.profile.name;
    // Fix: Use optional chaining
    const name = user.profile?.name;
    // Or: Type guard
    if (user.profile) {
    const name = user.profile.name;
    }

    3. "Property 'X' does not exist on type 'Y'"

    What it means: TypeScript doesn't know about that property.

    Quick fix:

    // Error: Property 'customProp' does not exist
    <div customProp="value" />;
    // Fix: Extend the type
    declare module 'react' {
    interface HTMLAttributes<T> {
    customProp?: string;
    }
    }

    4. "Type instantiation is excessively deep and possibly infinite"

    What it means: Your types are too complex or recursive.

    Quick fix:

    // Simplify complex prop spreading
    // Instead of spreading everything, be explicit
    interface ButtonProps
    extends Pick<
    React.ButtonHTMLAttributes<HTMLButtonElement>,
    'onClick' | 'disabled' | 'type'
    > {
    variant: string;
    }

    5. "Cannot find name 'React'"

    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 types
    const result = poorlyTypedLibrary.method();
    // ❌ Avoid @ts-ignore - silently ignores errors forever
    // @ts-ignore
    const result = poorlyTypedLibrary.method();

    Tools and Resources

    VS Code extensions:

    • Pretty TypeScript errors - Formats complex type errors
    • Total TypeScript - Inline TypeScript tips and best practices

    Useful TypeScript tools:

    • ts-reset - Improves built-in TypeScript types for better DX
    • type-fest - Collection of essential TypeScript utility types
    • ts-pattern - Pattern matching library with excellent type inference
    • zod - Runtime validation with TypeScript type inference

    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.


    Common interview questions you are now ready for

    After mastering these mistakes, you're prepared for these React TypeScript interview questions:

    Junior Level:

    1. What's the difference between interface and type in TypeScript?
    2. How do you type a React component's props?
    3. What's the correct way to type a useState hook?
    4. How do you type event handlers in React?
    5. What's the difference between ReactNode and ReactElement?

    Mid Level:

    1. Explain discriminated unions and when to use them in React
    2. How do you properly type a useReducer hook? 8. What's the difference between ComponentPropsWithRef and ComponentPropsWithoutRef?
    3. How do you create a type-safe context with TypeScript? 1
    4. Explain how to type a generic component in React

    Senior Level:

    1. How do you implement polymorphic components with the "as" prop pattern?
    2. What are the tradeoffs between runtime validation (Zod) and compile-time types?
    3. How would you type a complex form library with dynamic fields?
    4. Explain module augmentation and when you'd use it
    5. How do you handle type narrowing in complex conditional rendering scenarios?

    Conclusion

    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:

    • Fewer unexpected errors
    • Easier onboarding for team members
    • More confidence when refactoring
    • Better IDE support
    • Clearer component interfaces

    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.

  • Angular Interview Questions and AnswersMaster Angular interviews with our comprehensive guide covering 45+ questions from basic to advanced. Includes real-world scenarios, common mistakes, and expert tips for both freshers and experienced developers.
    Author
    GreatFrontEnd Team
    4 min read
    Oct 24, 2025
    Angular Interview Questions and Answers

    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.

    How to use this guide

    This guide is structured to help both freshers and experienced developers prepare for Angular interviews effectively:

    1. For freshers (0-2 years experience):

    2. For experienced developers (2+ years):

    Angular interview questions for freshers

    For developers starting their Angular journey, here are the key areas to focus on:

    Core concepts (Must know)

    • Components and component lifecycle
    • Services and dependency injection
    • Data binding (one-way and two-way)
    • Directives and pipes
    • Routing basics

    Modern features

    • Standalone components
    • Signals for state management
    • New control flow syntax

    Dive deep into 20 essential basic Angular questions →

    Angular interview questions for experienced developers

    For senior developers and architects, the focus shifts to advanced concepts:

    Architecture & performance

    • MVVM architecture implementation
    • Change detection strategies
    • Lazy loading and performance optimization
    • Dependency injection hierarchy

    Modern Angular features

    • Signal-based reactivity
    • Standalone components architecture
    • Server-side rendering and hydration

    Master 25 advanced Angular concepts →

    Angular Scenario-Based Interview Questions

    Real-world scenarios you might encounter:

    1. Performance Optimization

    Scenario: "Our Angular application is slow with a large list of items. How would you optimize it?"

    Solution:

    • Implement OnPush change detection
    • Use trackBy with ngFor
    • Virtualize long lists
    • Lazy load modules/components

    2. State Management

    Scenario: "Design a scalable state management solution for a large Angular application."

    Solution:

    • Use Signals for local state
    • Implement Services with BehaviorSubject for shared state
    • Consider NgRx for complex state requirements

    3. Component Communication

    Scenario: "Implement communication between deeply nested components without prop drilling."

    Solution:

    • Create a shared service
    • Use RxJS BehaviorSubject
    • Implement Signal-based state

    Explore more scenario-based Angular questions →

    Common interview mistakes to avoid

    1. Not Understanding Change Detection

      • ❌ Treating all components the same
      • ✅ Know when to use OnPush and why
    2. Misusing Observables

      • ❌ Forgetting to unsubscribe
      • ✅ Using appropriate cleanup in ngOnDestroy
    3. Poor Performance Practices

      • ❌ Heavy computations in templates
      • ✅ Using pure pipes and memoization

    Interview tips for success

    1. Before the interview

    • Build a small Angular project
    • Review your own production code
    • Practice explaining architectural decisions

    2. During the interview

    • Start with high-level explanations
    • Use examples from your experience
    • Ask clarifying questions

    3. Technical preparation

    • Set up a development environment
    • Practice coding common features
    • Review recent Angular updates

    Continue learning

    To further enhance your Angular interview preparation:

    Quick reference: Key Angular concepts

    ConceptBasic levelAdvanced level
    ComponentsCreation, lifecyclePerformance, architecture
    ServicesBasic DI, HTTPCustom providers, hierarchical injection
    State ManagementServices, InputsSignals, RxJS patterns
    PerformanceBasic optimizationChange detection strategies, bundle optimization
    TestingComponent testsE2E, integration testing

    Ready to practice with real Angular interview questions?

    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.

  • Angular Interview Questions for ExperiencedMaster advanced Angular interview questions for experienced developers. 25+ expert-level questions covering RxJS, testing, performance optimization, and real-world scenarios for senior roles.
    Author
    GreatFrontEnd Team
    29 min read
    Oct 15, 2025
    Angular Interview Questions for Experienced

    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.


    Why read this post?

    If you classify yourself as:

    • A senior Angular developer preparing for product-based interviews
    • A frontend engineer targeting enterprise-level architecture roles
    • Or a tech lead revising your fundamentals before mentoring others

    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.

    1. Explain the MVVM architecture pattern in Angular.

    Angular follows Model-View-ViewModel (MVVM) pattern where:

    • Model: Data and business logic (services, HTTP calls)
    • View: Template (HTML)
    • ViewModel: Component class that binds data to view
    // ViewModel (Component)
    export class UserComponent {
    users$ = this.userService.getUsers(); // Model interaction
    constructor(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.

    2. What is an Angular NgModule, and how does it organize code structure?

    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, pipes
    imports: [BrowserModule], // Other modules
    providers: [], // Services
    bootstrap: [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.

    3. Describe the Angular bootstrapping process and what happens internally.

    Angular bootstrapping process:

    1. main.ts calls platformBrowserDynamic().bootstrapModule(AppModule)
    2. Angular creates the platform and application injector
    3. AppModule is instantiated and its providers are registered
    4. Bootstrap component (usually AppComponent) is created
    5. Component is rendered into the DOM at the specified selector
    // main.ts
    platformBrowserDynamic()
    .bootstrapModule(AppModule)
    .catch((err) => console.error(err));

    4. How does the 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:

    • Works in functional contexts (not limited to classes)
    • Simplifies testing and reusability
    • Enables dependency injection in providers without creating extra classes
    // Traditional constructor injection
    export class UserComponent {
    constructor(private userService: UserService) {}
    }
    // Using inject() API
    export class UserComponent {
    private userService = inject(UserService);
    // Can be used in functions
    loadUsers = () => {
    const http = inject(HttpClient);
    return http.get('/api/users');
    };
    }

    5. Explain the importance of OnPush change detection and how to apply it effectively.

    The OnPush change detection strategy improves performance by limiting when Angular checks for changes. Instead of running on every event, it only triggers when:

    • An input property changes (by reference)
    • An event originates from the component or its children
    • You manually mark it for check (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 updates
    this.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.

    6. How would you implement lazy loading, and when is it useful?

    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):

    1. Create a feature module (e.g., users.module.ts)
    2. Configure a route using 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).

    7. What is the difference between 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 children
    viewProviders: [AuthService], // Only for this component's view
    })
    export class ParentComponent {}

    8. Describe the process and benefits of Ahead-of-Time (AOT) Compilation.

    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.

    How it works

    1. Angular CLI compiles templates, metadata, and decorators into optimized JavaScript during the build
    2. The browser loads pre-compiled code, eliminating the need for runtime compilation

    Benefits

    • Faster startup: Templates are pre-compiled, reducing browser work.
    • Early error detection: Template and type errors are caught at build time
    • Smaller bundles: Removes Angular compiler from the final code
    • Improved security: Reduces risks of injection attacks in templates

    Angular CLI uses AOT by default in production builds:

    ng build --configuration production

    9. How do you create and manage custom directives (structural/attribute)?

    Directives in Angular are classes that add behavior or modify the DOM. There are two main types:

    1. Attribute Directives - Change the appearance or behavior of an element
    2. Structural Directives - Change the DOM structure by adding or removing elements

    1. Creating an Attribute Directive

    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>

    2. Creating a Structural Directive

    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>

    Managing Directives

    • Declare them in an NgModule (or use standalone components/directives in Angular 14+)
    • Scope control: Apply only to specific components by including in module declarations
    • Reuse: Combine with @Input and @Output to make directives flexible and configurable

    10. Compare Signals and RxJS Observables - when would you use each?

    Signals and Observables both handle reactive data in Angular but differ in scope and use case.

    Signals

    • Angular 16 feature for local reactive state
    • Updates the UI automatically when the value changes
    • Best for component-level state
    import { signal } from '@angular/core';
    const count = signal(0);
    count.set(count() + 1);

    RxJS Observables

    • Streams of data over time (async operations, events)
    • Powerful operators for filtering, mapping, combining
    • Best for HTTP calls, events, or shared state

    11. How does 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.

    Why use Renderer2

    • Ensures cross-platform compatibility (browser, server-side rendering, Web Workers)
    • Helps prevent XSS attacks by sanitizing changes
    • Makes unit testing easier, since DOM operations can be mocked

    Example: Using Renderer2

    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.

    12. What are tree-shakable providers, and how do they optimize bundle size?

    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.

    Benefits

    • Smaller bundle size: Unused services are automatically excluded
    • Automatic dependency management: No need to manually add services to NgModule providers
    • Scoped injection: Services can also be provided at component or module level if needed

    Example

    import { 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.

    13. Explain the dependency injection hierarchy and token resolution in Angular.

    Angular uses a hierarchical DI system: injectors are organized in a tree, and services are resolved top-down.

    Hierarchy:

    1. Root injector - singleton services (providedIn: 'root')
    2. Module injector - services scoped to feature modules
    3. Component injector - services provided via providers or viewProviders

    Token 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.

    14. What are resolution modifiers (Optional, Self, SkipSelf), and how are they used?

    Resolution modifiers control how Angular resolves dependencies in the injector hierarchy.

    1. @Optional()

    • Marks a dependency as optional
    • If the service is not found, Angular injects null instead of throwing an error
    constructor(@Optional() private logger?: LoggerService) {}

    2. @Self()

    • Tells Angular to look only in the current injector
    • Throws an error if the service is not found locally
    constructor(@Self() private localService: LocalService) {}

    3. @SkipSelf()

    • Skips the current injector and looks only in parent injectors
    • Useful to avoid using a service provided at the current component level
    constructor(@SkipSelf() private parentService: ParentService) {}

    15. How can you share data between distant components in a large application?

    In large Angular applications, distant components (not parent-child) can share data using services with observables or signals.

    1. Using a Shared Service with RxJS

    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));

    2. Using Signals (Angular 16+)

    import { signal, effect } from '@angular/core';
    export const sharedSignal = signal('Hello');
    // Component A
    sharedSignal.set('New Message');
    // Component B
    effect(() => {
    console.log(sharedSignal());
    });

    16. What is the purpose of an 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.

    17. How do you test components that depend on HttpClient or routing modules?

    Testing Components with HttpClient or Routing

    Use Angular's testing modules to mock HTTP requests and routes without real calls.

    HttpClient

    import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
    TestBed.configureTestingModule({
    imports: [HttpClientTestingModule]
    });

    Use HttpTestingController to mock requests and provide test data.

    Routing

    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.

    18. What are pure vs. impure pipes? Which one is better for performance?

    Pipes transform data in templates and can be pure or impure.

    • Pure Pipes (default)
      • Run only when input reference changes
      • Better performance
    @Pipe({ name: 'pureExample', pure: true })
    export class PureExamplePipe implements PipeTransform {
    transform(value: string) { return value.toUpperCase(); }
    }
    • Impure Pipes
      • Run on every change detection cycle, regardless of input changes
      • Use sparingly due to performance impact
    @Pipe({ name: 'impureExample', pure: false })
    export class ImpureExamplePipe implements PipeTransform {
    transform(value: string) { return value.toUpperCase(); }
    }

    19. Explain the difference between structural and attribute directives.

    Directives in Angular modify the DOM or element behavior. They are of two types: structural and attribute.

    Structural Directives

    • Change the DOM structure by adding or removing elements
    • Use * syntax
    • Examples: *ngIf, *ngFor
    <p *ngIf="isVisible">Visible only when isVisible is true</p>

    Attribute Directives

    • Change the appearance or behavior of an element
    • Examples: ngClass, ngStyle, custom directives
    <div [ngClass]="{ active: isActive }">Content</div>

    20. How would you identify and fix a performance bottleneck in a large Angular app?

    Identifying and Fixing Performance Bottlenecks in Angular

    1. Identify Bottlenecks

    • Use Chrome DevTools Performance tab to profile rendering and scripts
    • Enable Angular DevTools to check change detection cycles and component re-renders
    • Look for:
      • Components updating too frequently
      • Large lists without virtualization
      • Unnecessary API calls or computations in templates

    2. Fix Bottlenecks

    • Use OnPush change detection for components
    • Implement trackBy in *ngFor for large lists
    • Lazy load modules and components
    • Move heavy computations to pure pipes or services.
    • Debounce frequent events (e.g., input, scroll)
    • Optimize RxJS streams using operators like shareReplay, take, debounceTime

    Example: OnPush with trackBy

    @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.

    21. What's the significance of 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 nodes
    trackByUserId(index: number, user: User): number {
    return user.id;
    }
    }

    Benefits: Reduces DOM manipulation, improves performance for large lists, preserves component state during updates.

    22. Compare combineLatest, withLatestFrom, and forkJoin in RxJS.

    These operators handle multiple observables differently:

    • combineLatest - emits latest values from all observables whenever any emits
    combineLatest([obs1, obs2]).subscribe(([a,b]) => console.log(a,b));
    • withLatestFrom - emits when the source observable emits, combining it with the latest from other observables
    obs1.pipe(withLatestFrom(obs2)).subscribe(([a,b]) => console.log(a,b));
    • forkJoin - waits for all observables to complete, then emits the last values as an array
    forkJoin([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.

    23. What are Signals effects, and when should you use them?

    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.

    Example

    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"

    When to use:

    • Respond to signal changes with side-effects (e.g., logging, calling services)
    • Keep component templates pure while performing reactive actions

    24. How do you debug a "Expression has changed after it was checked" error?

    This error occurs when Angular detects a change to a value after change detection has run.

    Steps to Debug

    1. Identify the source: Check the component/template causing the error
    2. Check lifecycle hooks: Avoid updating bound values in ngAfterViewInit or ngAfterContentInit directly
    3. Use setTimeout or Promise: Delay updates to the next microtask cycle if necessary
    ngAfterViewInit() {
    setTimeout(() => {
    this.value = newValue; // Updates safely after change detection
    });
    }
    1. ** Consider OnPush strategy**: Ensures Angular only checks the component when inputs change
    2. Avoid changing inputs during rendering: Keep bound values stable during a single change detection cycle

    The key is to update values before or after change detection, not during.

    25. What is the role of 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.

    Optimizing Performance

    • Use 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.


    Angular scenario-based interview questions

    These Angular scenario-based interview questions for experienced professionals test problem-solving in real-world situations.

    1. Performance issue: The app is slow due to frequent change detection. How do you debug and fix this?

    When an Angular app becomes slow because of frequent change detection, you can debug and optimize using the following steps.

    1. Debugging

    • Angular DevTools: Check which components trigger many change detection cycles
    • Chrome Performance Tab: Profile JS execution and rendering
    • Look for patterns: Large lists, heavy computations in templates, frequent event emissions (scroll, input)

    2. Fixing / Optimization

    1. Use OnPush Change Detection
    @Component({
    selector: 'app-list',
    templateUrl: './list.component.html',
    changeDetection: ChangeDetectionStrategy.OnPush
    })
    export class ListComponent {}
    • Component only checks for changes when inputs or signals change.
    1. *Implement trackBy in ngFor
    <div *ngFor="let item of items; trackBy: trackById">{{ item.name }}</div>
    • Prevents unnecessary DOM updates by tracking items by unique identifiers
    1. runOutsideAngular()
    • For high-frequency events like scroll or mousemove:
    this.ngZone.runOutsideAngular(() => {
    window.addEventListener('scroll', this.onScroll);
    });
    1. Move heavy computations to pipes or services
    • Avoid calculations directly in templates
    1. Lazy load modules and components
    • Reduce initial bundle size and unnecessary checks

    2. Migration: You're migrating an Angular 12 app using RxJS to Signals. How would you plan it?

    Migrating from RxJS to Angular Signals requires careful planning to maintain reactivity and performance while reducing boilerplate.

    1. Audit Current App

    • Identify all RxJS streams (BehaviorSubject, Observable, Subject) in components and services
    • Determine which streams are local state vs. async external data (HTTP, WebSocket)

    2. Decide Scope

    • Local component state → convert to signals
    • Service-level shared state → consider using signal stores or keep RxJS if complex async flows are involved

    3. Refactor Local State

    • Replace BehaviorSubject / 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);
    }
    }

    4. Convert Derived State

    • Use computed signals for derived or calculated values:
    import { computed } from '@angular/core';
    total = computed(() => this.items().reduce((sum, i) => sum + i.value, 0));

    5. Replace Subscriptions

    • Remove .subscribe() in templates and components
    • Use effects or computed signals to react to state changes
    import { effect } from '@angular/core';
    effect(() => {
    console.log('Count changed:', this.count());
    });

    6. Keep complex Async as RxJS (if needed)

    • For multi-step streams, operators like mergeMap, combineLatest, keep RxJS to avoid complex refactors
    • Gradually migrate where simpler

    7. Test thoroughly

    • Ensure UI updates correctly after signal migration
    • Verify performance improvements and no memory leaks

    3. State management: How would you architect a shared dashboard state using services or NgRx?

    You can manage shared state using services with RxJS/Signals or NgRx depending on app complexity.

    1. Using a Shared Service

    • Simple and lightweight for small to medium apps.
    • Use BehaviorSubject / signal to hold state and provide getters/setters.
    @Injectable({ providedIn: 'root' })
    export class DashboardService {
    // RxJS
    private 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 => { ... });
    // or
    this.dashboardService.widgets.set(newWidgets);

    2. Using NgRx Store

    • Best for large apps with complex state and multiple actions
    • Define actions, reducers, selectors for dashboard state
    // Action
    export const loadWidgets = createAction('[Dashboard] Load Widgets');
    // Reducer
    export const dashboardReducer = createReducer(
    initialState,
    on(loadWidgets, state => ({ ...state, loading: true }))
    );
    // Selector
    export const selectWidgets = (state: AppState) => state.dashboard.widgets;

    Components: Subscribe via store.select(selectWidgets) and dispatch actions to update state.

    4. Security: Design a system to prevent unauthorized access using route guards and interceptors.

    1. Route Guards

    • Use 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] }

    2. HTTP Interceptors

    • Automatically attach tokens and handle unauthorized responses
    @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 }]

    5. Build Time Optimization: The production build is 3MB; what steps do you take to reduce it?

    If your Angular app's production build is large (e.g., 3MB), you can optimize it using these strategies:

    1. Lazy Loading

    • Split modules and load them only when needed.
    { path: 'dashboard', loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule) }

    2. Remove Unused Code

    • Use tree-shakable providers and remove unused libraries or components

    3. Enable Build Optimizations

    ng build --prod --optimization --build-optimizer

    4. Minify & Compress Assets

    • Enable Terser for JS and gzip/brotli for server delivery

    5. Use OnPush & Signals

    • Optimize change detection to avoid heavy runtime processing

    6. Externalize Large Libraries

    • Load heavy libraries (e.g., lodash, moment) via CDN or import only required functions

    Combining lazy loading, tree-shaking, and asset optimization can significantly reduce bundle size.

    6. SEO & SSR: You're asked to add Server-Side Rendering (SSR) to an existing Angular SPA. How would you do it?

    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:

    1. Add Angular Universal: Use the Angular CLI to add SSR support:
    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.

    1. Update App for SSR Compatibility:
    • Ensure that browser-specific APIs like window, document, and localStorage - are only used in the browser context
    • Wrap such code using isPlatformBrowser from @angular/common
    1. Build and Serve SSR: Build both client and server bundles:
    npm run build:ssr
    npm run serve:ssr

    This serves the pre-rendered HTML from the server, improving SEO and initial load performance.

    1. Optional Enhancements:
    • Implement lazy loading and pre-rendering for critical routes to boost SEO
    • Add meta tags dynamically using Meta and Title services for better indexing

    Result: Users and search engines receive fully rendered HTML on the first request, improving SEO, performance, and crawlability of the Angular app.

    7. Modularization: How do you break down a monolithic Angular app into feature modules?

    To break down a monolithic Angular app into feature modules, I would follow these steps:

    1. Identify Features: Analyze the app and group related functionality (components, services, and routes) into logical features like UserModule, DashboardModule, ProductsModule, etc.

    2. 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.

    1. Move Components, Services, and Routing:
    • Move all components, directives, and pipes related to the feature into the module
    • Move feature-specific services into the module (consider providedIn: FeatureModule if appropriate)
    • Create a feature routing module (feature-name-routing.module.ts) to handle internal routes
    1. Lazy Loading: Update the main app routing to lazy load feature modules:
    { path: 'dashboard', loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule) }
    1. Shared and Core Modules:
    • Extract reusable components, directives, and pipes into a SharedModule
    • Keep singleton services in a CoreModule imported only once in AppModule
    1. Test and Refactor: Ensure each module works independently and routes/services are correctly wired. This improves maintainability, code reusability, and enables lazy loading for better performance

    8. Testing strategy: How do you structure unit tests for complex component hierarchies?

    When structuring unit tests for complex component hierarchies, I follow these practices:

    1. Test Components in Isolation:
    • Use Angular's TestBed to create a testing module for the component
    • Mock child components, directives, and services to isolate the component under test
    1. Use Shallow Testing for Parents:
    • Replace child components with stubs or mocks to focus on parent component behavior without testing children's internal logic
    1. Test Inputs, Outputs, and DOM Interactions:
    • Verify @Input() bindings, @Output() events, template rendering, and user interactions
    • Ensure component reacts correctly to input changes and emits expected events
    1. Service Dependencies:
    • Use spies or mock services for any external dependencies to control behavior and avoid side effects
    1. Organize Tests Logically:
    • Group tests by functionality (rendering, events, service calls) using describe blocks
    • Keep tests small, focused, and maintainable, mirroring the component's structure

    Result: This approach ensures each component's behavior is tested reliably while keeping tests fast, maintainable, and isolated from unrelated parts of the hierarchy.


    Advanced Angular interview questions

    These questions push beyond fundamentals - they're frequent in senior developer and tech lead interviews.

    1. How does the Angular DI lifecycle differ across lazy-loaded and eagerly-loaded modules?

    Angular creates a single root injector for eagerly-loaded code, while each lazy-loaded route gets its own child injector.

    • Eager providers (AppModule) → singletons app-wide
    • Lazy module providers → new instance per lazy boundary (scoped to that route tree)
    • 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 instances
    constructor(private s: FeatureService) {}

    This scoping helps isolate features and avoid cross-feature state leaks while keeping true singletons in root.

    2. Explain hydration in Angular and how partial rehydration improves SSR speed.

    Hydration attaches Angular runtime to server-rendered HTML without re-rendering it. The DOM remains; Angular wires up event listeners and state.

    • Faster TTI: skip initial client re-render
    • Fewer layout thrashes vs CSR
    • Partial hydration (deferred/fragment hydration) hydrates only critical views first and defers the rest until visible or interacted
    // main.ts (standalone app)
    bootstrapApplication(AppComponent, {
    providers: [provideClientHydration()],
    });
    // Template: defer non-critical islands to reduce hydration cost
    @defer (on viewport) {
    <heavy-widget />
    } @placeholder { Loading... }

    3. How does standalone component architecture simplify Angular dependency graphs?

    Standalone components remove NgModule indirection-dependencies are imported where used, and providers are scoped closer to usage.

    • Fewer module graphs to reason about; imports are explicit per component
    • Better tree-shaking; simpler incremental adoption
    • Component-level 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)],
    });

    4. What are the differences between zone-full and zone-less change detection setups?

    • Zone-full (default): Zone.js patches async APIs and triggers global change detection automatically. Simpler dev ergonomics; higher runtime overhead
    • Zone-less: Disable Zone.js and use push-based patterns (Signals, OnPush, manual triggers). Lower overhead; you opt in to updates
    // 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 code
    constructor(private cdr: ChangeDetectorRef) {}
    }

    Use zoneless with Signals and async pipe for most UI; use runOutsideAngular() for high-frequency events.

    5. Explain how to implement custom preloading strategies in routing.

    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.

    6. How do you leverage the build optimizer, budgets, and source-map-analyzer to cut bundle sizes?

    • Build optimizer: enabled by default in production; ensures better tree-shaking and Terser minification
    • Budgets: enforce caps to catch regressions during CI
    • Source map analysis: find large deps and heavy modules
    // angular.json (excerpt)
    {
    "configurations": {
    "production": {
    "budgets": [
    { "type": "initial", "maximumWarning": "500kb", "maximumError": "2mb" },
    { "type": "anyComponentStyle", "maximumWarning": "150kb" }
    ]
    }
    }
    }

    Workflow:

    1. Build with stats: ng build --configuration production --stats-json
    2. Analyze: npx source-map-explorer dist/**/*.js (or webpack bundle analyzer)
    3. Act: lazy-load, split routes, replace heavy libs (e.g., date-fns over moment), use providedIn: 'root'/tree-shakable APIs

    7. What Common RxJS pitfalls (like unhandled subscriptions) can cripple Angular scalability?

    • Leaks from manual subscribe() without unsubscribe → prefer async pipe or takeUntilDestroyed()
    • Nested subscribes → use flattening operators (switchMap, concatMap)
    • Re-executed HTTP on every subscription → share results (shareReplay({ refCount: true, bufferSize: 1 }))
    • Missing error handling → catchError with fallbacks
    import { 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).

    8. Describe how to implement CI/CD for an Angular app (testing, linting, SSR build, deployment pipelines).

    Core stages: install → lint → test → build (SPA/SSR) → artifact → deploy.

    # .github/workflows/ci.yml (simplified)
    name: Angular CI
    on: [push, pull_request]
    jobs:
    build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
    with: { 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@v4
    with: { 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.


    Best practices & optimization tips

    • Use OnPush strategy for pure components
    • Track lists using trackBy in *ngFor to minimize DOM re-renders
    • Use Signals for local reactive patterns; keep global state in NgRx or Akita
    • Always unsubscribe or leverage async pipe or takeUntilDestroyed()
    • Avoid deep component hierarchies; use standalone components where possible
    • Compress assets and enable AOT Compilation for production builds
    • Use Renderer2 for cross-platform DOM-safe operations
    • Keep the codebase modular with feature-based folder organization

    Following these keeps senior developers' Angular apps maintainable and performant in large-scale setups


    Continue Learning

    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.

    Ready to practice with real Angular interview questions?

    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.

  • Top Angular Basic Interview QuestionsPrepare for Angular interviews with 20 essential basic questions covering components, services, directives, data binding, routing, and more with concise answers and code examples.
    Author
    GreatFrontEnd Team
    15 min read
    Oct 14, 2025
    Top Angular Basic Interview Questions

    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.

    Top 20 Angular basic interview questions and answers

    1. What is Angular?

    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:

    • Modularity via NgModules
    • Reusable UI components
    • Two-way data binding
    • Dependency injection for clean architecture
    • Routing for single-page navigation

    2. What are Components in Angular?

    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:

    • Template - defines the HTML structure
    • Class - written in TypeScript, holds data and logic
    • Metadata - defined using @Component, links the class with its template, selector, and styles

    Components 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?

    3. What is a Module in Angular?

    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:

    • Organize the app into logical sections
    • Configure the compiler and dependency injector
    • Control visibility of declarations across modules

    Key properties in @NgModule include:

    • declarations - components, directives, and pipes in this module
    • imports - other modules to use their features
    • providers - services available for dependency injection
    • exports - elements made available to other modules

    Code 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.

    4. What is Data Binding in Angular?

    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:

    1. One-way data binding

    Data flows in one direction - either from component → view or view → component.

    • Interpolation: {{ value }} - shows data
    • Property Binding: [property]="value" - binds DOM properties
    • Event Binding: (event)="handler()" - listens to user actions
    <h3>{{ title }}</h3>
    <!-- Interpolation -->
    <img [src]="imageUrl" />
    <!-- Property binding -->
    <button (click)="onClick()">Click</button>
    <!-- Event binding -->

    2. Two-way data 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'}!`);
    }
    }

    5. What is a Directive in Angular?

    Directives in Angular are classes that add behavior to elements. They let you manipulate the DOM, change appearance, or modify structure.

    Types of directives:

    • Component directives - have a template; act as custom elements.
    • Structural directives - modify DOM layout. Prefixed with *. Examples: *ngIf, *ngFor.
    • Attribute directives - change element appearance or behavior. Examples: NgClass, NgStyle.

    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 Component
    import { 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 = '';
    }

    6. What is a Service in Angular?

    A Service is a class decorated with @Injectable() that contains business logic and data operations shared across multiple components.

    Key features:

    • Singleton pattern - one instance shared app-wide
    • Dependency injection - injected into components via constructor
    • Separation of concerns - keeps business logic out of components

    Common uses:

    • API calls and HTTP requests
    • Data sharing between components
    • Business logic and calculations

    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?

    7. What is Dependency Injection in Angular?

    Dependency Injection (DI) is a design pattern where Angular automatically provides dependencies (like services) to components instead of components creating them manually.

    Key benefits:

    • Loose coupling - components don't create their own dependencies
    • Testability - easy to mock dependencies for testing
    • Reusability - same service instance shared across components

    How it works:

    1. Register services using @Injectable() and providedIn
    2. Inject dependencies via constructor parameters
    3. Angular's injector creates and manages instances

    Code example:

    // Service
    @Injectable({
    providedIn: 'root', // Makes the service a singleton available throughout the app
    })
    export class AuthService {
    isLoggedIn(): boolean {
    return true;
    }
    }
    // Component
    export 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?

    8. What is TypeScript and why does Angular use it?

    TypeScript is a superset of JavaScript that adds static typing and modern features. Angular is built with TypeScript by default.

    Benefits for Angular:

    • Type safety - catch errors at compile time
    • Better IDE support - autocomplete, refactoring
    • Object-oriented features - classes, interfaces, decorators
    • Modern JavaScript features - async/await, modules

    Example:

    interface User {
    id: number;
    name: string;
    email: string;
    }
    export class UserComponent {
    user: User = {
    id: 1,
    name: 'John Doe',
    email: 'john@example.com',
    };
    }

    9. What is Angular Router?

    Angular Router enables navigation between different views/components in a single-page application.

    Key concepts:

    • Routes - map URLs to components
    • Router outlet - placeholder for routed components
    • Navigation - programmatic and declarative routing

    Basic setup:

    // app-routing.module.ts
    const 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>

    10. What are Angular Pipes?

    Pipes transform data in templates without changing the original data. They're used for formatting display values.

    Built-in pipes:

    • DatePipe - format dates
    • CurrencyPipe - format currency
    • UpperCasePipe - convert to uppercase
    • JsonPipe - display objects as JSON

    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"

    11. What are Angular Lifecycle Hooks?

    Lifecycle hooks are methods that Angular calls at specific moments in a component's lifecycle.

    Common hooks:

    • ngOnInit - after component initialization
    • ngOnDestroy - before component destruction
    • ngOnChanges - when input properties change
    • ngAfterViewInit - after view initialization

    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?

    12. What is Event Binding in Angular?

    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!');
    }
    }

    13. What is Property Binding in Angular?

    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';
    }

    14. What is Interpolation in Angular?

    Interpolation displays component data in templates using double curly braces {{ }}.

    Features:

    • Data display - show component properties
    • Expression evaluation - simple calculations
    • Method calls - display method results

    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();
    }
    }

    15. What are Signals in Angular?

    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:

    • Fine-grained reactivity - only updates what changes
    • Better performance - optimized change detection
    • Simpler syntax - easier to read and write
    • Computed values - automatically derived from other signals

    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 signal
    count = signal(0);
    // Computed signal - automatically updates
    doubleCount = computed(() => this.count() * 2);
    increment() {
    // Update signal value
    this.count.update((value) => value + 1);
    }
    }

    Signals improve Angular's reactivity model and are the future direction of the framework.

    16. What are Standalone Components in Angular?

    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:

    • No NgModule needed - components work independently
    • Simpler imports - import dependencies directly in the component
    • Better tree-shaking - smaller bundle sizes
    • Modern Angular approach - recommended for new projects

    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';
    }

    17. What is Angular's new Control Flow Syntax?

    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:

    • Better performance - optimized by the compiler
    • Cleaner syntax - more readable and intuitive
    • Type-safe - better TypeScript support
    • Built-in - no imports needed

    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';
    }

    18. What is Angular Forms?

    Angular Forms handle user input, validation, and form submission. There are two approaches:

    Template-driven forms:

    • Simpler syntax using directives
    • Good for basic forms
    • Uses FormsModule

    Reactive forms:

    • More control and flexibility
    • Better for complex forms
    • Uses ReactiveFormsModule

    Template-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);
    }
    }
    }

    19. What are @Input and @Output decorators?

    @Input and @Output decorators enable communication between parent and child components.

    @Input - passes data from parent to child:

    // Child Component
    export class ChildComponent {
    @Input() userName: string = '';
    }
    <!-- Parent Template -->
    <app-child [userName]="parentName"></app-child>

    @Output - sends events from child to parent:

    // Child Component
    export class ChildComponent {
    @Output() notify = new EventEmitter<string>();
    sendMessage() {
    this.notify.emit('Hello from child!');
    }
    }
    <!-- Parent Template -->
    <app-child (notify)="handleMessage($event)"></app-child>
    // Parent Component
    handleMessage(message: string) {
    console.log(message);
    }

    20. What are Observables in Angular?

    Observables are a key part of Angular's reactive programming approach, used extensively for handling asynchronous operations.

    Key concepts:

    • Stream of data - emit values over time
    • Lazy - don't execute until subscribed
    • Used in Angular - HTTP requests, event handling, routing
    • RxJS library - provides operators for transforming data

    Basic example:

    import { Observable } from 'rxjs';
    export class DataComponent {
    constructor(private http: HttpClient) {}
    getData(): void {
    // HTTP returns an Observable
    this.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).

    Common beginner mistakes in Angular interviews

    Avoid these common pitfalls that can cost you in interviews:

    1. Confusing Components and Modules

    • Mistake: Saying "components and modules are the same thing"
    • Reality: Components control views; modules organize and group components, services, and other features.

    2. Not understanding change detection

    • Mistake: Not knowing when Angular updates the view
    • Reality: Angular uses zone.js to detect changes. Modern Angular uses Signals for more efficient reactivity.

    3. Forgetting to unsubscribe from Observables

    • Mistake: Creating subscriptions without cleaning them up
    • Reality: Always unsubscribe in ngOnDestroy to prevent memory leaks (except HTTP calls).
    // ❌ Bad
    ngOnInit() {
    this.dataService.getData().subscribe(data => this.data = data);
    }
    // ✅ Good
    subscription: Subscription;
    ngOnInit() {
    this.subscription = this.dataService.getData().subscribe(data => this.data = data);
    }
    ngOnDestroy() {
    this.subscription?.unsubscribe();
    }

    4. Using wrong binding syntax

    • Mistake: Mixing up [], (), and {{}} syntax
    • Reality:
    • {{ }} for interpolation
    • [property] for property binding
    • (event) for event binding
    • [(ngModel)] for two-way binding

    5. Not knowing the difference between providedIn: 'root' and providers[]

    • Mistake: Unable to explain where services are registered
    • Reality: providedIn: 'root' creates app-wide singleton; providers[] creates instance per module/component.

    6. Ignoring modern Angular features

    • Mistake: Only knowing old syntax like *ngIf and *ngFor
    • Reality: Angular 17+ uses @if, @for, and standalone components are now standard.

    7. Not understanding the component lifecycle

    • Mistake: Putting initialization logic in the constructor
    • Reality: Use ngOnInit for initialization, constructor only for dependency injection.

    Quick concept recap

    Here's a rapid-fire review of key concepts:

    ConceptKey point
    ComponentsBuilding blocks with template, class, and metadata
    ModulesContainer for organizing related components (less common with standalone)
    Data BindingOne-way ({{ }}, [], ()) and two-way ([()])
    ServicesReusable business logic, injected via DI
    Dependency InjectionAngular provides dependencies automatically
    DirectivesAdd behavior to DOM elements
    PipesTransform data in templates
    Lifecycle HooksngOnInit, ngOnDestroy, ngOnChanges, etc.
    RouterNavigate between views in SPA
    FormsTemplate-driven (simple) vs Reactive (complex)
    ObservablesHandle async operations with RxJS
    SignalsModern reactive primitive (Angular 16+)
    StandaloneComponents without NgModules (Angular 14+)
    Control Flow@if, @for, @switch syntax (Angular 17+)

    Before your interview

    • Practice explaining each concept in 2-3 sentences
    • Write code examples for components, services, and data binding
    • Build a small app to demonstrate understanding
    • Review your code and be ready to discuss design decisions
    • Know the why - understand reasoning behind Angular patterns

    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! 🚀

    Ready to practice with real Angular interview questions?

    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.

  • 100+ React Interview Questions Straight from Ex-interviewers100 prioritized React interview questions and answers, carefully selected and prepared by senior engineers and ex-interviewers from leading tech companies
    Author
    GreatFrontEnd Team
    46 min read
    Jul 16, 2025
    100+ React Interview Questions Straight from Ex-interviewers

    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:

    React fundamentals

    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.

    1. What is React, and what are its main features?

    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.

    React Features

    Find in-depth explanations and track study progress here ->

    2. What is JSX and how does it work?

    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!').

    How JSX works

    Find in-depth explanations and track study progress here ->

    3. Explain the concept of the Virtual DOM in React.

    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 ->

    4. How does virtual DOM in React work? What are its benefits and downsides?

    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.

    • Benefits: It improves performance by reducing costly direct DOM manipulations and makes UI updates declarative and predictable.
    • Downsides: There's some overhead from diffing and extra memory usage, and in very dynamic UIs, it may not always outperform manual optimizations.

    Find in-depth explanations and track study progress here ->

    5. What is the difference Between React Node, React Element, and React Component?

    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 ->

    6. What are React Fragments used for?

    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 ->

    7. What is the purpose of the 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 ->

    8. What is the consequence of using array indices as keys in React?

    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 ->

    9. What are props in React? How are they different from state?

    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 ->

    10. What is the difference between React's class components and functional components?

    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.

    11. When should you use a class component over a function component?

    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.

    12. What is React Fiber?

    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 ->

    13. What is reconciliation?

    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.

    React Reconciliation

    Find in-depth explanations and track study progress here ->

    14. What is the difference between Shadow DOM and Virtual DOM?

    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.

    15. What is the difference between Controlled and Uncontrolled React components?

    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 (
    <input
    type="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 ->

    16. How would you lift the state up in a React application, and why is it necessary?

    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 up
    const 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.

    17. What are Pure Components?

    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.

    • Class components can extend React.PureComponent to become pure
    • Functional components can use React.memo for the same effect
    const PureFunctionalExample = React.memo(function ({ value }) {
    return <div>{value}</div>;
    });

    18. What is the difference between createElement and cloneElement?

    The difference between createElement and cloneElement in React is as follows:

    createElement:

    • Used to create a new React element.
    • It takes the type of the element (e.g., 'div', a React component), props, and children, and returns a new React element.
    • Commonly used internally by JSX or when dynamically creating elements. Example:
    React.createElement('div', { className: 'container' }, 'Hello World');

    cloneElement:

    • Used to clone an existing React element and optionally modify its props.
    • It allows you to clone a React element and pass new props or override the existing ones, keeping the original element's children and state.
    • Useful when you want to manipulate an element without recreating it. Example:
    const element = <button className="btn">Click Me</button>;
    const clonedElement = React.cloneElement(element, { className: 'btn-primary' });

    19. What is the role of 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.

    20. What are stateless components?

    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.

    Key points:

    • Do not use this.state
    • Render UI based on props
    • Focused on displaying information, not managing behavior
    function 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.

    21. What are stateful components?

    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.

    Key points:

    • Use this.state (in class components) or useState (in functional components)
    • Can update state using event handlers or lifecycle methods
    • Handle logic and data management
    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.

    22. What are the recommended ways for type checking of React component props?

    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.

    23. Why does React recommend against mutating state?

    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 ->

    React Hooks

    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.

    24. What are the benefits of using hooks in React?

    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 ->

    25. What are the rules of React hooks?

    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 ->

    26. What is the difference between 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 ->

    27. What does the dependency array of useEffect affect?

    The dependency array of useEffect controls when the effect re-runs:

    • If it's empty, the effect runs only once after the initial render.
    • If it contains variables, the effect re-runs whenever any of those variables change.
    • If omitted, the effect runs after every render.

    Find in-depth explanations and track study progress here ->

    28. What is the 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 ->

    29. What is the purpose of callback function argument format of 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 ->

    30. What is the 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 ->

    31. What is the 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 ->

    32. What is the 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 ->

    33. What is the 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 ->

    34. Can you explain how to create and use custom hooks in React?

    To create and use custom hooks in React:

    1. Create a function that starts with use and uses built-in hooks like useState or useEffect
    2. Return the values or functions you want to share.

    Example:

    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.

    Advanced concepts

    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.

    35. What does re-rendering mean in React?

    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:

    1. Recalculating the JSX returned by the component
    2. Comparing the new JSX with the previous one (using the Virtual DOM)
    3. Updating the real DOM with only the differences (efficient rendering)
    4. Re-rendering ensures that the UI stays in sync with the component's state and props

    Virtual DOM vs Browser DOM

    Find in-depth explanations and track study progress here ->

    36. What is 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 ->

    37. What are error boundaries in React for?

    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 ->

    38. What is React Suspense?

    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 ->

    39. Explain what React hydration is?

    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 Hydration

    Find in-depth explanations and track study progress here ->

    40. What are React Portals used for?

    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 ->

    41. What is React strict mode and what are its benefits?

    React Strict Mode is a development feature in React that activates extra checks and warnings to help identify potential issues in your app.

    • Detects unsafe lifecycles: Warns about deprecated lifecycle methods
    • Identifies side effects: Highlights components with side effects in render methods
    • Warns about unexpected state changes: Catches unexpected state mutations
    • Enforces best practices: Flags potential problems, encouraging modern practices
    <React.StrictMode>
    <App />
    </React.StrictMode>

    Wrapping components in <React.StrictMode> activates these development checks without affecting production builds.

    42. What is code splitting in a React application?

    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 Suspense
    const 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 ->

    43. How would one optimize the performance of React contexts to reduce rerenders?

    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 ->

    44. What is the Flux pattern?

    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.

    Flux pattern

    Find in-depth explanations and track study progress here ->

    45. Explain one-way data flow of React

    In React, one-way data flow means data moves from parent to child components through props.

    • Parent to child: The parent passes data to the child
    • State updates: To change data, the child calls a function passed down by the parent

    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.

    React data flow

    Find in-depth explanations and track study progress here ->

    46. What are some pitfalls of using context in React?

    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 ->

    47. What are some React anti-patterns?

    React anti-patterns are practices that can lead to inefficient or hard-to-maintain code. Common examples include:

    • Directly mutating state instead of using setState
    • Using componentWillMount for data fetching
    • Overusing componentWillReceiveProps
    • Not using keys in lists or using array index for keys
    • Excessive inline functions in render
    • Deeply nested state

    Find in-depth explanations and track study progress here ->

    48. How do you decide between using React state, context, and external state managers?

    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 ->

    49. Explain what happens when setState is called in React?

    When setState is called in React:

    1. State update: It updates the component's state, triggering a re-render of the component
    2. Batching: React may batch multiple setState calls into a single update for performance optimization
    3. Re-render: React re-renders the component (and its child components if needed) with the new state
    4. Asynchronous: State updates may be asynchronous, meaning React doesn't immediately apply the state change; it schedules it for later to optimize performance

    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.

    50. Explain prop drilling

    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.

    51. Describe lazy loading in React

    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.

    52. Discuss synthetic events in React

    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.

    53. Explain the React component lifecycle methods in class components.

    React class components have lifecycle methods for different phases:

    Mounting:

    • constructor: Initializes state or binds methods
    • componentDidMount: Runs after the component mounts, useful for API calls or subscriptions
    componentDidMount() {
    console.log('Component mounted');
    }

    Updating:

    • shouldComponentUpdate: Determines if the component should re-render
    • componentDidUpdate: Runs after updates, useful for side effects

    Unmounting:

    • componentWillUnmount: Cleans up (e.g., removing event listeners).
    componentWillUnmount() {
    console.log('Component will unmount');
    }

    These methods allow you to manage component behavior throughout its lifecycle.

    54. What is Concurrent Mode in React, and how does it improve rendering performance?

    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.

    55. How does React handle concurrent rendering with multiple updates and prioritize them?

    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.

    56. How would you handle long-running tasks or expensive computations in React applications without blocking the UI?

    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);
    }, []);

    57. Explain server-side rendering of React applications and its benefits

    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.

    Server side rendering

    Find in-depth explanations and track study progress here ->

    58. Explain static generation of React applications

    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 ->

    59. What are higher-order components in React?

    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 ->

    60. Explain the presentational vs container component pattern in React

    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 ->

    61. What are render props in React?

    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
    <DataFetcher
    url="/api/data"
    render={(data) => <div>{data ? data.name : 'Loading...'}</div>}
    />;

    Find in-depth explanations and track study progress here ->

    62. Explain the composition pattern in React.

    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 ->

    63. How do you re-render the view when the browser is resized?

    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.

    64. How do you handle asynchronous data loading in React applications?

    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 ->

    65. What are some common pitfalls when doing data fetching in React?

    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 ->

    React Router

    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.

    66. What is a React Router?

    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.

    67. How does React Router work, and how do you implement dynamic routing?

    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 parameter
    return <h1>User ID: {id}</h1>;
    }
    export default function App() {
    return (
    <BrowserRouter>
    <Routes>
    <Route path="/user/:id" element={<UserPage />} /> {/* Dynamic path */}
    </Routes>
    </BrowserRouter>
    );
    }

    Key features:

    • Dynamic Segments: :id captures dynamic data from the URL.
    • useParams Hook: Accesses these dynamic values for rendering.

    68. How do you handle nested routes and route parameters in React Router?

    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 layout
    • useParams: Retrieves route parameters for dynamic routing
    import {
    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>
    );
    }

    69. What is the difference between BrowserRouter and HashRouter?

    • 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).

    70. How React Router is different from the history library?

    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.

    71. What are the <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.

    72. What is the purpose of the push and replace methods of history?

    The push and replace methods of the history library are used to manage the browser's history stack and control navigation.

    push:

    • Adds a new entry to the history stack, which means the user can navigate back to it using the browser's back button.
    • Example: history.push('/new-page')

    replace:

    • Replaces the current entry in the history stack with a new one, meaning the user cannot go back to the previous page using the back button.
    • Example: history.replace('/new-page')

    73. How do you navigate programmatically in React Router?

    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.

    74. How would you implement route guards or private routes in React?

    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+.

    75. How do you manage the active route state in a multi-page React application?

    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>
    );
    }

    76. How do you handle 404 errors or page not found in React Router?

    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.

    77. How to get query parameters in React Router?

    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.

    78. How do you perform an automatic redirect after login in React Router?

    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 logic
    navigate('/dashboard');
    };
    return (
    <div>
    <button onClick={handleLogin}>Login</button>
    </div>
    );
    }

    In this example, the handleLogin function navigates to the /dashboard route after successful login.

    79. How do you pass props to a route component in React Router?

    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.

    React Internationalization

    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.

    80. How do you localize React applications?

    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-i18next
    import { useTranslation } from 'react-i18next';
    const MyComponent = () => {
    const { t } = useTranslation();
    return <p>{t('welcome_message')}</p>;
    };

    Find in-depth explanations and track study progress here ->

    81. What is 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.

    82. What are the main features of react-intl?

    • Formatted text: Helps in formatting messages and strings with placeholders.
    • Number formatting: Allows for formatting numbers, currencies, and percentages according to the locale.
    • Date and time formatting: Helps in formatting dates and times in various formats based on the locale.
    • Plural and gender support: Provides plural and gender-aware string formatting.

    83. What are the two ways of formatting in react-intl?

    • Component-based formatting: Using React components like <FormattedMessage />, <FormattedNumber />, <FormattedDate />, etc., to format content.
    • Hook-based formatting: Using hooks like useIntl for formatting messages, numbers, or dates imperatively within components.

    84. How to use 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 (
    <FormattedMessage
    id="welcome"
    defaultMessage="Hello, {name}!"
    values={{ name: 'John' }}
    />
    );
    }

    Here, {name} is a placeholder, and John will replace it.

    85. How to access the current locale with React Intl?

    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.

    86. How to format date using 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 (
    <FormattedDate
    value={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.

    React Testing

    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.

    87. How do you test React applications?

    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 ->

    88. What is Jest and how is it used for testing React applications?

    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.

    89. What is React Testing Library and how is it used for testing React components?

    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.

    90. How do you test React components using React Testing Library?

    To test React components using React Testing Library, you can:

    1. Render the component using render.
    2. Interact with the component (e.g., clicking buttons, entering text).
    3. Assert on the rendered output using queries like 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.

    91. How do you test asynchronous code in React components?

    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.

    92. How do you mock API calls in React component tests?

    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.

    93. How do you test React hooks in functional components?

    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.

    94. How do you test custom hooks in React?

    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.

    95. What is Shallow Renderer in React testing?

    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 Component
    function Button({ onClick, label }) {
    return <button onClick={onClick}>{label}</button>;
    }
    // Test using Shallow Renderer
    describe('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 function
    const wrapper = shallow(<Button label="Click Me" onClick={onClickMock} />);
    wrapper.simulate('click'); // Simulate click event
    expect(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.

    96. What is Snapshot Testing in React?

    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.

    97. How do you test React components that use context?

    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.

    98. How do you test React components that use Redux?

    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.

    99. What are the key differences between shallow rendering and full DOM rendering in React tests?

    • Shallow Rendering: Renders only the component being tested, without rendering its child components. Useful for isolated unit testing.
    • Full DOM Rendering: Mounts the entire component tree, including children, providing a complete DOM structure. Ideal for integration tests.

    100. What is the TestRenderer package in React?

    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.

    Conclusion

    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.

  • 50 Must-know JavaScript Interview Questions by Ex-interviewers50 essential JavaScript coding interview questions and answers, curated by senior engineers and former interviewers from leading tech companies.
    Author
    GreatFrontEnd Team
    35 min read
    Jul 10, 2025
    50 Must-know JavaScript Interview Questions by Ex-interviewers

    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:

    1. What is Debouncing in JavaScript?

    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.

    Why is it important?

    It prevents performance bottlenecks by reducing the number of unnecessary function calls, making your app smoother and more efficient.

    How does it work?

    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 here
    console.log('Searching for:', searchInput.value);
    }, 300);
    searchInput.addEventListener('input', debouncedSearch);

    Key features of debouncing

    • Delay-based execution: Runs the function after user activity has stopped
    • Improves performance: Prevents excessive computations or network calls during rapid events
    • Flexible configurations: Supports leading (immediate) and trailing (delayed) execution, and even a maximum wait time

    How is it different from throttling?

    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 ->

    2. Understanding 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);
    });

    Key Features of Promise.all

    • Concurrency: Runs multiple asynchronous tasks in parallel, improving performance.
    • All-or-nothing resolution: The promise resolves only when all tasks succeed, or it rejects if any one fails.
    • Simplifies workflows: Ideal for managing interdependent or independent tasks efficiently.

    Practice implementing a Promise.all function on GreatFrontEnd ->

    3. What is Deep Equal?

    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 usage
    const 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 ->

    4. Understanding Event Emitters

    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 event
    eventEmitter.on('customEvent', (data) => {
    console.log('Event emitted with data:', data);
    });
    // Emit the event
    eventEmitter.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 ->

    5. What is 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

    Why Use reduce?

    • Flexibility: Handles various operations, from aggregations to transformations.
    • Functional programming: Encourages declarative and clean code.
    • Powerful: Can replace loops or multiple utility methods in a single chain.

    Practice implementing Array.protoype.reduce on GreatFrontEnd ->

    6. Simplifying arrays – Flattening

    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 flattener
    function 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 ->

    7. Merging data structures

    Merging data is crucial when handling complex structures. JavaScript provides efficient ways to combine objects or arrays.

    Merging objects

    Using the spread operator

    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 }

    Using 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 }

    Merging arrays

    Using the spread operator

    const array1 = [1, 2, 3];
    const array2 = [4, 5, 6];
    const mergedArray = [...array1, ...array2];
    console.log(mergedArray); // Output: [1, 2, 3, 4, 5, 6]

    Using 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]

    Deep merging

    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 ->

    8. Selecting DOM Elements – getElementsByClassName

    getElementsByClassName fetches elements matching a specific class and returns them as a live HTMLCollection.

    // Fetch and loop through elements
    const elements = document.getElementsByClassName('example');
    for (let i = 0; i < elements.length; i++) {
    console.log(elements[i].textContent);
    }

    Multiple classes

    You can combine class names for more specific selections:

    const elements = document.getElementsByClassName('class1 class2');

    Live collections

    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 ->

    9. Avoiding redundant computations with memoization

    Memoization saves computed results to avoid redundant calculations.

    function expensiveOperation(n) {
    console.log('Calculating for', n);
    return n * 2;
    }
    // Memoize function
    function 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, 10
    console.log(memoizedExpensiveOperation(5)); // From cache for 5, 10

    Libraries like Lodash also provide a memoize utility.

    Practice implementing a memoize function on GreatFrontEnd ->

    10. Safer nested property access: get

    Accessing 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 ->

    11. Hoisting in JavaScript

    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.

    Hoisting with var

    Variables declared with var are hoisted and initialized as undefined. Accessing them before initialization results in undefined.

    console.log(foo); // undefined
    var foo = 1;
    console.log(foo); // 1

    Hoisting with let, const, and class

    Variables 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); // ReferenceError
    let y = 'local';

    Function hoisting

    Function declarations

    Both the declaration and definition of functions are hoisted, allowing them to be called before their declaration.

    foo(); // 'FOOOOO'
    function foo() {
    console.log('FOOOOO');
    }

    Function expressions

    For function expressions, only the variable is hoisted, not the function itself.

    console.log(bar); // undefined
    bar(); // TypeError: bar is not a function
    var bar = function () {
    console.log('BARRRR');
    };

    Import statements

    Imports are hoisted, making them available throughout the module. However, their initialization happens before the module code executes.

    foo.doSomething(); // Works fine
    import foo from './modules/foo';

    Best practices

    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 ->

    12. What are the differences between JavaScript variables created using 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.

    Scope

    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); // 1
    console.log(bar); // ReferenceError
    console.log(baz); // ReferenceError

    Initialization

    var and let can be declared without initialization, but const requires an initial value.

    var a; // Valid
    let b; // Valid
    const c; // SyntaxError: Missing initializer

    Redeclaration

    Variables declared with var can be redeclared, but let and const cannot.

    var x = 10;
    var x = 20; // Allowed
    let y = 10;
    let y = 20; // SyntaxError: Identifier 'y' has already been declared

    Reassignment

    var and let allow reassignment, while const does not.

    let a = 1;
    a = 2; // Allowed
    const b = 1;
    b = 2; // TypeError: Assignment to constant variable

    Access before declaration

    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); // undefined
    var foo = 'foo';
    console.log(bar); // ReferenceError
    let bar = 'bar';

    Best practices

    • Use const for variables that don't change to ensure immutability.
    • Use let when reassignment is needed.
    • Avoid var due to its hoisting and scoping issues.
    • Use tools like ESLint to enforce modern best practices

    Read more about the differences between let, var, and const on GreatFrontEnd ->

    13. Explain the difference between == and === in JavaScript?

    The == operator checks for equality after performing type conversion, while === checks for strict equality without type conversion.

    Loose equality (==)

    == allows type coercion, which means JavaScript converts values to the same type before comparison. This can lead to unexpected results.

    42 == '42'; // true
    0 == false; // true
    null == undefined; // true

    Strict equality (===)

    === checks both value and type, avoiding the pitfalls of type coercion.

    42 === '42'; // false
    0 === false; // false
    null === undefined; // false

    Use cases

    • Prefer === for most comparisons as it avoids implicit type conversion and makes code more predictable.
    • Use == only when comparing null or undefined for simplicity.
    let x = null;
    console.log(x == null); // true
    console.log(x == undefined); // true

    Bonus: 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)); // false
    console.log(Object.is(NaN, NaN)); // true

    Conclusion

    • Use === for strict comparisons to avoid bugs caused by type coercion.
    • Rely on Object.is() for nuanced comparisons like distinguishing -0 and +0.

    Explore the differences between == and === on GreatFrontEnd ->

    14. Understanding the Event Loop in JavaScript

    The event loop is the backbone of JavaScript's asynchronous behavior, enabling single-threaded execution without blocking.

    Key components

    1. Call stack: Tracks function executions in a Last-In-First-Out (LIFO) order
    2. Web APIs/Node.js APIs: Handle asynchronous tasks like setTimeout and HTTP requests on separate threads
    3. Task queue (Macrotask queue): Queues tasks like setTimeout and UI events
    4. Microtask queue: Prioritizes tasks like Promise callbacks, executed before macrotasks

    How it works

    1. Synchronous code execution: Functions are pushed and popped from the call stack.
    2. Asynchronous tasks: Offloaded to APIs for processing.
    3. Task completion: Completed tasks are queued.
    4. Event loop execution: Executes microtasks until the queue is empty. Processes one macrotask and checks the microtask queue again.
    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');

    Output:

    Start
    End
    Promise 1
    Timeout 1
    Timeout 2

    Explanation:

    • Synchronous logs (Start, End) run first.
    • Microtasks (Promise 1) follow.
    • Macrotasks (Timeout 1, Timeout 2) run last.

    Explore the event loop in JavaScript on GreatFrontEnd ->

    15. What is Event Delegation in JavaScript?

    Event delegation is an efficient way to manage events for multiple elements by attaching a single event listener to their common parent.

    How it works

    1. Attach a listener: Add an event listener to a parent element instead of each child.
    2. Event bubbling: Events triggered on children bubble up to the parent.
    3. Identify target: Use event.target to determine the clicked element.
    4. Perform action: Execute logic based on the event target.
    // 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}`);
    }
    });

    Benefits

    1. Efficiency: Reduces the number of event listeners, improving performance.
    2. Dynamic content: Automatically handles new elements added to the DOM.

    Explore event delegation in JavaScript on GreatFrontEnd ->

    16. How this works in JavaScript

    The value of this depends on how a function is called. Let's explore its different behaviors.

    Scenarios

    1. 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'
    2. 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'
    3. Method call: this refers to the object the method is called on.

      const obj = {
      name: 'Alice',
      greet() {
      console.log(this.name);
      },
      };
      obj.greet(); // 'Alice'
    4. 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();
    5. 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

    ES6 and this

    Arrow 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 ->

    17. What sets Cookies, sessionStorage, and localStorage apart?

    When it comes to client-side storage, cookies, localStorage, and sessionStorage serve distinct roles:

    Cookies

    • Function: Stores small pieces of data sent along with HTTP requests to the server.
    • Limit: Roughly 4KB per domain.
    • Lifetime: Can persist or expire after a set time. Session cookies disappear when the browser closes.
    • Scope: Accessible across pages and subdomains for a single domain.
    • Security: Features like HttpOnly and Secure flags add extra security.
    // Set a cookie with an expiry date
    document.cookie = 'userId=12345; expires=Fri, 31 Dec 2025 23:59:59 GMT; path=/';
    // Read all cookies
    console.log(document.cookie);
    // Delete a cookie
    document.cookie = 'userId=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';

    localStorage

    • Function: Allows persistent data storage on the client side.
    • Limit: About 5MB per origin.
    • Lifetime: Data stays until explicitly removed.
    • Scope: Shared across all tabs and windows for the same origin.
    • Security: Accessible by JavaScript within the same origin.
    // Store data in localStorage
    localStorage.setItem('username', 'john_doe');
    // Retrieve data
    console.log(localStorage.getItem('username'));
    // Remove an item
    localStorage.removeItem('username');
    // Clear all localStorage data
    localStorage.clear();

    sessionStorage

    • Function: Stores data for the duration of a page session.
    • Limit: Similar to localStorage (around 5MB).
    • Lifetime: Cleared when the tab or browser closes.
    • Scope: Data is confined to the current tab or window.
    • Security: Accessible by JavaScript on the same origin.
    // Store data in sessionStorage
    sessionStorage.setItem('sessionId', 'abcdef');
    // Retrieve data
    console.log(sessionStorage.getItem('sessionId'));
    // Remove an item
    sessionStorage.removeItem('sessionId');
    // Clear all sessionStorage data
    sessionStorage.clear();

    Learn more about cookies, sessionStorage, and localStorage on GreatFrontEnd ->

    18. How do <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 ->

    19. What's the difference between null, undefined?

    Undeclared

    Variables not defined using var, let, or const are considered undeclared and can cause global scope issues.

    undefined

    A declared variable that hasn't been assigned a value is undefined.

    null

    Represents the intentional absence of any value. It's an explicit assignment. Example Code:

    let a;
    console.log(a); // undefined
    let b = null;
    console.log(b); // null
    try {
    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 ->

    20. What's the difference between .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:

    • C for call = comma-separated
    • A for apply = array
    function sum(a, b) {
    return a + b;
    }
    console.log(sum.call(null, 1, 2)); // 3
    console.log(sum.apply(null, [1, 2])); // 3

    Learn more about .call and .apply on GreatFrontEnd ->

    21. How does 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.

    Key uses of bind:

    1. Maintaining Context: Ensures that this is correctly set for the function.
    2. Preset Arguments: Allows you to predefine arguments for a function.
    3. Borrowing Methods: Enables you to use methods from one object in another.
    const john = {
    age: 42,
    getAge: function () {
    return this.age;
    },
    };
    console.log(john.getAge()); // 42
    const unboundGetAge = john.getAge;
    console.log(unboundGetAge()); // undefined
    const boundGetAge = john.getAge.bind(john);
    console.log(boundGetAge()); // 42
    const mary = { age: 21 };
    const boundGetAgeMary = john.getAge.bind(mary);
    console.log(boundGetAgeMary()); // 21

    Explore Function.prototype.bind on GreatFrontEnd ->

    22. Why use arrow functions in constructors?

    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(); // John
    john.sayName2(); // John
    john.sayName1.call(dave); // Dave
    john.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 ->

    23. How does prototypal inheritance work?

    Prototypal inheritance is a way for objects to share properties and methods through their prototype chain.

    Key concepts:

    1. Prototypes: Each object has a prototype, from which it inherits properties and methods.
    2. Prototype chain: JavaScript looks for properties/methods up the chain until it finds them or reaches null.
    3. Constructor functions: Functions used with 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 ->

    24. Differences between: function Person(){}, const person = Person(), and const person = new Person()?

    Key differences:

    1. function Person(){}: A function declaration, typically used for constructors if written in PascalCase.
    2. const person = Person(): Calls the function normally and assigns the result to person. No object creation happens unless explicitly returned.
    3. const person = new Person(): Invokes the function as a constructor, creating a new object and setting its prototype to Person.prototype.

    Explore the difference between: function Person(){}, const person = Person(), and const person = new Person() on GreatFrontEnd ->

    25. Function declarations vs. Function expressions

    Function declarations:

    • Syntax: function foo() {}
    • Hoisting: Fully hoisted; can be called before its definition.
    foo(); // "Hello!"
    function foo() {
    console.log('Hello!');
    }

    Function expressions:

    • Syntax: var foo = function() {}
    • Hoisting: Only the variable is hoisted, not the function body.
    foo(); // TypeError: foo is not a function
    var foo = function () {
    console.log('Hello!');
    };

    Explore the differences on the usage of foo between function foo() {} and var foo = function() {} on GreatFrontEnd ->

    26. What are the different ways to create objects in JavaScript?

    Here are various approaches to creating objects in JavaScript:

    1. 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',
      };
    2. Object constructor: Use the built-in Object constructor with the new keyword.

      const person = new Object();
      person.firstName = 'John';
      person.lastName = 'Doe';
    3. 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.
    4. 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.
    5. 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 ->

    27. What is a higher-order function?

    A higher-order function is a function that either:

    1. 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!
    2. 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 ->

    28. How do ES2015 classes differ from ES5 constructor functions?

    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.

    Key differences:

    • Syntax: ES2015 classes are more readable and concise.
    • Static methods: Easier to define using static in ES2015.
    • Inheritance: Simpler with the extends and super keywords in ES2015.

    Explore differences between ES2015 classes and ES5 constructor functions on GreatFrontEnd ->

    29. What is event bubbling?

    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.

    Prevent 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 ->

    30. What is event capturing?

    Event capturing, also called "trickling", is the reverse of bubbling. The event propagates from the root element down to the target element.

    Enable event capturing

    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 ->

    31. How do mouseenter and mouseover differ?

    mouseenter

    • Does not bubble up the DOM tree.
    • Triggered only when the mouse pointer enters the element itself, excluding its children.
    • Fires a single event when entering the target element.

    mouseover

    • Bubbles up the DOM tree.
    • Triggered when the mouse pointer enters the target element or any of its children.
    • Fires multiple events when moving over child elements.

    Explore the differences between mouseenter and mouseover on GreatFrontEnd ->

    32. What's the difference between synchronous and asynchronous functions?

    Synchronous functions

    • Execute tasks in a sequential, blocking manner.
    • Program execution halts until the current task completes.
    • Easier to debug due to their predictable flow.
    const fs = require('fs');
    const data = fs.readFileSync('file.txt', 'utf8');
    console.log(data); // Blocks until the file is fully read
    console.log('Program ends');

    Asynchronous functions

    • Perform tasks without blocking program execution.
    • Other operations can run while waiting for the task to finish.
    • Commonly used for I/O operations, network requests, and timers.
    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 ->

    33. What is AJAX?

    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.

    Key points

    • Asynchronous: Updates parts of a page without reloading.
    • Data formats: Initially XML, now primarily JSON due to its simplicity.
    • APIs: Traditionally used XMLHttpRequest; fetch() is the modern alternative.

    Using 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();

    Using 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 ->

    34. What are the pros and cons of using AJAX?

    Advantages

    • Enhances user experience by enabling seamless updates.
    • Reduces server load by fetching only necessary data.
    • Keeps the user on the same page while updating content.

    Disadvantages

    • Relies on JavaScript, so functionality may break if it's disabled.
    • SEO challenges with dynamically loaded content.
    • Bookmarking specific page states becomes difficult.

    Explore the advantages and disadvantages of using AJAX on GreatFrontEnd ->

    35. What are the differences between XMLHttpRequest and fetch()?

    XMLHttpRequest

    • Syntax: Event-driven; requires listeners for response handling.
    • Progress tracking: Supports progress tracking via onprogress.
    • Error handling: Uses 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()

    • Syntax: Promise-based; simpler and more readable.
    • Error handling: Uses .catch() for better error management.
    • Modern features: Built-in support for AbortController for cancellations.
    fetch('https://example.com/api')
    .then((response) => response.json())
    .then((data) => console.log(data))
    .catch((error) => console.error(error));

    Key differences

    • 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 ->

    36. What are the various data types in JavaScript?

    JavaScript features a mix of primitive and non-primitive (reference) data types.

    Primitive data types

    • Number: Includes integers and floating-point values.
    • String: Text values enclosed in single, double quotes, or backticks.
    • Boolean: Represents true or false.
    • Undefined: A declared variable that hasn't been assigned a value.
    • Null: Indicates an intentional lack of value.
    • Symbol: A unique and immutable identifier often used as object property keys.
    • BigInt: Handles large integers with arbitrary precision.

    Non-primitive data types

    • Object: Collections of key-value pairs.
    • Array: Ordered lists of elements.
    • Function: First-class objects that can be assigned, passed, and returned.
    • Date: Represents date and time values.
    • RegExp: For pattern matching in strings.
    • Map: A collection of key-value pairs, allowing any type of key.
    • Set: Stores unique values, whether primitive or object references.

    Tip: Use the typeof operator to determine the type of a variable.

    Explore the various data types in JavaScript on GreatFrontEnd ->

    37. How do you iterate over object properties and array items?

    JavaScript provides multiple ways to iterate over objects and arrays.

    Iterating over objects

    1. for...in

    Loops over all enumerable properties, including inherited ones.

    for (const property in obj) {
    if (Object.hasOwn(obj, property)) {
    console.log(property);
    }
    }

    2. Object.keys()

    Retrieves an array of an object's own enumerable properties.

    Object.keys(obj).forEach((key) => console.log(key));

    3. Object.entries()

    Returns an array of [key, value] pairs.

    Object.entries(obj).forEach(([key, value]) => console.log(`${key}: ${value}`));

    4. Object.getOwnPropertyNames()

    Includes both enumerable and non-enumerable properties.

    Object.getOwnPropertyNames(obj).forEach((prop) => console.log(prop));

    Iterating over arrays

    1. for Loop

    Classic approach for iterating through arrays:

    for (let i = 0; i < arr.length; i++) {
    console.log(arr[i]);
    }

    2. Array.prototype.forEach()

    Executes a callback for each array item.

    arr.forEach((element, index) => console.log(element, index));

    3. for...of

    Ideal for looping through iterable objects like arrays.

    for (const element of arr) {
    console.log(element);
    }

    4. 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 ->

    38. What are the benefits of spread syntax, and how is it different from rest syntax?

    Spread syntax (...)

    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

    Rest syntax (...)

    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 ->

    39. What are the differences between Maps vs. Plain objects?

    Map

    • Keys can be any type.
    • Maintains the insertion order.
    • Has a size property.
    • Directly iterable.
    const map = new Map();
    map.set('key', 'value');
    console.log(map.size); // 1

    Plain objects

    • Keys are strings or symbols.
    • Iteration requires Object.keys(), Object.values(), or Object.entries().
    • No direct size property.
    const obj = { key: 'value' };
    console.log(Object.keys(obj).length); // 1

    Explore the difference between Map and plain objects on GreatFrontEnd ->

    40. What are the differences between Map/Set and WeakMap/WeakSet

    • Key Types: WeakMap and WeakSet keys must be objects, while Map and Set accept any data type.
    • Memory Management: WeakMap and WeakSet allow garbage collection of keys, making them useful for managing memory.
    • Size: Only Map and Set have a size property.
    • Iteration: WeakMap and WeakSet are not iterable.
    // Map Example
    const map = new Map();
    map.set({}, 'value');
    console.log(map.size); // 1
    // WeakMap Example
    const 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 ->

    41. What are practical use cases for arrow functions?

    Arrow functions simplify function syntax, making them ideal for inline callbacks.

    // Traditional function syntax
    const 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 syntax
    const 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 ->

    42. What are callback functions in asynchronous operations?

    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 ->

    43. What is debouncing and throttling?

    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 ->

    44. How does destructuring assignment work?

    Destructuring simplifies extracting values from arrays or objects into individual variables.

    // Array destructuring
    const [a, b] = [1, 2];
    // Object destructuring
    const { name, age } = { name: 'John', age: 30 };

    Explore the concept of destructuring assignment on GreatFrontEnd ->

    45. What is function hoisting?

    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 declaration
    hoistedFunction(); // Works fine
    function hoistedFunction() {
    console.log('This function is hoisted');
    }
    // Function expression
    nonHoistedFunction(); // Throws an error
    var nonHoistedFunction = function () {
    console.log('This function is not hoisted');
    };

    Explore the concept of hoisting on GreatFrontEnd ->

    46. How does inheritance work in ES2015 classes?

    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 ->

    47. What is lexical scoping?

    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 ->

    48. What are scopes in JavaScript?

    JavaScript has three main types of scope: global, function, and block.

    // Global scope
    var globalVar = 'I am global';
    function myFunction() {
    // Function scope
    var functionVar = 'I am in a function';
    if (true) {
    // Block scope
    let blockVar = 'I am in a block';
    console.log(blockVar); // Accessible here
    }
    // console.log(blockVar); // Error
    }

    Explore the concept of scope in JavaScript on GreatFrontEnd ->

    49. What is the spread operator?

    The spread operator (...) expands elements of an iterable (like arrays) or properties of objects into individual elements.

    // Copying an array
    const arr1 = [1, 2, 3];
    const arr2 = [...arr1];
    // Merging arrays
    const mergedArray = [...arr1, [4, 5]];
    // Copying an object
    const obj1 = { a: 1, b: 2 };
    const obj2 = { ...obj1 };
    // Passing as function arguments
    const sum = (x, y, z) => x + y + z;
    const nums = [1, 2, 3];
    sum(...nums); // 6

    Explore the spread operator on GreatFrontEnd ->

    50. How does 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 ->

    Conclusion

    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:

  • 30 Basic to Advanced React Interview Questions with Solutions30 React interview questions and solutions, covering basic to advanced topics. Ideal for developers preparing for their next job interview in 2025
    Author
    GreatFrontEnd Team
    11 min read
    Jul 1, 2025
    30 Basic to Advanced React Interview Questions with Solutions

    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:

    Basic Level Questions

    1. What is React?

    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.

    React Features

    Read more about it

    2. What is JSX?

    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!').

    How JSX works

    Read more about it

    3. Difference Between React Node, Element, and Component

    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.

    Read more about it

    4. What are fragments in React?

    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>
    </>
    );

    Read more about it

    5. What is the virtual DOM?

    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.

    Read more about it

    6. What is the purpose of the 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} />);
    }

    Read more about it

    7. What are the implications of using array indices as 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.

    Read more about it

    Intermediate Level Questions

    8. What are the rules of React hooks?

    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.

    Read more about it

    9. How do state and props differ in React?

    • State: State is an internal data structure managed within a component. It is mutable, meaning that it can be updated and changed over time, allowing the component to respond to user interactions or other events dynamically.
    • Props: Props, short for properties, are external data passed from parent components to child components. They are immutable within the child component, meaning that once set, they cannot be altered by the child. This ensures a clear flow of data and helps maintain the integrity of the component hierarchy.

    State vs Props

    Read more about it

    10. What are controlled components?

    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} />;
    }

    Read more about it

    11. Explain hooks in React.

    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.

    12. What distinguishes 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>;
    }

    Read more about it

    13. What does the dependency array of useEffect affect?

    The dependency array of useEffect controls when the effect re-runs:

    • If it's empty, the effect runs only once after the initial render.
    • If it contains variables, the effect re-runs whenever any of those variables change.
    • If omitted, the effect runs after every render.

    Read more about it

    Advanced Level Questions

    14. What is Redux?

    Redux is a predictable state management library often used with React to manage application state through actions and reducers, promoting a unidirectional data flow.

    15. Explain prop drilling.

    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.js
    import React from 'react';
    import ChildComponentA from './ChildComponentA';
    function ParentComponent() {
    const data = 'Hello from Parent';
    return <ChildComponentA data={data} />;
    }
    // ChildComponentA.js
    import React from 'react';
    import ChildComponentB from './ChildComponentB';
    function ChildComponentA({ data }) {
    return <ChildComponentB data={data} />;
    }
    // ChildComponentB.js
    import React from 'react';
    import ChildComponentC from './ChildComponentC';
    function ChildComponentB({ data }) {
    return <ChildComponentC data={data} />;
    }
    // ChildComponentC.js
    import 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.

    16. What are higher-order components (HOCs)?

    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} />;
    };
    }

    Read more about it

    17. Describe lazy loading in React.

    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>
    );
    }

    Read more about it

    18. What is Context API?

    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.

    Read more about it

    19. How do you optimize performance in React applications?

    Techniques include:

    • Using React.memo for functional components.
    • Implementing shouldComponentUpdate for class components.
    • Utilizing lazy loading for code splitting.

    Read more about it

    20. Explain error boundaries in React.

    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;
    }
    }

    Read more about it

    21. What are custom hooks?

    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;
    }

    22. How does React handle forms?

    Forms can be controlled or uncontrolled; controlled forms use state to manage input values while uncontrolled forms rely on DOM elements directly.

    Read more about it

    23. What is server-side rendering (SSR)?

    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.

    Server side rendering

    Read more about it

    24. How do you test React applications?

    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.

    Read more about it

    25. Discuss synthetic events in React.

    Synthetic events are cross-browser wrappers around native events that provide consistent behavior across different browsers while maintaining performance optimizations.

    26. Describe 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>
    </>
    );
    }

    Read more about it

    27. Explain what React hydration is.

    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 Hydration

    Read more about it

    28. What are some React anti-patterns?

    React anti-patterns are practices that can lead to inefficient or hard-to-maintain code. Common examples include:

    • Directly mutating state instead of using setState
    • Using componentWillMount for data fetching
    • Overusing componentWillReceiveProps
    • Not using keys in lists
    • Excessive inline functions in render
    • Deeply nested state

    Read more about it

    29. Explain how you would implement routing in a React application using React Router?

    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>
    );
    }

    30. How do you localize React applications?

    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-i18next
    import { useTranslation } from 'react-i18next';
    const MyComponent = () => {
    const { t } = useTranslation();
    return <p>{t('welcome_message')}</p>;
    };

    Read more about it

    Conclusion

    If you're looking for more in-depth React interview preparation materials, also check out these resources:

  • 50 Must-know HTML, CSS and JavaScript Interview Questions by Ex-interviewersDiscover fundamental HTML, CSS, and JavaScript knowledge with these expert-crafted interview questions and answers. Perfect for freshers preparing for junior developer roles.
    Author
    GreatFrontEnd Team
    43 min read
    Jun 28, 2025
    50 Must-know HTML, CSS and JavaScript Interview Questions by Ex-interviewers

    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:

    1. What Is Hoisting in JavaScript?

    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); // undefined
    var foo = 1;
    console.log(foo); // 1

    Visualized as:

    var foo;
    console.log(foo); // undefined
    foo = 1;
    console.log(foo); // 1

    Variables Declared with let, const, and class

    These are hoisted but remain uninitialized, leading to a ReferenceError if accessed before declaration.

    console.log(bar); // ReferenceError
    let bar = 'value';

    Function Declarations vs. Expressions

    Function declarations are fully hoisted (both declaration and definition), while function expressions are only partially hoisted (declaration without initialization).

    console.log(declared()); // Works
    function declared() {
    return 'Declared function';
    }
    console.log(expr); // undefined
    console.log(expr()); // TypeError: expr is not a function
    var expr = function () {
    return 'Function expression';
    };

    Imports

    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

    2. How Do let, var, and const Differ?

    1. Scope:

    • 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); // ReferenceError
    console.log(b); // ReferenceError
    console.log(c); // ReferenceError

    2. Initialization:

    • var and let: Can be declared without initialization.
    • const: Must be initialized during declaration.
    var a;
    let b;
    const c; // SyntaxError: Missing initializer

    3. Redeclaration:

    • var: Allows redeclaration in the same scope.
    • let and const: Redeclaration is not allowed.
    var x = 1;
    var x = 2; // Valid
    let y = 1;
    let y = 2; // SyntaxError

    4. Reassignment:

    • var and let: Reassignment is allowed.
    • const: Reassignment is not allowed.
    const z = 1;
    z = 2; // TypeError

    5. Hoisting:

    • var: Hoisted and initialized to undefined.
    • let and const: Hoisted but not initialized, causing a ReferenceError if accessed before declaration.
    console.log(a); // undefined
    var a = 1;
    console.log(b); // ReferenceError
    let b = 2;

    Explore the differences between let, var, and const on GreatFrontEnd

    3. What Is the Difference Between == and ===?

    Equality Operator (==):

    • Converts operands to a common type before comparison.
    • May produce unexpected results due to type coercion.
    42 == '42'; // true
    0 == false; // true
    null == undefined; // true

    Strict Equality Operator (===):

    • No type conversion; checks both value and type.
    • Ensures accurate comparisons.
    42 === '42'; // false
    0 === false; // false
    null === undefined; // false

    Best Practice:

    Prefer === to avoid unexpected behavior caused by type coercion, except when comparing against null or undefined.

    var value = null;
    console.log(value == null); // true
    console.log(value === null); // true

    Explore the difference between == and === on GreatFrontEnd

    4. What Is the Event Loop in JavaScript?

    The event loop allows JavaScript to handle asynchronous tasks on a single thread, ensuring smooth execution without blocking.

    Components:

    1. Call Stack: Tracks function calls in a LIFO order.
    2. Web APIs: Handle asynchronous tasks like timers and HTTP requests.
    3. Task Queue: Stores tasks like setTimeout and UI events.
    4. Microtask Queue: Handles high-priority tasks like Promise callbacks.

    Execution Order:

    1. Synchronous code executes first (call stack).
    2. Microtasks are processed next.
    3. Macrotasks are executed afterward.
    console.log('Start');
    setTimeout(() => console.log('Timeout'), 0);
    Promise.resolve().then(() => console.log('Promise'));
    console.log('End');

    Output:

    Start
    End
    Promise
    Timeout

    Explore the event loop in JavaScript on GreatFrontEnd

    5. What Is Event Delegation?

    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.

    • Reduces memory usage by limiting the number of listeners.
    • Dynamically handles added or removed child elements.
    document.getElementById('parent').addEventListener('click', (event) => {
    if (event.target.tagName === 'BUTTON') {
    console.log(`Clicked ${event.target.textContent}`);
    }
    });

    Explore event delegation in JavaScript on GreatFrontEnd

    6. How Does this Work in JavaScript?

    The value of this depends on how a function is invoked:

    1. Default Binding: Refers to the global object (window in browsers).
    2. Implicit Binding: Refers to the object before the dot.
    3. Explicit Binding: Defined using call, apply, or bind.
    4. Arrow Functions: Lexically inherit 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

    7. How Do Cookies, localStorage, and sessionStorage Differ?

    Cookies:

    • Sent with every HTTP request.
    • Limited to 4KB per domain.
    • Can be set to expire.
    document.cookie = 'token=abc123; expires=Fri, 31 Dec 2025 23:59:59 GMT; path=/';
    console.log(document.cookie);

    localStorage:

    • Persistent storage (until manually cleared).
    • 5MB limit per origin.
    localStorage.setItem('key', 'value');
    console.log(localStorage.getItem('key'));

    sessionStorage:

    • Data cleared when the tab or browser is closed.
    • Limited to 5MB.
    sessionStorage.setItem('key', 'value');
    console.log(sessionStorage.getItem('key'));

    Explore the difference between cookies, localStorage, and sessionStorage on GreatFrontEnd

    8. What Are <script>, <script async>, and <script defer>?

    <script>:

    • Blocks HTML parsing until the script loads and executes.

    <script async>:

    • Loads scripts asynchronously.
    • Executes as soon as the script is ready, potentially before HTML parsing completes.

    <script defer>:

    • Loads scripts asynchronously.
    • Executes only after the HTML parsing is complete.
    <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

    9. How Do 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.

    Undeclared:

    Variables not declared will throw a ReferenceError.

    let a;
    console.log(a); // undefined
    let b = null;
    console.log(b); // null

    Explore the difference between null, undefined, and undeclared variables on GreatFrontEnd

    10. What Is the Difference Between .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)); // 3
    console.log(sum.apply(null, [1, 2])); // 3

    Explore the difference between .call and .apply on GreatFrontEnd

    11. What Is 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()); // 42
    const unboundGetAge = john.getAge;
    console.log(unboundGetAge()); // undefined
    const boundGetAge = john.getAge.bind(john);
    console.log(boundGetAge()); // 42
    const mary = { age: 21 };
    const boundGetAgeMary = john.getAge.bind(mary);
    console.log(boundGetAgeMary()); // 21

    Common Uses:

    1. Binding this: bind is often used to fix the this value for a method, ensuring it always refers to the intended object.
    2. Partial Application: You can predefine some arguments for a function using bind.
    3. Method Borrowing: bind allows methods from one object to be used on another object.

    Explore Function.prototype.bind on GreatFrontEnd

    12. Why Use Arrow Functions in Constructors?

    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(); // John
    john.sayName2(); // John
    john.sayName1.call(dave); // Dave
    john.sayName2.call(dave); // John

    When to Use:

    • In scenarios like React class components, where methods are passed as props and need to retain their original this context.

    Explore the advantage of using the arrow syntax for a method in a constructor on GreatFrontEnd

    13. How Does Prototypal Inheritance Work?

    Prototypal inheritance allows objects to inherit properties and methods from other objects through the prototype chain.

    Key Concepts:

    1. Prototypes:

    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.

    2. Prototype Chain:

    JavaScript looks for properties and methods on the object and continues up the chain until it finds the property or reaches null.

    3. Constructor Functions:

    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 Fido
    fido.bark(); // Woof!

    Explore how prototypal inheritance works on GreatFrontEnd

    14. What’s the Difference Between function Person(){}, const person = Person(), and const person = new Person()?

    Function Declaration:

    function Person() {} is a standard function declaration. When written in PascalCase, it conventionally represents a constructor function.

    Function Call:

    const person = Person() calls the function and executes its code but does not create a new object.

    Constructor Call:

    const person = new Person() creates a new object, setting its prototype to Person.prototype.

    Explore the difference between function Person(){}, const person = Person(), and const person = new Person() on GreatFrontEnd

    15. How Do Function Declarations and Expressions Differ?

    Function Declarations:

    function foo() {
    console.log('Function declaration');
    }
    • Hoisted with their body.
    • Can be invoked before their definition.

    Function Expressions:

    const foo = function () {
    console.log('Function expression');
    };
    • Only the variable is hoisted, not the function body.
    • Cannot be invoked before their definition.

    Explore the differences between function declarations and expressions on GreatFrontEnd

    16. How Can You Create Objects in JavaScript?

    1. Object Literals:
    const person = { firstName: 'John', lastName: 'Doe' };
    1. Object() Constructor:
    const person = new Object();
    person.firstName = 'John';
    person.lastName = 'Doe';
    1. Object.create():
    const proto = {
    greet() {
    console.log('Hello!');
    },
    };
    const person = Object.create(proto);
    person.greet(); // Hello!
    1. ES2015 Classes:
    class Person {
    constructor(name, age) {
    this.name = name;
    this.age = age;
    }
    }

    Explore ways to create objects in JavaScript on GreatFrontEnd

    17. What Are Higher-Order Functions?

    Higher-order functions either:

    1. Take other functions as arguments.
    2. Return functions.
    function multiplier(factor) {
    return function (number) {
    return number * factor;
    };
    }
    const double = multiplier(2);
    console.log(double(5)); // 10

    Explore higher-order functions on GreatFrontEnd

    18. Differences Between ES2015 Classes and ES5 Constructors

    ES5 Constructor:

    function Person(name) {
    this.name = name;
    }
    Person.prototype.greet = function () {
    console.log(`Hello, I’m ${this.name}`);
    };

    ES2015 Class:

    class Person {
    constructor(name) {
    this.name = name;
    }
    greet() {
    console.log(`Hello, I’m ${this.name}`);
    }
    }

    Key Differences:

    • Syntax: Classes are easier to read and write.
    • Inheritance: Classes use extends and super.

    Explore ES2015 classes and ES5 constructors on GreatFrontEnd

    19. What Is Event Bubbling?

    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

    20. What Is Event Capturing?

    Event capturing is when an event starts at the root and propagates down to the target element.

    Enabling Capturing:

    parent.addEventListener('click', () => console.log('Parent capturing'), true);

    Explore event capturing on GreatFrontEnd

    21. How Do the mouseenter and mouseover Events Differ in JavaScript and Browsers?

    mouseenter

    • Does not propagate through the DOM tree
    • Fires solely when the cursor enters the element itself, excluding its child elements
    • Triggers only once upon entering the parent element, regardless of its internal content

    mouseover

    • Propagates upwards through the DOM hierarchy
    • Activates when the cursor enters the element or any of its descendant elements
    • May lead to multiple event callbacks if there are nested child elements

    Discover the distinctions between mouseenter and mouseover events in JavaScript and browsers on GreatFrontEnd

    22. Can You Differentiate Between Synchronous and Asynchronous Functions?

    Synchronous Functions

    • Execute operations in a sequential, step-by-step manner
    • Block the program's execution until the current task completes
    • Adhere to a strict, line-by-line execution order
    • Are generally easier to comprehend and debug due to their predictable flow
    • Common use cases include reading files synchronously and iterating over large datasets

    Example:

    const fs = require('fs');
    const data = fs.readFileSync('large-file.txt', 'utf8');
    console.log(data); // Blocks until file is read
    console.log('End of the program');

    Asynchronous Functions

    • Allow the program to continue running without waiting for the task to finish
    • Enable other operations to proceed while waiting for responses or the completion of time-consuming tasks
    • Are non-blocking, facilitating concurrent execution and enhancing performance and responsiveness
    • Commonly used for network requests, file I/O, timers, and animations

    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

    23. Provide a Comprehensive Explanation of AJAX

    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:

    • Asynchronous Operations: AJAX allows parts of a web page to update independently without reloading the entire page.
    • Data Formats: Initially utilized XML, but JSON has become more prevalent due to its seamless compatibility with JavaScript.
    • APIs: Traditionally relied on XMLHttpRequest, though fetch() is now the preferred choice for modern web development.

    XMLHttpRequest API

    Example:

    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();
    • Process: Initiates a new XMLHttpRequest, assigns a callback to handle state changes, opens a connection to a specified URL, and sends the request.

    fetch() API

    Example:

    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));
    • Process: Starts a fetch request, processes the response with .then() to parse JSON data, and handles errors using .catch().

    How AJAX Operates with fetch

    1. Initiating a Request

    • fetch() 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',
      },
      });

    2. Promise-Based Response

    • fetch() returns a Promise that resolves to a Response object representing the server's reply.

    3. Managing the Response

    • 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));

    4. Asynchronous Nature

    • fetch() operates asynchronously, allowing the browser to perform other tasks while awaiting the server's response.
    • Promises (.then(), .catch()) are processed in the microtask queue as part of the event loop.

    5. Configuring Request Options

    • The optional second parameter in fetch() allows configuration of various request settings, including HTTP method, headers, body, credentials, and caching behavior.

    6. Handling Errors

    • Errors such as network failures or invalid responses are captured and managed through the Promise chain using .catch() or try/catch with async/await.

    Learn how to explain AJAX in detail on GreatFrontEnd

    24. What Are the Pros and Cons of Utilizing AJAX?

    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.

    Advantages

    • Enhanced User Experience: Updates content seamlessly without refreshing the entire page.
    • Improved Performance: Reduces server load by fetching only the required data.
    • Maintains State: Preserves user interactions and client-side states within the page.

    Disadvantages

    • Dependency on JavaScript: Functionality can break if JavaScript is disabled in the browser.
    • Bookmarking Issues: Dynamic content updates make it difficult to bookmark specific states of a page.
    • SEO Challenges: Search engines may find it hard to index dynamically loaded content effectively.
    • Performance on Low-End Devices: Processing AJAX data can be resource-intensive, potentially slowing down performance on less powerful devices.

    Explore the benefits and drawbacks of using AJAX on GreatFrontEnd

    25. How Do XMLHttpRequest and fetch() Differ?

    Both XMLHttpRequest (XHR) and fetch() facilitate asynchronous HTTP requests in JavaScript, but they vary in syntax, handling mechanisms, and features.

    Syntax and Implementation

    • XMLHttpRequest: Utilizes an event-driven approach, requiring event listeners to manage responses and errors.
    • fetch(): Employs a Promise-based model, offering a more straightforward and intuitive syntax.

    Setting Request Headers

    • XMLHttpRequest: Headers are set using the setRequestHeader method.
    • fetch(): Headers are provided as an object within the options parameter.

    Sending the Request Body

    • XMLHttpRequest: The request body is sent using the send method.
    • fetch(): The body property within the options parameter is used to include the request body.

    Handling Responses

    • XMLHttpRequest: Uses the responseType property to manage different response formats.
    • fetch(): Offers a unified Response object with .then methods for accessing data.

    Managing Errors

    • XMLHttpRequest: Errors are handled via the onerror event.
    • fetch(): Errors are managed using the .catch method.

    Controlling Caching

    • XMLHttpRequest: Managing cache can be cumbersome and often requires workaround strategies.
    • fetch(): Directly supports caching options through its configuration.

    Canceling Requests

    • XMLHttpRequest: Requests can be aborted using the abort() method.
    • fetch(): Utilizes AbortController for canceling requests.

    Tracking Progress

    • XMLHttpRequest: Supports progress tracking with the onprogress event.
    • fetch(): Lacks native support for tracking progress.

    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

    26. What Are the Different Data Types in JavaScript?

    JavaScript encompasses a variety of data types, which are categorized into two main groups: primitive and non-primitive (reference) types.

    Primitive Data Types

    • Number: Represents both integer and floating-point numbers.
    • String: Denotes sequences of characters, enclosed in single quotes, double quotes, or backticks.
    • Boolean: Logical values with true or false.
    • Undefined: A variable that has been declared but not assigned a value.
    • Null: Signifies the intentional absence of any object value.
    • Symbol: A unique and immutable value used primarily as object property keys.
    • BigInt: Allows representation of integers with arbitrary precision, useful for very large numbers.

    Non-Primitive Data Types

    • Object: Stores collections of data and more complex entities.
    • Array: An ordered list of values.
    • Function: Functions are treated as objects and can be defined using declarations or expressions.
    • Date: Represents dates and times.
    • RegExp: Used for defining regular expressions for pattern matching within strings.
    • Map: A collection of keyed data items, allowing keys of any type.
    • Set: A collection of unique values.

    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

    27. What Constructs Do You Use to Iterate Over Object Properties and Array Elements?

    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:

    Iterating Over Objects

    1. for...in Loop

    Iterates over all enumerable properties of an object, including inherited ones.

    for (const property in obj) {
    if (Object.hasOwn(obj, property)) {
    console.log(property);
    }
    }

    2. Object.keys()

    Returns an array containing the object's own enumerable property names.

    Object.keys(obj).forEach((property) => console.log(property));

    3. 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}`));

    4. 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));

    Iterating Over Arrays

    1. for Loop

    A traditional loop for iterating over array elements.

    for (let i = 0; i < arr.length; i++) {
    console.log(arr[i]);
    }

    2. Array.prototype.forEach()

    Executes a provided function once for each array element.

    arr.forEach((element, index) => console.log(element, index));

    3. for...of Loop

    Iterates over iterable objects like arrays.

    for (let element of arr) {
    console.log(element);
    }

    4. 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);
    }

    Learn about the constructs used for iterating over object properties and array elements on GreatFrontEnd

    28. What Are the Advantages of Using Spread Syntax, and How Does It Differ from Rest Syntax?

    Spread Syntax

    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 }

    Rest Syntax

    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

    29. How Does a Map Object Differ from a Plain Object in JavaScript?

    Map Object

    • Key Flexibility: Allows keys of any type, including objects, functions, and primitives.
    • Order Preservation: Maintains the order in which keys are inserted.
    • Size Property: Includes a size property to easily determine the number of key-value pairs.
    • Iteration: Directly iterable with methods like forEach, keys(), values(), and entries().
    • Performance: Typically offers better performance for larger datasets and frequent modifications.

    Plain Object

    • Key Types: Primarily uses strings or symbols as keys. Non-string keys are converted to strings.
    • Order: Does not guarantee the order of key insertion.
    • Size Tracking: Lacks a built-in property to determine the number of keys; requires manual counting.
    • Iteration: Not inherently iterable. Requires methods like Object.keys(), Object.values(), or Object.entries() to iterate.
    • Performance: Generally faster for small datasets and simple operations.
    // Map
    const map = new Map();
    map.set('key1', 'value1');
    map.set({ key: 'key2' }, 'value2');
    console.log(map.size); // 2
    // Plain Object
    const 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

    30. What Are the Differences Between 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

    31. What is a Practical Scenario for Using the Arrow => 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 syntax
    const 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 syntax
    const 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

    32. How Do Callback Functions Operate in Asynchronous Tasks?

    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

    33. Can You Describe Debouncing and Throttling Techniques?

    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

    34. How Does Destructuring Assignment Work for Objects and Arrays?

    Destructuring assignment in JavaScript provides a concise way to extract values from arrays or properties from objects into individual variables.

    // Array destructuring
    const [a, b] = [1, 2];
    // Object destructuring
    const { 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

    35. What is Hoisting in the Context of Functions?

    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 declaration
    hoistedFunction(); // Works fine
    function hoistedFunction() {
    console.log('This function is hoisted');
    }
    // Function expression
    nonHoistedFunction(); // Throws an error
    var nonHoistedFunction = function () {
    console.log('This function is not hoisted');
    };

    Explore the concept of hoisting with regards to functions on GreatFrontEnd

    36. How Does Inheritance Work in ES2015 Classes?

    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

    37. What is Lexical Scoping?

    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

    38. What is Scope in JavaScript?

    Scope in JavaScript defines the accessibility of variables and functions in different parts of the code. There are three primary types of scope:

    1. Global Scope: Variables declared outside any function or block are accessible throughout the entire code.
    2. Function Scope: Variables declared within a function are accessible only within that function.
    3. Block Scope: Introduced in ES6, variables declared with let or const within a block (e.g., within {}) are accessible only within that block.
    // Global scope
    var globalVar = 'I am global';
    function myFunction() {
    // Function scope
    var functionVar = 'I am in a function';
    if (true) {
    // Block scope
    let 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

    39. What is the Spread Operator and How is it Used?

    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 array
    const arr1 = [1, 2, 3];
    const arr2 = [...arr1];
    // Merging arrays
    const arr3 = [4, 5, 6];
    const mergedArray = [...arr1, ...arr3];
    // Copying an object
    const obj1 = { a: 1, b: 2 };
    const obj2 = { ...obj1 };
    // Merging objects
    const obj3 = { c: 3, d: 4 };
    const mergedObject = { ...obj1, ...obj3 };
    // Passing array elements as function arguments
    const 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

    40. How Does 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

    41. What is a Block Formatting Context (BFC) and How Does It Function?

    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:

    • The float property is set to a value other than none.
    • The position property is assigned a value that is neither static nor relative.
    • The display property is set to table-cell, table-caption, inline-block, flex, inline-flex, grid, or inline-grid.
    • The 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

    42. What is 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

    43. How Does a Browser Match Elements to a CSS Selector?

    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

    44. What is the Box Model in CSS and How Can You Control Its Rendering?

    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:

    • The total space a block element occupies.
    • Whether borders and margins overlap or collapse.
    • The overall dimensions of a box.

    Box Model Rules

    • Dimensions Calculation: A block element's size is determined by its width, height, padding, and border.
    • Automatic Height: If no height is specified, a block element's height adjusts to its content plus padding (unless floats are involved).
    • Automatic Width: If no width is set, a non-floated block element expands to fit its parent's width minus padding, unless a max-width is specified.
      • Certain block-level elements like table, figure, and input have inherent width values and may not expand fully.
      • Inline elements like span do not have a default width and will not expand to fit.
    • Content Dimensions: An element's height and width are determined by its content.
    • Box-Sizing: By default (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.

    Additional Considerations

    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

    45. How Do You Utilize the CSS 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

    46. How Do 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

    47. What Should You Consider When Designing for Multilingual Websites?

    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).

    Search Engine Optimization (SEO)

    • Language Attribute: Use the lang attribute on the <html> tag to specify the page's language.
    • Locale in URLs: Include locale identifiers in URLs (e.g., en_US, zh_CN).
    • Alternate Links: Utilize <link rel="alternate" hreflang="other_locale" href="url_for_other_locale"> to inform search engines about alternate language versions of the page.
    • Fallback Pages: Provide a fallback page for unmatched languages using <link rel="alternate" href="url_for_fallback" hreflang="x-default" />.

    Locale vs. Language

    • Locale: Controls regional settings like number formats, dates, and times, which may vary within a language.
    • Language Variations: Recognize that widely spoken languages have different dialects and regional variations (e.g., en-US vs. en-GB, zh-CN vs. zh-TW).

    Locale Prediction and Flexibility

    • Automatic Detection: Servers can detect a visitor's locale using HTTP Accept-Language headers and IP addresses.
    • User Control: Allow users to easily change their preferred language and locale settings to account for inaccuracies in automatic detection.

    Text Length and Layout

    • Variable Lengths: Be aware that translations can alter text length, potentially affecting layout and causing overflow issues.
    • Design Flexibility: Avoid rigid designs that cannot accommodate varying text lengths, especially for headings, labels, and buttons.

    Reading Direction

    • Left-to-Right (LTR) vs. Right-to-Left (RTL): Accommodate different text directions, such as Hebrew and Arabic, by designing flexible layouts that can adapt to both LTR and RTL orientations.

    Avoid Concatenating Translated Strings

    • 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:

      // English
      const message = `I will travel on ${date}`;
      // Chinese
      const message = `我会在${date}出发`;

    Formatting Dates and Currencies

    • Regional Formats: Adapt date and currency formats to match regional conventions (e.g., "May 31, 2012" in the U.S. vs. "31 May 2012" in Europe).

    Text in Images

    • Scalability Issues: Avoid embedding text within images, as it complicates translation and accessibility. Use text elements styled with CSS instead to allow for easier localization.

    Cultural Perceptions of Color

    • Color Sensitivity: Be mindful that colors can carry different meanings and emotions across cultures. Choose color schemes that are culturally appropriate and inclusive.

    Understand Multilingual Design Considerations on GreatFrontEnd

    48. How Do 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:

    Propertyblockinline-blockinline
    SizeFills up the width of its parent container.Depends on content.Depends on content.
    PositioningStart 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 heightYesYesNo. Will ignore if being set.
    Can be aligned with vertical-alignNoYesYes
    Margins and paddingsAll 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 CasesLayout 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

    49. When Would You Prefer 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():

      • Flow Preservation: Elements remain in their original position within the document flow, similar to position: relative.
      • Performance Benefits: Modifying 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.
      • Layout Stability: The surrounding layout remains unaffected since the element's space is preserved.
      .element {
      transform: translateX(50px);
      }
    • Using absolute Positioning:

      • Flow Removal: The element is taken out of the normal document flow, and its position is calculated relative to the nearest positioned ancestor or the viewport.
      • Reflow Trigger: Changing an element's absolute position can cause the browser to recalculate the layout (reflow), which is more CPU-intensive.
      • Overlapping Control: Useful for precise placement of elements without affecting other elements' positions.
      .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

    50. What Does * { 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.

    What It Does

    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.

    Comparison Table

    Propertybox-sizing: content-box (default)box-sizing: border-box
    contentYesYes
    paddingNoYes
    borderNoYes
    marginNoNo

    Advantages

    • Intuitive Sizing: Including padding and border within the width and height makes it easier to calculate the size of elements, aligning more closely with designers' expectations.
    • Simplified Layouts: Prevents unexpected sizing issues, especially when adding padding or border to elements, as it doesn't alter the total size.
    • Consistency Across Frameworks: Many CSS frameworks like Bootstrap, Tailwind, and Bulma set 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

    Conclusion

    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.

  • 50 React.js Interview Questions for Experienced Developers50 React JS interview questions for experienced developers. Explore detailed solutions to enhance your preparation for 2025 job interviews
    Author
    GreatFrontEnd Team
    21 min read
    Jun 22, 2025
    50 React.js Interview Questions for Experienced Developers

    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:

    1. Can you explain how React's Virtual DOM works and its benefits?

    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.

    Benefits:

    • Performance: Minimizes direct DOM manipulations, which are expensive.
    • Declarative UI: Developers describe "what" the UI should look like, and React manages "how" to update it.
    • Cross-browser Compatibility: React abstracts browser quirks when manipulating the DOM.
    function Counter() {
    const [count, setCount] = React.useState(0);
    return (
    <button onClick={() => setCount(count + 1)}>Clicked {count} times</button>
    );
    }

    Read more about it here

    2. How does React's reconciliation algorithm work to update the UI efficiently?

    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:

    1. Diffing: Compares the previous and current VDOM tree to find changes.
    2. Key Comparison: Uses key props to identify stable elements across renders.
    3. Batch Updates: Minimizes updates by batching multiple state changes into a single DOM operation.

    3. What is the difference between React's class components and functional components?

    Class Components

    • Use ES6 classes
    • Manage state and lifecycle using class methods like componentDidMount
    • Verbose and harder to test

    Functional Components

    • Simpler functions.
    • Use hooks (useState, useEffect, etc.) for state and lifecycle
    • Encouraged in modern React due to their simplicity and performance benefits

    4. Explain the Fiber architecture and how it improves React's rendering process.

    The 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).

    Benefits:

    • Non-blocking rendering
    • Improved responsiveness for animations and transitions

    5. What are React fragments, and when would you use them instead of a wrapper element?

    React Fragments allow grouping child elements without adding an extra DOM node.

    When to use:

    • Avoiding unnecessary <div> elements in the DOM, which can cause layout or CSS issues
    function List() {
    return (
    <>
    <li>Item 1</li>
    <li>Item 2</li>
    </>
    );
    }

    Read more about it here

    6. What is the difference between props and state in React?

    • Props are immutable and passed from a parent component to a child component. They allow components to be dynamic by providing external values.
    • State is mutable and managed within the component itself. It can change over time, usually in response to user actions or network responses. Example:
    // Parent component passes props
    const Parent = () => {
    return <Child name="John" />;
    };
    // Child component receives props
    const Child = ({ name }) => {
    return <h1>Hello, {name}</h1>;
    };

    Read more about it here

    7. How would you lift state up in a React application, and why is it necessary?

    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 up
    const 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.

    8. Can you explain how you would manage deeply nested state in React?

    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.
    • Context API: For shared deep state across multiple components.
    • Immutable updates: Use libraries like Immer for easier state updates when working with nested data. Example with useReducer:
    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>
    );
    };

    9. How do controlled and uncontrolled components differ in React?

    • Controlled Components: React is responsible for managing the form element's value via state. Any change to the value is handled by React through the onChange handler.
    • Uncontrolled Components: React does not manage the form value directly. Instead, the DOM itself handles the state, and React uses refs to access the current value. Example:
    // Controlled component
    const ControlledInput = () => {
    const [value, setValue] = useState('');
    return <input value={value} onChange={(e) => setValue(e.target.value)} />;
    };
    // Uncontrolled component
    const UncontrolledInput = () => {
    const inputRef = useRef();
    return <input ref={inputRef} />;
    };

    Read more about it here

    10. What are some common methods to prevent unnecessary re-renders of a component in React?

    Here are some ways to prevent unnecessary re-renders:

    • React.memo: Memoizes a component, preventing re-renders when props haven't changed.
    • useMemo: Memoizes values, useful for expensive calculations.
    • useCallback: Memoizes functions, ensuring that they don't get recreated on every render.
    • shouldComponentUpdate: A lifecycle method that determines if a component should re-render (for class components).
    • PureComponent: Extends React.Component and only re-renders when props or state change.
    • Key Prop in Lists: Using a proper key for list items can help React efficiently update only the necessary DOM elements. Example:
    // Using React.memo
    const MyComponent = React.memo(({ name }) => {
    return <h1>{name}</h1>;
    });
    // Using useMemo
    const computedValue = useMemo(() => expensiveComputation(a, b), [a, b]);
    // Using useCallback
    const memoizedCallback = useCallback(() => {
    console.log('This function is memoized');
    }, []);

    11. How do hooks improve React components?

    Hooks allow using state and lifecycle features in functional components, making them concise, reusable, and easier to test.

    12. What is the purpose of the 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:

    • Empty array ([]): Runs only after the initial render
    • No array provided: Runs after every render
    • Specific dependencies ([dep1, dep2]): Runs when any dependency changes

    Read more about it here

    13. What is the difference between useMemo 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]);

    Read more about it here

    14. What is the 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);

    Read more about it here

    15. What is the difference between 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>;
    }

    Read more about it here

    16. Explain the React component lifecycle methods in class components.

    React class components have lifecycle methods for different phases:

    Mounting:

    • constructor: Initializes state or binds methods
    • componentDidMount: Runs after the component mounts, useful for API calls or subscriptions
    componentDidMount() {
    console.log('Component mounted');
    }

    Updating:

    • shouldComponentUpdate: Determines if the component should re-render
    • componentDidUpdate: Runs after updates, useful for side effects

    Unmounting:

    • componentWillUnmount: Cleans up (e.g., removing event listeners).
    componentWillUnmount() {
    console.log('Component will unmount');
    }

    These methods allow you to manage component behavior throughout its lifecycle.

    17. How do 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>);

    18. What is the purpose of the 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} />);
    }

    Read more about it here

    19. Can you explain how to use 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.

    20. How do you avoid or handle performance bottlenecks in a large-scale React application?

    • 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

    21. What are higher-order components in React?

    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);

    Read more about it here

    22. Explain how React's Context API works and when to use it

    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.

    When to use:

    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>
    );
    };

    23. What are the benefits and limitations of using React's Context API for state management?

    Benefits:

    • Simplifies State Sharing: Allows easy sharing of state across components without prop drilling.
    • Eliminates Prop Drilling: Avoids passing props through multiple layers, making the code more maintainable.

    Limitations:

    • Inefficient for Frequent Updates: Re-renders all components that consume the context on state changes, which can be inefficient for frequent updates.
    • Not Suitable for Large-Scale State Management: For complex or large applications with deep state needs, consider using state management libraries like Redux or Zustand for better performance and scalability.

    Read more about it here

    24. How do you implement lazy loading in React, and what is the role of 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>
    );
    }

    Read more about it here

    25. Can you explain how to create and use custom hooks in React?

    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 };
    }

    26. How do you manage forms in React?

    Controlled Components:

    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} />;

    Uncontrolled Components:

    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.

    27. What is the role of 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} />;

    28. How would you handle form validation in React?

    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.

    29. How do you handle dynamic form fields in React?

    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.

    30. Can you explain the debouncing technique for form inputs in React?

    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);

    31. How would you test a React component using Jest and React Testing Library?

    • Write tests to ensure proper rendering, interaction, and output.
    • Use render, fireEvent, and screen for assertions.
    test('renders button', () => {
    render(<button>Click Me</button>);
    expect(screen.getByText('Click Me')).toBeInTheDocument();
    });

    32. What is the purpose of snapshot testing in React?

    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.

    33. How do you handle asynchronous testing in React components?

    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.

    34. What are the key differences between shallow rendering and full DOM rendering in React tests?

    • Shallow Rendering: Renders only the component being tested, without rendering its child components. Useful for isolated unit testing.
    • Full DOM Rendering: Mounts the entire component tree, including children, providing a complete DOM structure. Ideal for integration tests.

    35. How would you mock API calls in a React test?

    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.

    36. How does React Router work, and how do you implement dynamic routing?

    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 parameter
    return <h1>User ID: {id}</h1>;
    }
    export default function App() {
    return (
    <BrowserRouter>
    <Routes>
    <Route path="/user/:id" element={<UserPage />} /> {/* Dynamic path */}
    </Routes>
    </BrowserRouter>
    );
    }

    Key Features:

    • Dynamic Segments: :id captures dynamic data from the URL.
    • useParams Hook: Accesses these dynamic values for rendering.

    37. How do you handle nested routes and route parameters in React Router?

    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 layout
    • useParams: Retrieves route parameters for dynamic routing
    import {
    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>
    );
    }

    38. What is the difference between BrowserRouter and HashRouter?

    • 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).

    39. How would you implement route guards or private routes in React?

    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+.

    40. How do you manage the active route state in a multi-page React application?

    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>
    );
    }

    41. What are error boundaries in React, and how do they help handle errors in the component tree?

    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;
    }
    }

    Read more about it here

    42. Can you explain how to implement global error handling in a React application?

    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.

    43. How do you handle asynchronous errors in React?

    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);
    }
    };

    44. How would you show a fallback UI in case of errors or slow network requests?

    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.

    45. Can you describe a strategy for handling large-scale error logging and monitoring in React?

    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.

    46. What are React portals, and in what scenarios would you use them?

    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'));

    Read more about it

    47. Can you explain the concept of React Suspense, and how does it help in data fetching?

    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>

    48. What is Concurrent Mode in React, and how does it improve rendering performance?

    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.

    49. How does React handle concurrent rendering with multiple updates and prioritize them?

    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.

    50. How would you handle long-running tasks or expensive computations in React applications without blocking the UI?

    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);
    }, []);
  • 30 Essential React Hooks Interview Questions You Must KnowPrepare with 30 key React Hooks interview questions you must know. Comprehensive guide for developers seeking success in job interviews.
    Author
    GreatFrontEnd Team
    9 min read
    Jun 13, 2025
    30 Essential React Hooks Interview Questions You Must Know

    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:

    1. What are React hooks?

    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.

    2. What are the benefits of using hooks in React?

    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.

    Read the detailed answer here

    3. What are the rules of React hooks?

    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.

    Read the detailed answer here

    4. What is the purpose of 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.

    Read the detailed answer here

    5. How does 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.

    6. Can you pass a function to 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);

    Read the detailed answer here

    7. Explain how useEffect works

    The useEffect hook handles side effects like data fetching or DOM updates. It runs after rendering and is controlled by a dependencies array:

    • No dependencies: Runs after every render.
    • Empty array: Runs once after the initial render.
    • With dependencies: Runs when specified dependencies change.
    useEffect(() => {
    console.log('Effect ran');
    return () => console.log('Cleanup logic'); // Runs on unmount or dependency change
    }, [dependency]);

    8. What are the different types of dependencies in useEffect?

    • Empty array ([]): The effect runs only once, after the initial render.
    • Dependencies array ([a, b]): The effect runs whenever the specified dependencies change.
    • No dependencies: The effect runs after every render, which can lead to performance issues.

    Read the detailed answer here

    9. How would you fetch data in 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();
    }, []);

    Read the detailed answer here

    10. How do you handle cleanup in 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 code
    return () => {
    // Cleanup code
    };
    }, [dependencies]);

    11. What is the purpose of 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" />;
    }

    Read the detailed answer here

    12. What is the difference between 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.

    13. What is the purpose of 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.

    14. What is the 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>
    );
    }

    Read the detailed answer here

    15. What are custom hooks in React?

    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.

    16. What are some use cases for 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.

    17. How does 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]);

    Read the detailed answer here

    18. What is the difference between 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.

    19. When should you use 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.

    20. What is the 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.

    21. How does useDeferredValue improve UI responsiveness?

    useDeferredValue defers updates to a value, prioritizing smoother UI interactions.

    22. What is useTransition?

    useTransition manages state transitions with a lower priority, useful for rendering smooth updates.

    23. What are the pitfalls of incorrect dependency management in hooks?

    • Stale state: Forgetting dependencies can cause the effect to use outdated values.
    • Infinite loops: Incorrect dependencies can cause effects to run repeatedly.

    24. What is the difference between 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>;
    }

    Read the detailed answer here

    25. What is the purpose of 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]);

    Read the detailed answer here

    26. How does 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);

    Read the detailed answer here

    27. What is the 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.

    28. How can you prevent unnecessary re-renders in React?

    To prevent unnecessary re-renders:

    • Use React.memo to memoize functional components.
    • Use useMemo to memoize calculations.
    • Use useCallback to memoize event handlers.
    • Properly manage state and minimize the number of state updates.

    29. When would you use custom hooks?

    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;
    };

    30. What is the purpose of callback function argument format of 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.

    Read the detailed answer here


    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.

  • 50 Essential React.js Interview Questions by Ex-Interviewers50 React interview questions and answers prioritized by senior engineers and ex-interviewers from leading tech companies
    Author
    GreatFrontEnd Team
    18 min read
    Jun 9, 2025
    50 Essential React.js Interview Questions by Ex-Interviewers

    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:

    1. What is React and its benefits?

    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.

    React Features

    Read the detailed answer here

    2. Difference Between React Node, Element, and Component

    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.

    Read the detailed answer here

    3. What is JSX and how does it work?

    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!').

    How JSX works

    Read the detailed answer here

    4. Difference between state and props in React

    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.

    State vs Props

    Read the detailed answer here

    5. What is the purpose of the 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} />);
    }

    Read the detailed answer here

    6. What is the consequence of using array indices as keys in React?

    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.

    Read the detailed answer here

    7. What is the difference between Controlled and Uncontrolled React components?

    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 (
    <input
    type="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} />;
    }

    Read the detailed answer here

    8. What are some pitfalls of using context in React?

    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.

    Read the detailed answer here

    9. What are the benefits of using hooks in React?

    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.

    Read the detailed answer here

    10. What are the rules of React hooks?

    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.

    Read the detailed answer here

    11. What is the difference between 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.

    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>;
    }

    Read the detailed answer here

    12. What does the dependency array of useEffect affect?

    The dependency array of useEffect controls when the effect re-runs:

    • If it's empty, the effect runs only once after the initial render.
    • If it contains variables, the effect re-runs whenever any of those variables change.
    • If omitted, the effect runs after every render.

    Read the detailed answer here

    13. What is the 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" />;
    }

    Read the detailed answer here

    14. What is the purpose of callback function argument format of 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.

    Read the detailed answer here

    15. What is the 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]);

    Read the detailed answer here

    16. What is the 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]);

    Read the detailed answer here

    17. What is the 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);

    Read the detailed answer here

    18. What is the 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>
    );
    }

    Read the detailed answer here

    19. What does re-rendering mean in React?

    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.

    Virtual DOM vs Browser DOM

    Read the detailed answer here

    20. What are React Fragments used for?

    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 />
    </>
    );

    Read the detailed answer here

    21. What is 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} />);

    Read the detailed answer here

    22. How do you reset a component's state in React?

    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);

    Read the detailed answer here

    23. Why does React recommend against mutating state?

    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.

    Read the detailed answer here

    24. What are error boundaries in React for?

    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.

    Read the detailed answer here

    25. How do you test React applications?

    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.

    Read the detailed answer here

    26. Explain what React hydration is

    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 Hydration

    Read the detailed answer here

    27. What are React Portals used for?

    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.

    Read the detailed answer here

    28. How do you debug React applications?

    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.

    Read the detailed answer here

    29. What is React strict mode and what are its benefits?

    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.

    Read the detailed answer here

    30. How do you localize React applications?

    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-i18next
    import { useTranslation } from 'react-i18next';
    const MyComponent = () => {
    const { t } = useTranslation();
    return <p>{t('welcome_message')}</p>;
    };

    Read the detailed answer here

    31. What is code splitting in a React application?

    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 Suspense
    const LazyComponent = React.lazy(() => import('./LazyComponent'));
    function App() {
    return (
    <React.Suspense fallback={<div>Loading...</div>}>
    <LazyComponent />
    </React.Suspense>
    );
    }

    Read the detailed answer here

    32. How would one optimize the performance of React contexts to reduce rerenders?

    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]);

    Read the detailed answer here

    33. What are higher-order components in React?

    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);

    Read the detailed answer here

    34. What is the Flux pattern?

    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.

    Flux pattern

    Read the detailed answer here

    35. Explain one-way data flow of React

    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.

    React data flow

    Read the detailed answer here

    36. How do you handle asynchronous data loading in React applications?

    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;

    Read the detailed answer here

    37. Explain server-side rendering of React applications and its benefits

    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.

    Server side rendering

    Read the detailed answer here

    38. Explain static generation of React applications

    Static generation pre-renders HTML at build time instead of runtime; this approach enhances performance by delivering static content quickly while improving SEO outcomes.

    Read the detailed answer here

    39. Explain the presentational vs container component pattern in React

    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.

    Read the detailed answer here

    40. What are some common pitfalls when doing data fetching in React?

    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.

    Read the detailed answer here

    41. What are render props in React?

    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
    <DataFetcher
    url="/api/data"
    render={(data) => <div>{data ? data.name : 'Loading...'}</div>}
    />;

    Read the detailed answer here

    42. What are some React anti-patterns?

    React anti-patterns are practices that can lead to inefficient or hard-to-maintain code. Common examples include:

    • Directly mutating state instead of using setState
    • Using componentWillMount for data fetching
    • Overusing componentWillReceiveProps
    • Not using keys in lists
    • Excessive inline functions in render
    • Deeply nested state

    Read the detailed answer here

    43. How do you decide between using React state, context, and external state managers?

    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.

    Read the detailed answer here

    44. Explain the composition pattern in React

    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>;
    }

    Read the detailed answer here

    45. What is virtual DOM in React?

    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.

    Virtual DOM

    Read the detailed answer here

    46. How does virtual DOM in React work? What are its benefits and downsides?

    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.

    How Virtual DOM works

    Read the detailed answer here

    47. What is React Fiber and how is it an improvement over the previous approach?

    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.

    Read the detailed answer here

    48. What is reconciliation in React?

    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 Reconciliation

    Read the detailed answer here

    49. What is React Suspense?

    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>
    );
    }

    Read the detailed answer here

    50. Explain what happens when setState is called in React

    When 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.

    Read the detailed answer here

    Conclusion

    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.

  • Practice 50 React Coding Interview Questions with SolutionsPractice 50 React coding interview questions with solutions. Essential for front end developers aiming to excel in their 2025 job interviews
    Author
    GreatFrontEnd Team
    7 min read
    May 30, 2025
    Practice 50 React Coding Interview Questions with Solutions

    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:

    Why practice React coding questions?

    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:

    • Problem solving: Tackle real-world UI problems
    • Component-based thinking: How you structure and optimize React components
    • State management: Work with React's state and hooks in practical scenarios
    • Strong foundation: Ability to create reusable and accessible UI components

    Here's a sneak peek at some of the most popular React coding questions available on our platform:

    Beginner-friendly questions

    1. Accordion

    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 ->

    2. Contact Form

    Build a contact form which submits user feedback and contact details to a back end API.

    Practice question on GreatFrontEnd ->

    3. Holy Grail

    Build the famous holy grail layout consisting of a header, 3 columns, and a footer.

    Practice question on GreatFrontEnd ->

    4. Progress Bars

    Build a list of progress bars that fill up gradually when they are added to the page.

    Practice question on GreatFrontEnd ->

    5. Mortgage Calculator

    Build a calculator that computes the monthly mortgage for a loan.

    Practice question on GreatFrontEnd ->

    6. Flight Booker

    Build a component that books a flight for specified dates.

    Practice question on GreatFrontEnd ->

    7. Generate Table

    Generate a table of numbers given the rows and columns.

    Practice question on GreatFrontEnd ->

    8. Progress Bar

    Build a progress bar component that shows the percentage completion of an operation.

    Practice question on GreatFrontEnd ->

    9. Temperature Converter

    Build a temperature converter widget that converts temperature values between Celsius and Fahrenheit.

    Practice question on GreatFrontEnd ->

    10. Tweet

    Build a component that resembles a Tweet from Twitter.

    Practice question on GreatFrontEnd ->

    Intermediate questions

    11. Tabs

    Build a tabs component that displays a list of tab elements and one associated panel of content at a time.

    Practice question on GreatFrontEnd ->

    12. Data Table

    Build a users data table with pagination features.

    Practice question on GreatFrontEnd ->

    13. Dice Roller

    Build a dice roller app that simulates the results of rolling 6-sided dice.

    Practice question on GreatFrontEnd ->

    14. File Explorer

    Build a file explorer component to navigate files and directories in a tree-like hierarchical viewer.

    Practice question on GreatFrontEnd ->

    15. Like Button

    Build a Like button that changes appearance based on the states.

    Practice question on GreatFrontEnd ->

    16. Modal Dialog

    Build a reusable modal dialog component that can be opened and closed.

    Practice question on GreatFrontEnd ->

    17. Star Rating

    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 ->

    18. Todo List

    Build a Todo list that lets users add new tasks and delete existing tasks.

    Practice question on GreatFrontEnd ->

    19. Traffic Light

    Build a traffic light where the lights switch from green to yellow to red after predetermined intervals and loop indefinitely.

    Practice question on GreatFrontEnd ->

    20. Digital Clock

    Build a 7-segment digital clock that shows the current time.

    Practice question on GreatFrontEnd ->

    21. Tic-tac-toe

    Build a tic-tac-toe game that is playable by two players.

    Practice question on GreatFrontEnd ->

    22. Image Carousel

    Build an image carousel that displays a sequence of images.

    Practice question on GreatFrontEnd ->

    23. Job Board

    Build a job board that displays the latest job postings from Hacker News.

    Practice question on GreatFrontEnd ->

    24. Stopwatch

    Build a stopwatch widget that can measure how much time has passed.

    Practice question on GreatFrontEnd ->

    25. Transfer List

    Build a component that allows transferring of items between two lists.

    Practice question on GreatFrontEnd ->

    26. Accordion II

    Build an accessible accordion component that has the right ARIA roles, states, and properties.

    Practice question on GreatFrontEnd ->

    27. Accordion III

    Build a fully accessible accordion component that has keyboard support according to ARIA specifications.

    Practice question on GreatFrontEnd ->

    28. Analog Clock

    Build an analog clock where the hands update and move like a real clock.

    Practice question on GreatFrontEnd ->

    29. Data Table II

    Build a users data table with sorting features.

    Practice question on GreatFrontEnd ->

    30. File Explorer II

    Build a semi-accessible file explorer component that has the right ARIA roles, states, and properties.

    Practice question on GreatFrontEnd ->

    31. File Explorer III

    Build a file explorer component using a flat DOM structure.

    Practice question on GreatFrontEnd ->

    32. Grid Lights

    Build a grid of lights where the lights deactivate in the reverse order they were activated.

    Practice question on GreatFrontEnd ->

    33. Modal Dialog II

    Build a semi-accessible modal dialog component that has the right ARIA roles, states, and properties.

    Practice question on GreatFrontEnd ->

    34. Modal Dialog III

    Build a moderately-accessible modal dialog component that supports common ways to close the dialog.

    Practice question on GreatFrontEnd ->

    35. Progress Bars II

    Build a list of progress bars that fill up gradually in sequence, one at a time.

    Practice question on GreatFrontEnd ->

    36. Tabs II

    Build a semi-accessible tabs component that has the right ARIA roles, states, and properties.

    Practice question on GreatFrontEnd ->

    37. Tabs III

    Build a fully accessible tabs component that has keyboard support according to ARIA specifications.

    Practice question on GreatFrontEnd ->

    38. Progress Bars III

    Build a list of progress bars that fill up gradually concurrently, up to a limit of 3.

    Practice question on GreatFrontEnd ->

    39. Birth Year Histogram

    Build a widget that fetches birth year data from an API and plot it on a histogram.

    Practice question on GreatFrontEnd ->

    40. Connect Four

    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 ->

    41. Image Carousel II

    Build an image carousel that smoothly transitions between images.

    Practice question on GreatFrontEnd ->

    Advanced questions

    42. Nested Checkboxes

    Build a nested checkboxes component with parent-child selection logic.

    Practice question on GreatFrontEnd ->

    43. Auth Code Input

    Build an auth code input component that allows users to enter a 6-digit authorization code.

    Practice question on GreatFrontEnd ->

    44. Progress Bars IV

    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 ->

    45. Data Table III

    Build a generalized data table with pagination and sorting features.

    Practice question on GreatFrontEnd ->

    46. Modal Dialog IV

    Build a fully-accessible modal dialog component that supports all required keyboard interactions.

    Practice question on GreatFrontEnd ->

    47. Selectable Cells

    Build an interface where users can drag to select multiple cells within a grid.

    Practice question on GreatFrontEnd ->

    48. Wordle

    Build Wordle, the word-guessing game that took the world by storm.

    Practice question on GreatFrontEnd ->

    49. Tic-tac-toe II

    Build an N x N tic-tac-toe game that requires M consecutive marks to win.

    Practice question on GreatFrontEnd ->

    50. Image Carousel III

    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.

  • Advanced JavaScript Interview Questions for 10+ Years of ExperienceExplore advanced JavaScript interview questions and answers designed for engineers with 10+ years of experience, curated by big tech senior engineers.
    Author
    GreatFrontEnd Team
    15 min read
    Apr 26, 2025
    Advanced JavaScript Interview Questions for 10+ Years of Experience

    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:

    1. Explain the concept of a microtask queue?

    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

    2. What are the potential pitfalls of using closures?

    Potential pitfalls of using closures in JavaScript include:

    • Memory Leaks: Closures can unintentionally keep outer function scopes alive, causing memory leaks.
    • Variable Sharing: They can lead to unexpected variable sharing between closures.
    • Performance Issues: Overuse can impact performance due to increased memory usage.
    • Debugging Complexity: Understanding and debugging code with closures can be challenging due to the complexity of the scope chain.

    Explore the potential pitfalls of using closures on GreatFrontEnd

    3. What is the typical use case for anonymous functions?

    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 here
    console.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

    4. What are some advantages and disadvantages of using TypeScript with JavaScript?

    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.

    5. What is the Event Loop in JavaScript? How does it handle asynchronous operations?

    The event loop in JavaScript manages asynchronous operations to prevent blocking the single-threaded execution:

    Parts of the Event Loop:

    • Call Stack: Tracks and executes functions in a Last In, First Out (LIFO) manner.
    • Web APIs/Node.js APIs: Handle asynchronous tasks like setTimeout(), HTTP requests, and file I/O on separate threads.
    • Task Queue (Macrotask Queue/Callback Queue): Stores callbacks from completed asynchronous tasks.
    • Microtasks Queue: Executes higher-priority tasks immediately after the current script finishes.

    Event Loop Order:

    1. JavaScript executes synchronous code, adding tasks to the call stack.
    2. Asynchronous tasks are delegated to APIs for background processing.
    3. Completed task callbacks are queued in the task or microtasks queue.
    4. When the call stack is empty:
      • Microtasks queue is processed first.
      • Then, tasks in the macrotask queue are executed in order.

    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

    6. Explain the concept of data binding in JavaScript?

    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:

    1. One-way Data Binding: Updates in the model reflect in the view.
    2. Two-way Data Binding: Changes in the model update the view and vice versa. This approach is common in frameworks like Angular, React with state management, and Vue.js, simplifying UI updates and user interactions.

    Explore the concept of data binding in JavaScript on GreatFrontEnd

    7. What are the potential issues caused by hoisting?

    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); // undefined
    var a = 5;
    console.log(b); // ReferenceError: b is not defined
    let 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

    8. What is async/await and how does it simplify asynchronous code?

    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:

    • The async function fetchData() uses await to fetch data from an API endpoint.
    • Using await ensures that each asynchronous operation completes before proceeding, simplifying error handling within the try...catch block.

    Explore async/await and how it simplifies asynchronous code on GreatFrontEnd

    9. What are iterators and generators in JavaScript and what are they used for?

    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, 5
    result = 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

    10. What are Web Workers and how can they be used to improve performance?

    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

    11. Explain the concept of memoization in JavaScript and how it can be implemented.

    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: 8
    console.log(memoizedFibonacci(7)); // Output: 13
    console.log(memoizedFibonacci(6)); // Output: 8 (retrieved from cache)

    12. How can you optimize DOM manipulation for better performance?

    To optimize performance and reduce reflows and repaints, follow these strategies:

    • Minimize DOM Manipulations: Reduce direct changes to the DOM by batching updates whenever possible.
    • Batch DOM Changes: Use techniques like DocumentFragment or innerHTML to insert multiple DOM nodes at once.
    • Use CSS Classes: Apply style changes using CSS classes rather than manipulating styles directly with JavaScript.
    • Avoid Complex CSS Selectors: Simplify CSS selectors to improve rendering performance.
    • Use requestAnimationFrame: Schedule animations and layout changes using requestAnimationFrame for smoother rendering.
    • Optimize with will-change: Mark elements that will undergo frequent changes with the will-change CSS property to optimize rendering.
    • Separate Read and Write Operations: Avoid layout thrashing by reading from the DOM separately from writing to it, minimizing reflows.

    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

    13. What are JavaScript polyfills for?

    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.

    How Polyfills Operate

    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;
    };
    }

    Steps to Implement Polyfills

    1. Identify the missing feature: Determine if the feature is supported by the target browsers using methods like typeof, in, or window.
    2. Create the fallback implementation: Develop a custom solution that mimics the behavior of the missing feature using JavaScript code.
    3. Test and integrate: Thoroughly test the polyfill across various browsers to ensure consistent functionality and integrate it into your project.

    Considerations

    • Selective loading: Load polyfills only when necessary to optimize performance and minimize unnecessary code execution.
    • Feature detection: Use feature detection techniques to avoid overwriting native browser implementations and ensure seamless integration.

    Popular Polyfill Tools

    • core-js: A versatile library offering polyfills for a wide array of ECMAScript features.
    import 'core-js/actual/array/flat-map'; // Example: polyfill for Array.prototype.flatMap
    [1, 2].flatMap((it) => [it, it]); // Output: [1, 1, 2, 2]
    • Polyfill.io: A service that dynamically serves polyfills based on browser capabilities and specified feature requirements.
    <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

    14. What are the benefits of using a module bundler?

    Module bundlers like Webpack, Parcel, and Rollup offer key benefits for web development:

    1. Dependency Management: Efficiently manage dependencies between JavaScript modules.
    2. Code Optimization: Bundle and optimize code for production, including minification and tree shaking.
    3. Browser Compatibility: Handle compatibility across different browsers and environments, including transpiling for older browsers.
    4. Performance: Improve performance by reducing HTTP requests and supporting caching and lazy loading.
    5. Integration: Integrate well with build tools, preprocessors, testing frameworks, and deployment workflows.

    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

    15. Explain the concept of tree shaking in module bundling?

    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

    16. What are some common performance bottlenecks in JavaScript applications?

    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

    17. Explain the difference between unit testing, integration testing, and end-to-end testing

    Unit Testing:

    • Focus: Tests individual functions or methods in isolation.
    • Purpose: Ensures each unit of code behaves as expected.
    • Scope: Doesn't interact with external dependencies.
    • Tools: Jest, Mocha, Jasmine.

    Integration Testing:

    • Focus: Tests interactions between integrated units.
    • Purpose: Validates units work together correctly.
    • Scope: Includes databases, APIs, or external services.
    • Tools: Selenium, Postman, custom scripts.

    End-to-End Testing:

    • Focus: Tests the entire application workflow.
    • Purpose: Checks application behavior in real-world scenarios.
    • Scope: Includes UI, backend, and external dependencies.
    • Tools: Cypress, Puppeteer, Selenium WebDriver.

    Each type of testing plays a crucial role in ensuring software quality across different levels of application functionality and integration.

    Explore the difference between unit testing, integration testing, and end-to-end testing on GreatFrontEnd

    18. What are some tools and techniques for identifying security vulnerabilities in JavaScript code?

    Tools:

    1. Static Analysis: ESLint with security plugins, SonarQube, CodeQL.
    2. Dynamic Analysis: OWASP ZAP, Burp Suite.
    3. Dependency Scanning: npm Audit, Snyk.
    4. Browser Tools: Chrome DevTools, Firefox Developer Tools.

    Techniques:

    1. Secure Coding Practices: Input validation, output encoding, error handling.
    2. Penetration Testing: Manual or automated tests to simulate attacks.
    3. Regular Audits: Periodic reviews to detect and fix vulnerabilities.
    4. Security Headers: Implementing headers like Content Security Policy.

    Using these tools and techniques helps ensure JavaScript applications are secure against common vulnerabilities.

    Explore tools and techniques for identifying security vulnerabilities in JavaScript code on GreatFrontEnd

    19. Explain the concept of Content Security Policy (CSP) and how it enhances security

    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

    20. When would you use 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

    Conclusion

    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.

  • JavaScript Interview Questions for 5+ Years of ExperienceExplore advanced JavaScript interview questions and answers designed for engineers with 5+ years of experience, curated by big tech senior engineers.
    Author
    GreatFrontEnd Team
    18 min read
    Apr 17, 2025
    JavaScript Interview Questions for 5+ Years of Experience

    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:

    1. What's a typical use case for anonymous functions in JavaScript?

    Anonymous functions provide a concise way to define functions, especially useful for simple operations or callbacks. They are commonly used in:

    • Immediate execution: Encapsulate code within a local scope using Immediately Invoked Function Expressions (IIFEs).
    • Callbacks: Define handlers directly where they are called without having to search elsewhere.
    • Higher-order functions: Use as arguments to functions like map()filter(), and reduce().
    • Event Handling: Common in React for inline event handling.

    Checkout the below code example-

    // Encapsulating Code using IIFE
    (function () {
    // Some code here.
    })();
    // Callbacks
    setTimeout(function () {
    console.log('Hello world!');
    }, 1000);
    // Functional programming constructs
    const 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

    2. What is a closure in JavaScript, and why would you use one?

    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:

    • Data encapsulation: Create private variables and functions that can't be accessed from outside the closure.
    • Maintaining state: Particularly in functional programming and React hooks.
    • Event handlers and callbacks: Retain access to variables when handlers are defined.

    Explore what a closure is in JavaScript, and why you would use one on GreatFrontEnd

    3. What are the pros and cons of using Promises instead of callbacks in JavaScript?

    Pros:

    • Avoid callback hell: Promises simplify nested callbacks.

      // Callback hell
      getData1((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:

    • Can be slightly more complex to understand initially.

    Explore the pros and cons of using Promises instead of callbacks in JavaScript on GreatFrontEnd

    4. How do you abort a web request using AbortController in JavaScript?

    AbortController allows you to cancel ongoing asynchronous operations like fetch requests. To use it:

    1. Create an instance: const controller = new AbortController(); 2.Pass the signal: Add the signal to the fetch request options.
    2. Abort the request: Call 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 request
    controller.abort();

    Some of its use cases can be:

    • Cancel a fetch() request on a user action
    • Prioritize latest requests in a race condition
    • Cancel requests that are no longer needed

    Explore how to abort a web request using AbortController in JavaScript on GreatFrontEnd

    5. Why is extending built-in JavaScript objects not a good idea?

    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:

    • Future-proofing: Risk of conflicts with future implementations.
    • Collisions: Potential clashes with other libraries.
    • Maintenance: Difficult for other developers to understand.
    • Performance: Potential negative impact.
    • Security: Risk of introducing vulnerabilities.
    • Compatibility: Issues across different environments.

    Explore why extending built-in JavaScript objects is not a good idea on GreatFrontEnd

    6. Why is it, in general, a good idea to leave the global JavaScript scope of a website as-is and never touch it?

    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:

    • Naming conflicts: Multiple scripts sharing the global scope can lead to conflicts and bugs when new global variables or changes are introduced.
    • Cluttered global namespace: Minimizing the global namespace helps keep the codebase manageable and maintainable.
    • Scope leaks: Accidental references to global variables in closures or event handlers can result in memory leaks and performance issues.
    • Modularity and encapsulation: Good design practices involve keeping variables and functions within specific scopes, improving organization, reusability, and maintainability.
    • Security concerns: Global variables are accessible by all scripts, including potentially malicious ones, posing security risks, especially if they contain sensitive data.
    • Compatibility and portability: Relying heavily on global variables reduces code portability and complicates integration with other libraries or frameworks.

    Explore why it is, in general, a good idea to leave the global JavaScript scope of a website as-is and never touch it on GreatFrontEnd

    7. Explain the differences between CommonJS modules and ES modules in JavaScript?

    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.js
    const value = 42;
    module.exports = { value };
    // main.js
    const 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.js
    export const value = 42;
    // main.js
    import { value } from './my-module.js';
    console.log(value); // 42

    Explore the differences between CommonJS modules and ES modules in JavaScript on GreatFrontEnd

    8. Explain the difference between mutable and immutable objects in JavaScript

    Immutability is a core principle in functional programming but it has lots to offer to object-oriented programs as well.

    Mutable objects

    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 object
    mutableObject.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.

    Immutable objects

    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 object
    immutableObject.name = 'Jane'; // This change won't affect the object
    console.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 objects

    A 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 const
    const person = { name: 'John' };
    person = { name: 'Jane' }; // Error: Assignment to constant variable
    person.name = 'Jane'; // Allowed, person.name is now 'Jane'
    // Using Object.freeze() to create an immutable object
    const 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

    9. Why might you want to create static class members in JavaScript?

    Static class members in JavaScript, denoted by the static keyword, are accessed directly on the class itself, not on instances. They serve multiple purposes:

    1. Namespace Organization: They help organize constants or configuration values within the class, preventing naming conflicts.
    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
    1. Helper Functions: Static methods act as utility functions for the class or its instances, improving code readability.
    class Arithmetic {
    static add(a, b) {
    return a + b;
    }
    static subtract(a, b) {
    return a - b;
    }
    }
    console.log(Arithmetic.add(2, 3)); // Output: 5
    console.log(Arithmetic.subtract(5, 2)); // Output: 3
    1. Singleton Pattern: They can facilitate the singleton pattern, ensuring only one instance of a class exists.
    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
    1. Performance Benefits: Static members reduce memory usage by being shared across all instances.

    Explore why you might want to create static class members in JavaScript on GreatFrontEnd

    10. What are 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 unique
    const obj = {};
    const sym = Symbol('uniqueKey');
    obj[sym] = 'value';
    console.log(obj[sym]); // "value"

    Key characteristics include:

    • Uniqueness: Each Symbol value is unique.
    • Immutability: Symbol values cannot be changed.
    • Non-enumerability: Symbol properties are not listed in 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); // true
    const 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

    11. What are JavaScript object getters and setters for?

    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

    12. What tools and techniques do you use for debugging JavaScript code?

    Tools and techniques for debugging JavaScript code vary depending on the context:

    • JavaScript
      • Chrome Devtools: Provides a comprehensive set of debugging tools including breakpoints, stepping through code, watching variables, profiling performance, etc. You can learn more about it here.
      • debugger statement: Inserting debugger; in code triggers breakpoints when Devtools are open, pausing execution for inspection.
      • Traditional console.log() debugging: Using console.log() statements to output variable values and debug messages.
    • React and Redux
      • React Devtools: Facilitates debugging React component hierarchies, state, and props.
      • Redux Devtools: Enhances debugging Redux state changes, actions, and dispatched events.
    • Vue
      • Vue Devtools: Extends Vue.js development experience with component inspection, state visualization, and event tracking.

    Explore what tools and techniques are used for debugging JavaScript code on GreatFrontEnd

    13. Can you give an example of a curry function and why this syntax offers an advantage?

    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 function
    function 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 curried
    function multiply(a, b, c) {
    return a * b * c;
    }
    // Currying the multiply function
    const curriedMultiply = curry(multiply);
    // Applying curried functions
    const step1 = curriedMultiply(2); // partially apply 2
    const step2 = step1(3); // partially apply 3
    const result = step2(4); // apply the final argument
    console.log(result); // Output: 24

    Advantages of Curry Syntax:

    • Partial Application: You can create specialized versions of functions by fixing some arguments, making it easier to reuse and compose functions.
    • Modularity and Reusability: Encourages modular programming by breaking down functions into smaller, reusable parts.
    • Flexibility: Allows for function chaining and incremental application of arguments, which can improve code readability and maintainability.

    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

    14. What is the difference between the document 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.

    Explore the difference between the document load event and the document DOMContentLoaded event on GreatFrontEnd

    15. Explain how JSONP works (and how it's not really Ajax).

    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

    16. Explain the same-origin policy with regard to JavaScript.

    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

    17. Explain what a single page app is and how to make one SEO-friendly.

    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:

    • Improved responsiveness with no full-page refreshes.
    • Fewer HTTP requests, as assets are reused.
    • Clear separation between client and server, allowing independent updates.

    Cons:

    • Heavier initial load due to loading all necessary assets upfront.
    • Requires server configuration to route all requests to a single entry point.
    • SEO challenges, as some search engines may not execute JavaScript, resulting in empty content.

    To enhance SEO for SPAs, consider:

    • Server-side rendering to generate HTML on the server.
    • Using services like Prerender to render JavaScript in a browser, save static HTML, and return it to search engine crawlers.

    Explore what a single page app is and how to make one SEO-friendly on GreatFrontEnd

    18. How do you organize your code?

    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

    19. What is the extent of your experience with Promises and/or their polyfills?

    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

    20. What's the difference between an "attribute" and a "property"?

    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

    Conclusion

    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.

  • JavaScript Interview Questions for 2 Years of ExperienceExplore a list of JavaScript interview questions and answers tailored for engineers with 2 years of experience, curated by big tech senior engineers.
    Author
    GreatFrontEnd Team
    16 min read
    Apr 12, 2025
    JavaScript Interview Questions for 2 Years of Experience

    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:

    1. Explain the concept of caching and how it can be used to improve performance

    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

    2. Explain the concept of lazy loading and how it can improve performance

    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

    3. What are design patterns and why are they useful?

    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

    4. Explain the concept of the Prototype pattern

    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

    5. Explain the concept of the Singleton pattern

    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

    6. What is the Factory pattern and how is it used?

    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

    7. Explain the Observer pattern and its use cases

    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

    8. What is the Decorator pattern and how is it used?

    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

    9. Explain the concept of the Strategy pattern

    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 A
    return data.sort(); // Example: sorting algorithm
    }
    }
    class ConcreteStrategyB {
    algorithm(data) {
    // Implementation of algorithm B
    return data.reverse(); // Example: reverse algorithm
    }
    }
    // Usage
    const 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

    10. What is the Command pattern and how is it used?

    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 on
    lightOnCommand.undo(); // Light is off

    Explore the Command pattern and how it is used on GreatFrontEnd

    11. What is the Module pattern and how does it help with encapsulation?

    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

    12. How can you avoid problems related to hoisting?

    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 const
    let x = 10;
    const y = 20;
    // Declare functions before calling them
    function myFunction() {
    console.log('Hello, world!');
    }
    myFunction();

    Explore how you can avoid problems related to hoisting on GreatFrontEnd

    13. How can you share code between JavaScript files?

    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.js
    export function greet() {
    console.log('Hello, world!');
    }
    // file2.js
    import { greet } from './file1.js';
    greet();

    Alternatively, in Node.js, you can use module.exports and require:

    Using CommonJS Modules (Node.js):

    // file1.js
    module.exports = function greet() {
    console.log('Hello, world!');
    };
    // file2.js
    const greet = require('./file1.js');
    greet();

    Explore you can share code between JavaScript files on GreatFrontEnd

    14. How do you get the query string values of the current page in JavaScript?

    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 string
    const params = new URLSearchParams(window.location.search);
    // Retrieve specific query parameter values
    const keyValue = params.get('key'); // 'value'
    const fooValue = params.get('foo'); // 'bar'
    // Example usage
    console.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

    15. How do you handle errors in asynchronous operations?

    Handling errors in asynchronous operations can be done effectively with both async/await and Promises:

    Using async/await with try...catch:

    javascriptCopy code
    async 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);
    }
    }

    Using Promises with .catch() method:

    javascriptCopy code
    fetch('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

    16. How do you manipulate CSS styles using JavaScript?

    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 color
    document.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

    17. What are the common pitfalls of using the 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

    18. What is the DOM and how is it structured?

    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

    19. What do you think of AMD vs CommonJS?

    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

    20. What are the different ways to make an API call in JavaScript?

    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

    21. What are some tools that can be used for JavaScript testing?

    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

    22. What is the difference between 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

    23. What is the difference between 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 innerHTML
    element.innerHTML = '<strong>Bold Text</strong>'; // Renders as bold text
    // Example of textContent
    element.textContent = '<strong>Bold Text</strong>'; // Renders as plain text: <strong>Bold Text</strong>

    Explore the difference between innerHTML and textContent on GreatFrontEnd

    24. What is the difference between the 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

    25. What is the difference between 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

    26. How do you use 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 history
    history.pushState({ page: 1 }, 'title 1', '?page=1');
    // Replace the current history entry
    history.replaceState({ page: 2 }, 'title 2', '?page=2');
    // Navigate back, forward, or to a specific point in history
    history.back(); // Go back one step
    history.forward(); // Go forward one step
    history.go(-2); // Go back two steps

    Explore how to use window.history API on GreatFrontEnd

    27. What are the pros and cons of using Promises instead of callbacks in JavaScript?

    Pros of Promises over Callbacks:

    • Avoids Callback Hell: Promises provide a more linear and readable structure compared to deeply nested callbacks.
    • Sequential Execution: Easily write sequential asynchronous code using .then(), improving readability and maintainability.
    • Parallel Execution: Use Promise.all() for parallel asynchronous operations, handling multiple promises concisely.

    Cons:

    • Slightly More Complex: Some developers find promises marginally more complex compared to straightforward callbacks.

    Explore the pros and cons of using Promises instead of callbacks in JavaScript on GreatFrontEnd

    28. What are the metadata fields of a module?

    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

    29. What are the different types of errors in JavaScript?

    In JavaScript, there are three main types of errors:

    1. Syntax Errors: Occur when the code violates the language's grammar rules, like missing a parenthesis.
    2. Runtime Errors: Happen during code execution, such as trying to access a property of undefined.
    3. Logical Errors: Mistakes in the code's logic that lead to incorrect results without throwing an error.

    Explore different types of errors in JavaScript on GreatFrontEnd

    30. Explain the concept of error propagation in JavaScript

    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

    Conclusion

    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.

  • Basic JavaScript Interview Questions and Answers For FreshersBrush up on fundamental JavaScript skills with these essential interview questions and answers. Perfect for freshers preparing for junior developer roles.
    Author
    GreatFrontEnd Team
    47 min read
    Apr 5, 2025
    Basic JavaScript Interview Questions and Answers For Freshers

    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:

    1. Explain the concept of "hoisting" in JavaScript

    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); // undefined
    var foo = 1;
    console.log(foo); // 1

    Visualized as:

    var foo;
    console.log(foo); // undefined
    foo = 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 initialization
    let y = 'local';
    console.log(z); // ReferenceError: Cannot access 'z' before initialization
    const z = 'local';
    console.log(Foo); // ReferenceError: Cannot access 'Foo' before initialization
    class Foo {
    constructor() {}
    }

    Function Expressions:

    Only the declaration is hoisted.

    console.log(bar); // undefined
    bar(); // Uncaught TypeError: bar is not a function
    var 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

    2. What are the differences between JavaScript variables created using letvar or const?

    Scope

    • 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); // 1
    console.log(baz); // 2
    console.log(qux); // 3
    }
    console.log(bar); // ReferenceError
    console.log(baz); // ReferenceError
    console.log(qux); // ReferenceError
    if (true) {
    var bar = 1;
    let baz = 2;
    const qux = 3;
    }
    console.log(bar); // 1
    console.log(baz); // ReferenceError
    console.log(qux); // ReferenceError

    Initialization

    • var and let: Can be declared without an initial value.
    • const: Must be initialized at the time of declaration.

    Example:

    var foo; // Ok
    let bar; // Ok
    const baz; // SyntaxError

    Redeclaration

    • var: Allows redeclaration.
    • let and const: Do not allow redeclaration.

    Example:

    var foo = 1;
    var foo = 2; // Ok
    let baz = 3;
    let baz = 4; // SyntaxError

    Reassignment

    • var and let: Allow reassignment.
    • const: Does not allow reassignment.

    Example:

    var foo = 1;
    foo = 2; // Ok
    let bar = 3;
    bar = 4; // Ok
    const baz = 5;
    baz = 6; // TypeError

    Accessing before declaration

    • 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); // undefined
    var foo = 'foo';
    console.log(baz); // ReferenceError
    let baz = 'baz';
    console.log(bar); // ReferenceError
    const bar = 'bar';

    Explore the differences between Javascript variables created using let, var, and const on GreatFrontEnd

    3. What is the difference between == and === in JavaScript?

    Equality Operator (==)

    • Performs type coercion, converting values to a common type before comparison.
    • Can yield unintuitive results due to type conversion.

    Examples:

    42 == '42'; // true
    0 == false; // true
    null == undefined; // true
    [] == false; // true
    '' == false; // true

    ###Strict Equality Operator (===)

    • Does not perform type coercion.
    • Both value and type must be the same for the comparison to return true.

    Examples:

    42 === '42'; // false
    0 === false; // false
    null === undefined; // false
    [] === false; // false
    '' === false; // false

    Best Practices

    • Use == only when comparing against null or undefined for convenience.

      var a = null;
      console.log(a == null); // true
      console.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

    4. What is the event loop in JavaScript?

    The event loop is crucial for handling asynchronous operations in JavaScript, allowing single-threaded execution without blocking.

    Components

    1. Call Stack:

    • Tracks function execution in LIFO order.
    • Functions are added and removed as they are called and complete.

    2. Web APIs/Node.js APIs:

    • Handle asynchronous tasks (setTimeout(), HTTP requests) on separate threads.

    3. Task Queue (Macrotask Queue):

    • Holds tasks like setTimeout(), setInterval(), and UI events.

    4. Microtask Queue:

    • Contains high-priority tasks (e.g., Promise callbacks).
    • Executes after the current script but before the next macrotask.

    Execution Order

    1. Script Execution:
      • Synchronous tasks are placed on the call stack.
    2. Asynchronous Operations:
      • Offloaded to Web APIs/Node.js APIs.
    3. Task Completion:
      • Completed tasks are queued in the appropriate task or microtask queue.
    4. Event Loop Monitoring:
      • Executes tasks from the call stack.
      • When the stack is empty:
        • Processes the microtask queue until empty.
        • Processes one macrotask, then checks the microtask queue again.
        • Repeats indefinitely.
    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:

    Start
    End
    Promise 1
    Timeout 1
    Timeout 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

    5. Explain event delegation in JavaScript

    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.

    How Event Delegation Works

    1. Attach Listener to Ancestor:
      • Attach one listener to a parent element instead of individual listeners on each child.
    2. Event Bubbling:
      • When an event occurs on a child, it bubbles up to the parent, where the listener can handle it.
    3. Determine the Target:
      • Use event.target to identify the actual element that triggered the event.
    4. Perform Action:
      • Execute actions based on the target element.

    Benefits of Event Delegation

    • Efficiency: Fewer event listeners improve memory and performance.
    • Dynamic Elements: Automatically handles new or removed child elements.
    // 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.

    Use Cases

    1. 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);
      });
    2. Simplifying Code:

      const userForm = document.getElementById('user-form');
      userForm.addEventListener('input', (event) => {
      const { name, value } = event.target;
      console.log(`Changed ${name}: ${value}`);
      });

      Explore event delegation in JavaScript on GreatFrontEnd

    6. Explain how this works in JavaScript

    The 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:

    1. Using the new Keyword

    Creates 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

    2. Using apply, call, or bind

    Explicitly sets this to a specified object.

    javascriptCopy code
    function greet() {
    console.log(this.name);
    }
    const person = { name: 'Alice' };
    greet.call(person); // 'Alice'

    3. Method Call

    this is bound to the object the method is called on.

    const obj = {
    name: 'Alice',
    greet: function () {
    console.log(this.name);
    },
    };
    obj.greet(); // 'Alice'

    4. Free Function Invocation

    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();

    5. Arrow Functions (ES6):

    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

    ES2015 (ES6) and this

    ES2015 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 instance
    console.log(this.seconds);
    }, 1000);
    }
    const timer = new Timer();

    Explore how this works in JavaScript on GreatFrontEnd

    7. Describe the difference between a cookie, sessionStorage and localStorage.

    Cookies, localStorage, and sessionStorage are key client-side storage mechanisms in web applications, each serving distinct purposes:

    Cookies

    • 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 expiry
      document.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 cookie
      document.cookie =
      'auth_token=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';

    localStorage

    • Purpose: 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 localStorage
      localStorage.setItem('key', 'value');
      // Get an item from localStorage
      console.log(localStorage.getItem('key'));
      // Remove an item from localStorage
      localStorage.removeItem('key');
      // Clear all data in localStorage
      localStorage.clear();

    sessionStorage

    • Purpose: 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 sessionStorage
      sessionStorage.setItem('key', 'value');
      // Get an item from sessionStorage
      console.log(sessionStorage.getItem('key'));
      // Remove an item from sessionStorage
      sessionStorage.removeItem('key');
      // Clear all data in sessionStorage
      sessionStorage.clear();

    Explore the difference between a cookie, sessionStorage and localStorage on GreatFrontEnd

    8. Describe the difference between <script>, <script async> and <script defer>

    <script> Tag

    The <script> tag is used to include JavaScript in a web page. When used without async or defer attributes:

    • Behavior: HTML parsing halts while the script is fetched and executed immediately.
    • Usage: Suitable for critical scripts required for initial page rendering.

    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> Tag

    • Behavior: Downloads the script asynchronously while HTML parsing continues. Executes the script as soon as it's available, potentially before HTML parsing completes.
    • Usage: Suitable for independent scripts like analytics or ads.

    Example:

    <!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:

    • Behavior: Downloads the script asynchronously while HTML parsing proceeds. Executes the script after the HTML document is fully parsed but before DOMContentLoaded.
    • Usage: Ideal for scripts dependent on a fully-parsed DOM structure.

    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

    9. What's the difference between a variable that is: 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:

    • Always declare variables before using them.
    • Assign null to variables if you don't intend to use them yet.
    • Use static analysis tools like ESLint or TypeScript Compiler to detect undeclared variables.

    Explore the difference between a variable that is: null, undefined or undeclared on GreatFrontEnd

    10. What's the difference between .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:

    • .call: Takes comma-separated arguments.
    • .apply: Takes an array of arguments.

    Memory Aid:

    • C for call and comma-separated.
    • A for apply and array.

    Example:

    javascriptCopy code
    function add(a, b) {
    return a + b;
    }
    console.log(add.call(null, 1, 2)); // 3
    console.log(add.apply(null, [1, 2])); // 3
    // ES6 with spread operator
    console.log(add.call(null, ...[1, 2])); // 3

    Explore the difference between .call and .apply in JavaScript on GreatFrontEnd

    11. Explain Function.prototype.bind

    Function.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()); // 42
    const unboundGetAge = john.getAge;
    console.log(unboundGetAge()); // undefined
    const boundGetAge = john.getAge.bind(john);
    console.log(boundGetAge()); // 42
    const mary = { age: 21 };
    const boundGetAgeMary = john.getAge.bind(mary);
    console.log(boundGetAgeMary()); // 21

    Its main purposes are:

    1. Binding 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.
    2. Partial application of arguments: 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.
    3. Method borrowing: 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

    12. What advantage is there for using the arrow syntax for a method in a constructor?

    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(); // John
    john.sayName2(); // John
    // `this` can change for regular functions but not for arrow functions
    john.sayName1.call(dave); // Dave
    john.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

    13. Explain how prototypal inheritance works

    Prototypical inheritance allows objects to inherit properties and methods from other objects using a prototype-based model.

    Key Concepts

    1. Prototypes

    • Every object has a prototype, another object it inherits from.
    • Access or modify prototypes using 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."

    2. Prototype Chain

    JavaScript looks for properties/methods on the object, then its prototype, and so on up the chain until null.

    3. Constructor Functions

    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"

    4. 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

    14. Difference between: function Person(){}, const person = Person(), and const person = new Person()?

    Function Declaration

    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.

    Function Call

    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.

    Constructor Call

    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.

    Explore the difference between: function Person(){}, const person = Person(), and const person = new Person() on GreatFrontEnd

    15. Explain the differences on the usage of foo between function foo() {} and var foo = function() {}

    Function Declarations

    • Syntax: function foo() {}

    • Description: Defines a named function that can be called throughout the enclosing scope.

    • Example:

      function foo() {
      console.log('FOOOOO');
      }

    Function Expressions

    • 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');
      };

    Key Differences

    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 function
      var 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

    Usage Recommendations

    Function Declarations:

    • Use for reusable functions available throughout the scope.

    Function Expressions:

    • Use for functions needed only in specific contexts to limit the scope.

    Explore the differences on the usage of foo between function foo() {} and var foo = function() {} on GreatFrontEnd

    16. What are the various ways to create objects in JavaScript?

    Here are the various ways to create objects in JavaScript:

    1. Object Literals ({}): Simplest way to create objects using key-value pairs within curly braces.

      const person = {
      firstName: 'John',
      lastName: 'Doe',
      };
    2. 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';
    3. 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.
    4. 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.
    5. Constructor Functions: Reusable blueprints for objects, using the new keyword to create instances.

      // Constructor function
      function 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

    17. What is the definition of a higher-order function?

    A higher-order function is a function that:

    1. 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!
    2. 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

    18. What are the differences between ES2015 classes and ES5 function constructors?

    ES5 Function Constructors

    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.

    ES2015 Classes

    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.

      Key differences

      Syntax and Readability

      • ES5: Function constructors and prototypes
      • ES2015: class keyword, more concise and easier to understand

      Static Methods

      • ES5: Added directly to the constructor function
      • ES2015: Defined within the class using static keyword

      Inheritance

      • ES5: Using Object.create() and manual prototype chain setting
      • ES2015: Using extends keyword, simpler and more intuitive

      Super Calls

      • ES5: Manual parent constructor function call
      • ES2015: Using super keyword to call parent class's constructor and methods

    Explore differences between ES2015 classes and ES5 function constructors on GreatFrontEnd

    19. Describe event bubbling

    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

    20. Describe event capturing

    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:

    1. Capturing Phase: The event moves down from the root to the target element.
    2. Target Phase: The event reaches the target element.
    3. Bubbling Phase: The event bubbles up from the target element back to the root.

    Enabling Event Capturing:

    • Event capturing is disabled by default.
    • Enable it by passing { 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

    21. What is the difference between mouseenter and mouseover event in JavaScript and browsers?

    mouseenter

    • Does not bubble
    • Triggered only when the mouse pointer enters the element itself, not its descendants
    • Only triggered once upon entry of parent element, without regard for its contents

    mouseover

    • Bubbles up the DOM tree
    • Triggered when the mouse pointer enters the element or one of its descendants
    • Can result in multiple event callbacks fired if there are child elements

    Explore the difference between mouseenter and mouseover event in JavaScript and browsers on GreatFrontEnd

    22. Explain the difference between synchronous and asynchronous functions

    Synchronous Functions

    • Execute in a sequential order, one after the other
    • Blocking, meaning the program execution halts until the current operation finishes
    • Follow a strict sequence, executing instructions line by line
    • Easier to understand and debug since the flow is predictable
    • Examples: reading files synchronously, looping over large datasets

    Example:

    const fs = require('fs');
    const data = fs.readFileSync('large-file.txt', 'utf8');
    console.log(data); // Blocks until file is read
    console.log('End of the program');

    Asynchronous Functions

    • Do not block the execution of the program
    • Allow other operations to continue while waiting for a response or completion of a time-consuming task
    • Non-blocking, enabling concurrent execution and improving performance and responsiveness
    • Commonly used for tasks like network requests, file I/O, and timers
    • Examples: network requests, user input and events, timers, and animations

    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

    23. Explain AJAX in as much detail as possible

    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:

    • Asynchronous: AJAX enables web applications to update parts of a web page without reloading the whole page.
    • Data Formats: Initially used XML, but JSON is now more common due to its compatibility with JavaScript.
    • APIs: Traditionally used XMLHttpRequest, but fetch() is now preferred for modern web applications.

    XMLHttpRequest API

    Example:

    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();
    • Flow: Creates a new XMLHttpRequest, sets up a callback function to handle state changes, opens a request to a URL, and sends the request.

    fetch() API

    Example:

    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));
    • Flow: Initiates a fetch request, handles the response with .then() to parse JSON data, and manages errors with .catch().

    How AJAX Works with fetch

    1. Making a Request

    • fetch() 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',
      },
      });

    2. Return a Promise

    • fetch() returns a Promise that resolves to a Response object representing the server's response.

    3. Handling the 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));

    4. Asynchronous Nature

    • fetch() is asynchronous, allowing the browser to continue executing other tasks while waiting for the server response.
    • Promises (.then(), .catch()) are handled in the microtask queue as part of the event loop.

    5. Request Options

    • The optional second argument to fetch() configures various request aspects, such as HTTP method, headers, body, credentials, and caching.

    6. Error Handling

    • Errors (e.g., network failures, invalid responses) are caught and propagated through the Promise chain using .catch() or try/catch with async/await.

    Explore how to explain AJAX in as much detail as possible on GreatFrontEnd

    24. What are the advantages and disadvantages of using AJAX?

    AJAX (Asynchronous JavaScript and XML) enables web pages to send and retrieve data asynchronously, allowing for dynamic updates without full page reloads.

    Advantages

    • AJAX provides a seamless user experience by updating content without full page reloads.
    • It reduces server load and improves performance by only fetching necessary data.
    • AJAX maintains user interactions and client states within the page.

    Disadvantages

    • AJAX relies on JavaScript, which can break functionality if disabled.
    • Dynamic content makes bookmarking specific page states challenging.
    • Search engines may struggle to index dynamic content, posing SEO challenges.
    • Processing AJAX data on low-end devices can be slow, raising performance concerns.

    Explore the advantages and disadvantages of using AJAX on GreatFrontEnd

    25. What are the differences between XMLHttpRequest and fetch()?

    Both XMLHttpRequest (XHR) and fetch() enable asynchronous HTTP requests in JavaScript, but differ in syntax, handling, and features.

    Syntax and Usage

    • XMLHttpRequest: Event-driven, requiring event listeners to handle responses and errors.
    • fetch(): Promise-based, offering simpler and more intuitive syntax.

    Request Headers

    • XMLHttpRequest: Set headers using the setRequestHeader method.
    • fetch(): Pass headers as an object in the options parameter.

    Request Body

    • XMLHttpRequest: Send body with the send method.
    • fetch(): Use the body property in the options parameter.

    Response Handling

    • XMLHttpRequest: Use responseType to handle different formats.
    • fetch(): Provides a unified Response object with .then for accessing data.

    Error Handling

    • XMLHttpRequest: Handle errors with onerror event.
    • fetch(): Handle errors using the .catch method.

    Caching Control

    • XMLHttpRequest: Difficult to manage, often requiring workaround methods.
    • fetch(): Supports caching options directly.

    Cancellation

    • XMLHttpRequest: Use the abort() method.
    • fetch(): Use AbortController for request cancellation.

    Progress Support

    • XMLHttpRequest: Supports tracking progress with the onprogress event.
    • fetch(): Does not support native progress tracking.

    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

    26. What are the various data types in JavaScript?

    JavaScript has various data types categorized into two groups: primitive and non-primitive (reference) types.

    Primitive Data Types

    • Number: Represents both integers and floating-point numbers.
    • String: Represents sequences of characters, enclosed in single, double quotes, or backticks.
    • Boolean: Logical entities with values true or false.
    • Undefined: A declared variable without an assigned value.
    • Null: Represents the intentional absence of value.
    • Symbol: A unique, immutable value used as object property keys.
    • BigInt: Represents integers with arbitrary precision, useful for very large numbers.

    Non-Primitive Data Types

    • Object: Used to store collections of data and more complex entities.
    • Array: An ordered collection of data.
    • Function: Functions are objects and can be defined using declarations or expressions.
    • Date: Represents dates and times.
    • RegExp: Represents regular expressions for pattern matching in strings.
    • Map: A collection of keyed data items, allowing keys of any type.
    • Set: A collection of unique values.

    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

    27. What language constructions do you use for iterating over object properties and array items?

    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:

    Iterating over object

    1. for...in Statement

    Loops over all enumerable properties of an object, including inherited ones.

    for (const property in obj) {
    if (Object.hasOwn(obj, property)) {
    console.log(property);
    }
    }

    2. Object.keys()

    Returns an array of an object's own enumerable property names.

    Object.keys(obj).forEach((property) => console.log(property));

    3. 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}`),
    );

    4. 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),
    );

    Iterating Over Arrays

    1. for Loop

    Traditional loop over array elements.

    for (let i = 0; i < arr.length; i++) {
    console.log(arr[i]);
    }

    2. Array.prototype.forEach()

    Executes a provided function once for each array element.

    arr.forEach((element, index) => console.log(element, index));

    3. for...of Statement

    Loops over iterable objects like arrays.

    for (let element of arr) {
    console.log(element);
    }

    4. 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);
    }

    Explore language constructions used for iterating over object properties and array items on GreatFrontEnd

    28. What are the benefits of using spread syntax and how is it different from rest syntax?

    Spread Syntax

    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 }

    Rest Syntax

    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.
      }

    Explore the benefits of using spread syntax and how it is different from rest syntax on GreatFrontEnd

    29. What is the difference between a Map object and a plain object in JavaScript?

    Map Object

    • Key Types: Allows keys of any type (objects, functions, primitives).
    • Order: Maintains the insertion order of keys.
    • Size: Provides a size property to get the number of key-value pairs.
    • Iteration: Directly iterable, using methods like forEach, keys(), values(), and entries().
    • Performance: Generally better for larger datasets and frequent additions/deletions.

    Plain Object

    • Key Types: Keys are typically strings or symbols. Non-string keys are converted to strings.
    • Order: Insertion order is not guaranteed.
    • Size: No built-in property to get the size. Must manually count the number of keys.
    • Iteration: Not directly iterable. Use methods like Object.keys(), Object.values(), or Object.entries() for iteration.
    • Performance: Faster for small datasets and simple operations.
    // Map
    const map = new Map();
    map.set('key1', 'value1');
    map.set({ key: 'key2' }, 'value2');
    console.log(map.size); // 2
    // Plain Object
    const 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

    30. What are the differences between Map/Set vs WeakMap/WeakSet?

    The main distinctions between Map/Set and WeakMap/WeakSet in JavaScript are as follows:

    • Key types: 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.
    • Memory management: 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.
    • Key enumeration: Keys in 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.
    • Size property: 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.
    • Use cases: 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

    31. Can you offer a use case for the new arrow => function syntax?

    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 syntax
    const 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 syntax
    const 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

    32. Explain the concept of a callback function in asynchronous operations

    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: