
A frontend developer cover letter should be a short proof note. It should connect the role to one or two pieces of evidence, not repeat the resume in paragraph form.
If the same letter could be sent to 50 companies unchanged, it is too generic. The goal is not to sound impressive. The goal is to make the reviewer think, "This person has done something close to what we need."
Keep it short: 150-250 words for a formal cover letter, or 4-6 sentences for an email or referral note.
Do not start by listing your whole frontend stack. Start by finding the clearest requirement in the job description and choosing proof that matches it.
| If the role emphasizes | Your letter should prove |
|---|---|
| React product UI | You have built stateful UI with API data and edge states |
| Design systems | You understand reusable components, tokens, and accessibility |
| Dashboards | You can handle tables, filters, loading states, and charts |
| Web performance | You can name what was slow, what changed, and how you checked it |
| Fresher role | You can finish, deploy, and explain a complete project |
| Remote role | You communicate assumptions, tradeoffs, and progress well |
One good match is enough. A cover letter does not need to prove every requirement in the posting.
Short cover letters are easier to read and harder to fill with generic claims. Use four compact parts.
| Part | Purpose | Example |
|---|---|---|
| Opening | Name the role and fit | I am applying for the frontend developer role focused on dashboard UI and React. |
| Proof | Show one relevant project or shipped feature | My closest matching work is a data table project with URL filters, loading states, and keyboard-safe row actions. |
| Role match | Connect proof to the job description | That maps to your need for API-backed product screens and TypeScript components. |
| Close | Ask for next step | I would be glad to discuss the project and the tradeoffs behind it. |
Here is the base shape:
Hi [Name],I am applying for [role]. The part of the role that matches my work most closelyis [frontend problem from the job description].In [project/team], I built [specific UI or flow], where the main challenge was[state/API/performance/accessibility/design-system constraint]. The work required[skills from the job description] and resulted in [visible outcome, shippedfeature, metric, or reviewable artifact].I would be happy to discuss the project, the tradeoffs, and how the experiencemaps to your team.Best,[Name]
Good frontend proof has three parts:
Weak proof says, "I know React." Better proof says, "I built a React checkout settings flow with controlled forms, disabled submit states, API error handling, and a mobile review step."
Use the best evidence you have:
| Evidence type | Good use in a cover letter |
|---|---|
| Shipped feature | Name the flow, constraint, and product outcome |
| Portfolio project | Link the demo or case study and explain the hard part |
| GitHub repository | Mention the README, setup, tests, or implementation decision worth inspecting |
| Performance work | Name the metric, bottleneck, code change, and verification method |
| Design-system work | Name the component API, accessibility behavior, token work, or adoption problem |
If your proof is weak, improve the artifact before decorating the letter. A better frontend developer portfolio often does more than a longer cover letter.
Freshers should not pretend to have professional impact. Use finished projects and learning discipline as proof.
Hi [Name],I am applying for the frontend developer fresher role. I have been building Reactand JavaScript projects around forms, API states, and responsive layouts ratherthan only static pages.The project most relevant to this role is [project], where I built [specificbehavior], handled [loading/error/empty/validation state], and documented setupin GitHub. I also deployed it so reviewers can test the flow quickly.I am looking for a team where I can contribute to small frontend tasks, learnthrough review, and keep improving my browser, JavaScript, and UI skills.Thank you,[Name]
For fresher roles, the letter should make your work easy to inspect. Link one deployed project or GitHub repo, not five unfinished experiments.
Career switchers should connect prior experience to frontend product work without hiding the transition.
Hi [Name],I am applying for the frontend developer role. My background in [previous field]has given me experience with [domain/product/user/API skill], and I have beenbuilding frontend proof through [project or training path].The closest match to this role is [project], where I built [specific UI behavior]and handled [state/API/layout/accessibility issue]. My previous experience helpsme understand [customer, data, workflow, or collaboration angle] while my recentfrontend work shows the technical direction I am moving toward.I would be glad to discuss the project and how my previous experience can helpthis team.Best,[Name]
This works best when the switch is honest and the frontend proof is visible. Do not spend the whole letter explaining why you changed careers.
Experienced engineers should connect role requirements to shipped scope, not passion statements.
Hi [Name],I am interested in the senior frontend role because it combines [product area]with [frontend challenge from the job description]. In my current work, I owned[flow/system/component], where the main challenge was [constraint].I would bring practical experience in [two relevant skills], especially [specificexample]. I am also careful about quality gates such as accessibility,performance measurement, and test coverage where user flows are high-risk.I would be happy to discuss how I approached [project] and how that experiencemaps to this team.Best,[Name]
For senior roles, avoid a generic stack summary. The useful signal is scope: what you owned, what tradeoff you handled, who depended on the work, and how quality was checked.
Use this when the application flow has no formal cover-letter field, or when you are asking for a referral.
Hi [Name],I am interested in the [role] role because it matches my recent work on [specificfrontend area]. My closest proof is [project/feature], where I built [specific UIbehavior] and handled [constraint].Here are the two most relevant links: [portfolio/project] and [GitHub/case study].I would be glad to share more context if the team is hiring for this type of work.Best,[Name]
This version should feel like a useful note, not a mini essay.
Pick the angle that matches the role. Do not use all of them in one letter.
| Role type | Useful angle | Better proof than "I know React" |
|---|---|---|
| React product role | You can build user flows with changing state and API responses | Search, filters, forms, errors, empty states, and route state |
| Design-system role | You care about reusable UI and adoption | Component API, token migration, docs, keyboard behavior, review notes |
| Performance-heavy role | You measure before claiming improvement | LCP, bundle size, render bottleneck, lazy loading, or caching decision |
| Dashboard role | You handle dense product UI | Tables, charts, sorting, pagination, export, permissions, loading UI |
| Fresher role | You finish and explain work | Deployed project, readable GitHub, clear README, honest scope |
| Remote role | You reduce ambiguity | Written assumptions, PR notes, screenshots, async status updates |
Most weak cover letters are not wrong. They are vague. Rewrite claims until a reviewer can picture the work.
| Weak line | Better line |
|---|---|
| I am passionate about technology. | I built a booking flow with validation, saved progress, disabled submit states, and API error recovery. |
| I know React and TypeScript. | I built a typed table component with URL filters, row actions, loading states, and empty-state handling. |
| I am a quick learner. | I rebuilt a tutorial app into a custom workflow, added form validation, and documented the setup decisions in GitHub. |
| Your company is innovative. | Your role mentions design-system migration; I have worked on typed component APIs and adoption docs. |
| I improved performance. | I reduced unnecessary re-renders in a dashboard list and verified the change with React Profiler before merging. |
The better version does not need to be dramatic. It just needs a concrete behavior, constraint, and proof point.
Tailoring means changing proof order, not sprinkling every keyword from the job description. If a job asks for React, TypeScript, and dashboards, move the dashboard project above the landing page project and write the letter around typed data, filters, and loading states.
Use this quick process:
If the role is too far from your proof, the letter cannot fix that. Use the application as a signal to build a better project or target a closer role first.
Cut anything that could be copied into a marketing intern, backend engineer, or product manager application with no changes.
| Cut | Why it weakens the letter |
|---|---|
| "I am passionate about frontend development" | It says nothing about the role or proof |
| "I am a fast learner" | It asks the reader to trust a trait |
| "Your company is a leader in the industry" | It sounds copied unless tied to the role |
| Long company praise | It wastes space that should show evidence |
| Every tool you have touched | It repeats the resume and dilutes the signal |
| Claims you cannot explain in an interview | They create risk later |
| Links with no context | The reviewer will not know what to open |
The easiest test: ask whether the sentence helps the reviewer answer "why this candidate for this frontend role?" If not, cut it.
If the application requires one, yes. If it is optional, write one only when it adds context the resume cannot show quickly: a career switch, referral, unusually relevant project, relocation context, or a role that matches a specific piece of work.
Aim for 150-250 words. For an email note, 4-6 sentences is enough. If you need more space, the proof is probably unclear or you are repeating the resume.
Only if the certificate explains the path to a stronger project. A finished project with states, validation, API handling, and a clean README is usually more persuasive.
AI can help with structure, but it cannot invent proof. After using any draft, replace generic claims with your actual project, constraint, and link. If the final letter sounds like everyone else's, rewrite it.
Use project proof. Pick the most role-like project you have, then describe the user flow and edge states: loading, errors, validation, responsive behavior, keyboard behavior, API failure, or deployment. For more ideas, use projects that would also strengthen a frontend developer resume for freshers.
Use this when you already have a draft and want the fastest improvement.
A frontend developer cover letter is useful when it adds context the resume cannot. If it does not point to proof, shorten it until it does.

A frontend developer resume for freshers should prove one thing quickly: you can build, explain, and finish frontend work. Keep it to one page, lead with projects, use honest skills, and make GitHub, live demos, portfolio, and contact links easy to inspect.
The resume is not where you hide no experience behind extra words. It is where you turn projects, internships, college work, open-source fixes, and practice into evidence a recruiter or engineer can verify.
If you want the broader all-level version, read How to Write a Frontend Developer Resume. For GFE's interview-focused resume guidance, use the Front End Interview Playbook resume chapter. This guide stays on the fresher problem: what to write when projects are your main proof.
A fresher resume is judged on risk. The reviewer is asking whether you can be trusted with small frontend tasks without constant rescue.
| Reviewer question | Your resume should answer with |
|---|---|
| Can this person build a UI flow? | Two or three projects with live links, GitHub links, and clear bullets |
| Can they handle product states? | Loading, empty, error, invalid, success, and responsive behavior |
| Can they explain their own code? | Bullets that name decisions, constraints, and tradeoffs |
| Are the basics real? | Skills tied to projects, not a long tool dump |
| Will links waste my time? | Working portfolio, GitHub, LinkedIn, and deployed demos |
This is the same shortlisting logic used in the general frontend developer resume guide, but freshers need a different proof mix because projects usually carry more weight than jobs.
Freshers should usually place projects above education unless an internship, degree project, or college achievement is unusually relevant. The hiring team needs frontend behavior before it needs a biography.
| Section | What to include | What to avoid |
|---|---|---|
| Header | Name, city, email, phone, GitHub, LinkedIn, portfolio | Full address, multiple phone numbers, broken links |
| Headline | One line naming frontend lane and strongest proof | Generic career objective |
| Skills | Grouped skills you can explain | Every library you touched once |
| Projects | 2-3 deployed projects with bullets | Tutorial titles with no changes |
| Experience | Internship, freelance, open source, or club work | Inflated labels for tiny tasks |
| Education | Degree, college, dates, useful coursework | School details that push stronger projects down |
The Front End Interview Playbook resume chapter is useful because it is written for frontend candidates, not generic software resumes. For freshers, translate its advice into these checks.
| Playbook idea | Fresher resume version |
|---|---|
| Keep it short | One page is enough unless you have unusually relevant internships or shipped work |
| Use ATS-friendly formatting | Build in Google Docs, Word, Pages, or LaTeX; avoid design-tool resumes for applications |
| Keep the layout readable | Use a single column, common fonts, and readable font size |
| Show scale and complexity | For projects, mention data size, states handled, pages, flows, or performance checks |
| Show impact honestly | Use outcomes you can defend: faster load, fewer broken states, clearer README, demo |
| Curate the skills section | List the core tools you can explain instead of every library you have touched |
| Make projects inspectable | Add GitHub code, screenshots, README notes, and live links where possible |
Freshers will not always have business metrics like monthly active users or revenue impact. That is fine. Use project-level evidence instead: number of screens, edge states handled, API behavior, accessibility checks, performance notes, or before/after improvements.
No experience does not mean no evidence. It means you must label evidence honestly.
| Evidence you have | How to list it | What makes it weak |
|---|---|---|
| Personal project | Project section with Live and GitHub links | No README, no demo, only copied tutorial behavior |
| College capstone | Project section if you owned frontend work | Team project with unclear personal contribution |
| Internship | Experience section with assigned UI work and shipped changes | Vague "worked on website" bullets |
| Freelance or club website | Experience or project section with scope and constraints | Overstating it as full-time product engineering |
| Open-source contribution | Project or additional section with issue, PR, docs, or fix scope | Forked repo with no accepted change or explanation |
| Certification | Additional section only when it supports a project | Pushing projects down to list course names |
If your projects are still weak, improve the projects before polishing the resume. Use Frontend Project Ideas for Your Resume to choose project scopes that create better bullets.
Use a readable single-column layout when applying through job portals. Fancy columns and skill bars often make the resume harder to scan and copy into applicant systems.
NameCity | email | phone | GitHub | LinkedIn | PortfolioFrontend developer fresher building React and JavaScript projects with forms,API-backed views, responsive layouts, and clean GitHub documentation.SkillsFrontend: HTML, CSS, JavaScript, React, TypeScript basicsUI behavior: forms, validation, responsive layout, loading/error/empty statesTools: Git, GitHub, Vite, npm, Chrome DevToolsProjectsJob Tracker | Live | GitHub- Built a React job tracker with URL filters, saved applications, empty states,and responsive table layout using TypeScript and local persistence.- Added form validation, edit/delete flow, and a README with setup steps,screenshots, known limitations, and next improvements.Product Catalog | Live | GitHub- Built API-backed product browsing with search, category filters, pagination,loading state, error recovery, and mobile card layout.- Documented the data shape, retry behavior, and one accessibility improvementfor form labels and keyboard navigation.ExperienceFrontend Intern, Company or Club, Month Year - Month Year- Updated responsive UI sections and fixed form validation issues after review.- Wrote basic test notes for mobile, empty state, and failed API responses.EducationDegree, college, expected or completed yearRelevant coursework: web development, data structures, databasesAdditionalHackathon, open-source docs fix, certification, or volunteer site only if it adds proof.
A project bullet should tell the reviewer what you built, what behavior it handles, and why it goes beyond a static page. Do not write only "Built using React." The stack matters less than the behavior.
Formula:Built [project or flow] with [specific UI behavior], handling [state, error,accessibility, performance, routing, or responsive detail].Example:Built a React job tracker with URL filters, saved applications, empty states,and responsive table layout using TypeScript and local persistence.
| Weak bullet | Better bullet |
|---|---|
| Built React project | Built a React expense tracker with controlled forms, category filters, local persistence, empty states, and mobile layout |
| Made portfolio website | Built a portfolio with project case studies, deployed demos, GitHub links, responsive navigation, and contact validation |
| Used API | Built a search page using a public API with loading, empty, error, retry, and URL-based filter state |
| Created e-commerce website | Built product listing with responsive cards, filters, cart state, empty cart, unavailable items, and checkout review |
| Made todo app | Built task manager with filters, edit/delete flow, local persistence, and keyboard-friendly form controls |
The best bullet is the one you can defend in an interview. If the interviewer asks how retry worked, where state lived, or how the mobile layout changed, you should be able to answer from your own code.
Freshers often list too many small projects. Two complete projects beat five unfinished demos.
| Project type | List it when it has | Improve before listing when |
|---|---|---|
| Weather app | Search, loading, error, saved locations, mobile layout | It only calls an API and displays temperature |
| Job tracker | Forms, filters, persistence, empty states, responsive table | It is only a static card grid |
| E-commerce UI | Product states, cart state, checkout validation, mobile summary | It is only copied product cards |
| Portfolio | Case studies, demo links, GitHub links, contact path | It is only an about page and decorative screenshots |
| Component library | Button, Input, Modal, Tabs, Toast with states and notes | It is only unrelated components in one folder |
| Performance rewrite | Measurement, bottleneck, before/after notes | It says "optimized" without explaining what changed |
For a deeper project selection process, use Frontend Project Ideas for Your Resume. It has project scoring, clone upgrades, and project directions that create stronger resume bullets.
A fresher skill section should not read like a tool dump. Group skills by confidence and keep interview risk in mind: anything you list can become a question.
| Skill group | Good entry | Risky entry |
|---|---|---|
| Core web | HTML, CSS, JavaScript, responsive layout, forms | HTML5, CSS3, JavaScript, Bootstrap, Tailwind, Sass, Less, jQuery all together |
| Framework | React: components, props, state, effects, forms | React expert |
| Tools | Git, GitHub, Vite, npm, Chrome DevTools | Docker, AWS, GraphQL if you cannot explain them |
| Testing | Basic unit tests or manual test notes | Testing expert with no test files |
Do not list a tool because it appeared in one tutorial. If the resume says TypeScript, there should be at least one project where TypeScript helped with props, API data, forms, or state.
The resume creates the click. GitHub and the live demo decide whether that click builds trust. Each listed project should have a working demo, readable README, screenshots, setup commands, and no exposed keys.
GitHub's repository README docs are a useful baseline: a README should explain what the project does, why it is useful, and how someone can get started. For hiring, add screenshots, states handled, tradeoffs, and known limitations.
Use Frontend Developer GitHub Profile to clean up pinned repositories, profile README, project READMEs, and demo links. If you do not have a portfolio yet, use How to Build a Frontend Developer Portfolio With No Experience so your resume and portfolio point to the same proof.
Tailoring means changing proof order, not sprinkling every keyword from the job description. If a job asks for React, TypeScript, and dashboards, move the dashboard project above the landing page project and rewrite bullets around typed data, filters, loading states, and table behavior.
Use Frontend Developer Jobs for Freshers to plan the full job-search path: projects, GitHub, applications, interview prep, and feedback loops.
The fastest way to improve a fresher resume is usually rewriting one vague project section.
BeforeProjectsFood Delivery App- Created food delivery website using React.- Used CSS and JavaScript.- Added cart page.AfterProjectsFood Delivery App | Live | GitHub- Built restaurant listing, menu detail, cart state, empty cart, unavailable item,and checkout review screens using React and local persistence.- Added responsive layout for mobile ordering, form validation for deliverydetails, and README notes on state ownership and known limitations.
The second version gives an interviewer something to ask. They can ask how cart state worked, what happened when the cart was empty, how validation appeared, and how mobile layout changed. That is the point.
Before sending the resume, read it like an engineer who has never met you. Every major claim should be backed by a project, shipped feature, or link.
| Resume claim | Reviewer question | Better evidence |
|---|---|---|
| React experience | Where can I see stateful React work? | Link a project with forms, API states, and clear component boundaries |
| Performance work | What was slow and how did you know? | Name the measured bottleneck and the change made |
| Accessibility | Which interaction did you make accessible? | Mention labels, focus recovery, keyboard behavior, or error announcements |
| Team ownership | What did you own personally? | Separate your contribution from team output |
Remove lines that create doubt but do not add proof. Space is expensive on a one-page fresher resume.
| Remove | Why it weakens the resume | Better replacement |
|---|---|---|
| Generic objective | Says motivation without proof | One-line headline with role lane and project proof |
| Skill bars | Looks precise but usually means nothing | Grouped skills tied to projects |
| "React expert" | Creates interview risk for a fresher | React: components, props, state, effects, forms |
| Course list above projects | Pushes visible work down | One relevant coursework line under education |
| Copied tutorial project titles | Makes the project look unoriginal | Rename by user problem and describe added behavior |
| Too many certificates | Crowds out inspectable work | Keep only the one that supports a project or skill |
| Broken or private links | Blocks verification | Public demo, GitHub repo, screenshots, or case note |
Every bullet should survive a follow-up. If you write "improved performance," be ready to explain what was measured, which code changed, what tradeoff you accepted, and how you checked the result. If you cannot answer those questions, rewrite the bullet to match what you actually did.
Use this test:
If you cannot answer, do not delete the project immediately. Rewrite the bullet to match the truth, then improve the project after sending a small application batch.
Your resume should not live alone. A good fresher application packet has the same story across resume, GitHub, portfolio, LinkedIn, and optional cover letter.
| Asset | What it should do | GFE guide |
|---|---|---|
| Resume | Show the best one-page proof | How to Write a Frontend Developer Resume |
| Projects | Create bullets that survive follow-ups | Frontend Project Ideas for Your Resume |
| GitHub | Make code, demos, READMEs, and pinned repos easy to inspect | Frontend Developer GitHub Profile |
| Portfolio | Turn projects into case studies and a clear contact path | Frontend Developer Portfolio |
| Match headline, projects, and job target to the resume | Frontend Developer LinkedIn Profile | |
| Cover letter | Add context only when it explains fit better than the resume | Frontend Developer Cover Letter |
If you already have several years of experience, this fresher structure will undersell you. Use Senior Frontend Developer Resume to emphasize scope, judgment, architecture, mentoring, and measurable delivery.
Before sending the resume, open it in a private browser window or PDF viewer and run this check.
Use this when the current resume feels weak and you need progress today.
Hand the resume to someone for 90 seconds. Ask three questions:
If they cannot answer, the resume is not clear enough yet. A fresher resume wins when it makes small proof easy to inspect: finished projects, honest skills, working links, and bullets that describe behavior instead of only tools.

A frontend developer GitHub profile should make your work easy to inspect. In the first minute, a recruiter should understand your target role, and an engineer should know which repository proves your frontend judgment.
That means your profile is not a badge wall, a tutorial archive, or a second resume. It is a review path: role, best work, working demos, readable code, and a clear contact link.
GitHub's profile docs list the profile README, personal info, contribution activity, and pinned items as key profile elements. Use those parts deliberately. Put the strongest proof where people can see it without opening ten tabs.
Before rewriting anything, decide what a reviewer should do next after landing on your profile.
| Reviewer question | Your profile should answer with |
|---|---|
| What role is this person seeking? | One plain sentence in the bio or profile README |
| Which work should I open first? | One to three pinned repositories that match the target role |
| Can I test the UI? | Live demos, screenshots, and clear setup instructions |
| Can I trust the code? | Clean folders, recent fixes, no secrets, and readable project notes |
| How do I contact them? | Portfolio, LinkedIn, email, or another obvious contact path |
If the first screen does not answer these questions, adding more content usually creates more work for the reviewer.
GitHub profile README docs explain the setup requirement: create a public repository with the same name as your username and add a README.md at the root. The content should be short enough to scan and specific enough to direct attention.
Use this structure:
# [Name]Frontend developer focused on React, TypeScript, accessible UI states, and API-backed product screens.## Best work- [Job tracker](repo): filters, saved jobs, empty states, mobile table, deployed demo- [Checkout flow](repo): form validation, error recovery, review step, keyboard flow- [Component system](repo): button, modal, tabs, docs, accessibility notes## Current proof- Practicing performance measurement with Lighthouse and Web Vitals- Improving tests for forms, async states, and keyboard interactions## LinksPortfolio: ... LinkedIn: ... Email: ...
Keep badge rows, stats cards, contribution snake animations, and long tech-icon grids out of the top section unless they help the reviewer choose what to open. A frontend profile should feel like a useful index, not a sticker board.
GitHub lets you pin up to six repositories or gists. Do not use all six just because the slots exist. Use pins to answer different questions about your frontend ability.
| Pin type | What it proves | Better than |
|---|---|---|
| Form-heavy app | Validation, accessibility, state, error copy | A login page clone |
| API-backed dashboard | Loading, error, empty, pagination, filtering states | A static chart screenshot |
| Component project | Props, variants, keyboard behavior, documentation | A folder of unrelated UI snippets |
| Performance case study | Measurement, tradeoffs, before/after notes | "Optimized performance" with no numbers |
| Open-source contribution | Issue context, review discussion, tests or docs | A fork with no explanation |
| Product flow | Routing, persistence, edge cases, responsive layout | A landing page with only visual polish |
For a fresher, three complete projects are better than six half-finished repos. For a senior engineer, one clear architecture note or migration write-up can be more useful than another demo app.
GitHub's repository README docs say a README should explain what the project does, why it is useful, how to get started, and where people can get help. For hiring, translate that into a project review checklist.
Every pinned frontend repo should include:
Use this README skeleton for portfolio projects:
# Project nameShort description of the user problem and finished flow.## DemoLive: ... Screenshots: ...## What it includes- Authentication or saved state- Loading, empty, and error states- Responsive layout notes- Keyboard and accessibility behavior## Tech choices- React + TypeScript for UI and state- [Tool] because ...## Run locallypnpm install pnpm dev## Notes- Tradeoff made:- Known limitation:- Next improvement:
The "Notes" section is often where a frontend candidate separates copied code from personal judgment. Explain why a form validates on blur, how retry works after an API error, why a table collapses on mobile, or what you changed after measuring performance.
Tool lists are weak without behavior. A reviewer does not learn much from "React, Tailwind, Firebase." They learn from what the app handles.
| Claim on profile | Weak proof | Better proof |
|---|---|---|
| "I know React" | Todo app with no edge states | State-heavy flow with derived state and tests |
| "I build responsive UIs" | Desktop screenshot only | Mobile screenshots and layout notes |
| "I care about accessibility" | Accessibility badge | Keyboard behavior, labels, focus states, contrast fix |
| "I work with APIs" | Fetch call in one component | Loading, empty, error, retry, pagination states |
| "I write maintainable code" | Many folders | Clear component boundaries and short decision notes |
This is also how your GitHub should match your resume. If the resume says "built accessible forms," the pinned repo should make labels, errors, focus behavior, and validation choices visible.
Production frontend work is full of handoffs: design feedback, backend contracts, QA reports, accessibility review, and product changes. Your profile can show that without pretending a solo project is company work.
Useful signals include:
docs/decisions.md note for bigger choices.Do not manufacture noise. One clear PR or issue is enough if it helps someone understand how you think.
GitHub's accessibility tips for profile pages are especially relevant for frontend developers because the profile itself becomes a tiny sample of your UI judgment.
Apply the same discipline to your README:
An accessible profile is not only nicer to read. It also supports the claim that you care about real users.
Most GitHub profile fixes are subtraction. Remove the things that make a reviewer work harder.
| Weak signal | Better replacement |
|---|---|
| Six tutorial clones pinned | Two or three complete projects with demos and READMEs |
| Broken portfolio or demo links | Fixed links, or remove the link until it works |
| Private repo as the only proof | Public case study, screenshots, PR, or write-up |
| Generic bio | Target role plus strongest frontend proof |
| README copied from a starter | Your own summary, run commands, states handled, and limitations |
| Large skill grid with no examples | Three selected projects mapped to concrete frontend behavior |
| Old repo at the top for no reason | Reordered pins based on target role and proof strength |
Also check repository descriptions and topics. GitHub topics help classify a repo, but use them sparingly: react, typescript, accessibility, dashboard, or forms is useful; twenty vague topics is not.
The same GitHub profile area can support different levels, but the proof should change.
| Level | Best GitHub proof | Avoid |
|---|---|---|
| Fresher | Complete projects, clean READMEs, live demos | Claiming every tool in the ecosystem |
| Junior developer | Finished flows, fixes, tests, accessibility notes | Only visual clones |
| Mid-level | API states, component structure, debugging notes | Hiding tradeoffs behind vague descriptions |
| Senior developer | Architecture notes, migrations, review examples, scope | Treating GitHub like a beginner portfolio |
If your best work is private, do not fake it. Create a public write-up that explains the shape of the problem without sharing confidential code: constraints, role, decision, tradeoff, and result.
Open your GitHub profile while logged out or in a private browser window. Give yourself 90 seconds.
Check whether the visible profile answers:
If the answer is unclear, fix the first broken link in the chain before adding anything new.
Use one focused hour:
A good frontend developer GitHub profile reduces uncertainty. Make the best work obvious, runnable, and easy to discuss.

A frontend developer LinkedIn profile should make the next click obvious. In the first minute, a recruiter should understand the role you fit, and a hiring manager or engineer should know where to inspect proof of your frontend work.
That proof can be a portfolio, GitHub profile, shipped product, case study, code sample, open-source contribution, or a clear project write-up. LinkedIn is the route into that evidence. It is not a place to hide weak proof behind a long list of tools.
LinkedIn's own profile guidance says a complete, updated profile can improve discoverability and help recruiters understand your professional experience. For frontend developers, "complete" is not the same as "long." The best profile is specific, searchable, and easy to verify.
Before editing the headline or About section, decide what the reader should believe after 60 seconds.
| Reviewer question | Your profile should answer with |
|---|---|
| What frontend role do you fit? | A clear lane: React product UI, design systems, dashboards, web apps |
| What proof should I open first? | One featured portfolio, GitHub repo, case study, or shipped project |
| What frontend problems can you handle? | UI states, forms, accessibility, APIs, performance, testing, or CSS |
| Does the story match your resume? | Same role target, project names, dates, links, and strongest claims |
| Can someone contact or refer you quickly? | Clean public URL, current location or remote preference, contact path |
If your profile does not answer these questions, more badges, certificates, and activity posts will not fix the problem. The first job is to make the proof easy to find.
Most weak LinkedIn profiles try to sound available for everything. That makes the profile harder to search and harder to trust.
Choose one primary lane for the next version of the profile:
| Target lane | Keywords that belong | Proof that should be visible |
|---|---|---|
| Frontend developer fresher | JavaScript, React, HTML, CSS, projects | Complete projects, live demos, clean READMEs, basic UI states |
| React product engineer | React, TypeScript, forms, APIs, testing | Product flows, loading/error states, validation, API integration |
| Design systems frontend | TypeScript, components, accessibility | Reusable components, docs, variants, keyboard behavior |
| Dashboard or SaaS frontend | Tables, filters, charts, state | Data views, pagination, empty states, permissions, responsive UI |
| Web performance frontend | Core Web Vitals, JavaScript, CSS | Measured improvements, bundle work, image/loading decisions |
| Senior frontend engineer | Architecture, mentoring, migration | Design docs, migrations, review examples, cross-team decisions |
Use the language from job descriptions you actually want, but do not stuff every tool into the profile. A recruiter may search by keyword; an engineer will still click through and check whether the work supports the claim.
LinkedIn explains that the headline appears below your name and can be shown in search results. Do not waste it on "open to work" alone. Use it to name the role, stack, and evidence.
| Level | Weak headline | Better headline |
|---|---|---|
| Fresher | Frontend developer fresher | Frontend developer fresher - React, JavaScript - Portfolio with form and dashboard projects |
| Junior | React developer | Frontend developer - React + TypeScript - API-backed UI states and responsive layouts |
| Mid-level | Software engineer at Company | Frontend engineer - React dashboards - Forms, tables, performance, accessibility |
| Senior | Senior frontend developer | Senior frontend engineer - Design systems - TypeScript components and accessibility |
| Switching | Backend developer learning frontend | Backend-to-frontend engineer - React, TypeScript - Full-stack product flows |
| Freelance | Freelance frontend developer | Freelance frontend developer - Landing pages, React apps - Fast handoff from design to deploy |
Keep the headline readable. Separators are fine, but the headline should still work as a sentence when someone sees it in search.
LinkedIn describes the About section as the place to express mission, motivation, and skills. For a frontend job search, use it as a short map: what you build, what proof exists, and what role you want.
Use this structure:
I build [type of frontend work] with [primary stack].My strongest proof is [project/work], where I handled [frontend behavior or constraint].Selected links:- Portfolio: [link]- GitHub: [link]- Best project or case study: [link]I am looking for [target role, location/remote preference if relevant].
A fresher version:
I am a frontend developer focused on JavaScript, React, CSS, and accessible UI basics.My best projects are a job tracker and a checkout flow. They include form validation, loading and error states, responsive layouts, and clear README notes.Selected links:- Portfolio: [link]- GitHub: [link]- Best project: [link]I am looking for frontend developer or React developer roles where I can work on product UI and keep improving through code review.
A mid-level version:
I build React and TypeScript product interfaces for data-heavy screens, forms, and API-backed workflows.Recent work includes improving table filtering, reducing duplicate form logic, and fixing accessibility regressions in modal and keyboard flows.Selected links:- Portfolio or case study: [link]- GitHub or technical notes: [link]- Resume: [link]I am interested in frontend engineer roles on product teams that care about UI quality, maintainability, and measurable user-facing behavior.
A senior version:
I lead frontend work across design systems, TypeScript architecture, and product UI quality.My strongest work is in component contracts, accessibility, migration planning, performance budgets, and helping teams ship consistent interfaces without blocking product delivery.Selected proof:- Design system or migration case study: [link]- Technical writing or architecture note: [link]- Portfolio or resume: [link]I am interested in senior frontend or frontend platform roles with ownership over architecture, review quality, and team execution.
Cut anything that does not help the reader decide what to open next. A life story can be useful when it explains your career direction; it becomes noise when it hides the proof.
LinkedIn's Featured section supports work samples such as posts, articles, external links, and uploaded media. Use it to shorten the path from a profile claim to the work that supports it.
Feature one to four items, ordered by proof strength:
| If you have | Feature this first | Description to write |
|---|---|---|
| Portfolio | Portfolio home or selected work page | "Selected React and TypeScript projects with live demos" |
| Best GitHub project | Repo or profile with a polished README | "Job tracker with filters, empty states, and saved jobs" |
| Shipped work | Product page, case study, or public write-up | "Checkout settings UI: validation, API errors, QA fixes" |
| Technical writing | Post about a frontend decision or debugging story | "Why I changed table state management after profiling" |
| Open-source contribution | PR, issue, package, or docs contribution | "Accessibility fix for keyboard navigation in tabs" |
| Resume | Resume PDF only if it is current and matches the profile | "Frontend resume with React, TypeScript, and UI projects" |
Do not use Featured as a certificate carousel unless the certificate directly supports the target role. A recruiter may appreciate credentials, but an engineer needs to see work.
Experience bullets should make your frontend contribution inspectable. "Built UI" is too vague. Say what the UI had to handle.
| Weak bullet | Better LinkedIn bullet |
|---|---|
| Worked on frontend screens | Built checkout settings screens with validation, disabled states, and API error recovery |
| Used React and TypeScript | Typed shared form components and removed duplicate validation behavior across 4 flows |
| Fixed bugs | Resolved focus and keyboard regressions in modal flows after QA reports |
| Improved performance | Reduced unnecessary table rerenders by memoizing row state and measuring interaction lag |
| Made pages responsive | Reworked dashboard filters for mobile, tablet, and desktop without hiding key actions |
| Worked with backend APIs | Added loading, empty, error, retry, and pagination states for search results |
If you can use numbers honestly, use them. If you cannot, name the constraint: browser behavior, accessibility, API state, design handoff, QA regression, bundle size, permissions, or mobile layout.
For confidential work, describe the shape without exposing private details:
Built an internal admin workflow for reviewing user-submitted records. Owned the React UI, API state handling, table filters, empty states, and QA fixes across desktop and tablet layouts.
That is more useful than naming every library.
LinkedIn allows up to 100 skills and says relevant skills help match you with opportunities. Do not treat that as permission to list every framework you have touched once.
Choose skills in three groups:
| Skill group | Examples | What should prove it |
|---|---|---|
| Core frontend | JavaScript, TypeScript, React, HTML, CSS | Projects, experience bullets, GitHub repos, portfolio |
| Role-specific | Accessibility, performance, testing, forms | Case studies, bullets, PRs, docs, measurable changes |
| Collaboration context | Code review, design systems, API integration | Experience descriptions, recommendations, project notes |
Move the most relevant skills higher and remove weak noise. If the profile says React, TypeScript, Next.js, Vue, Angular, Svelte, Node, AWS, Figma, Webflow, Shopify, Kubernetes, and Python with no matching proof, the reader cannot tell what you actually do well.
LinkedIn recommendations can be requested from connections and include a personalized message. Use them selectively. One concrete recommendation from a teammate, manager, mentor, client, or open-source maintainer can carry more trust than ten generic endorsements.
Ask for recommendations around a specific work trait:
Hi [Name], I am updating my LinkedIn profile for frontend roles. If you are comfortable writing a short recommendation, could you mention the [project/flow] we worked on and what you saw from me around [UI quality, debugging, code review, accessibility, reliability, handoff, or communication]?No pressure if now is not a good time.
Good recommendations answer one of these questions:
Your resume, portfolio, GitHub, and LinkedIn should tell the same story at different levels of detail.
| Item to compare | What should match |
|---|---|
| Target role | Same frontend lane across headline, resume summary, and portfolio |
| Project names | Same names, links, and descriptions |
| Dates and titles | Same employment timeline and role wording |
| Skills | Skills on LinkedIn should appear in projects or work bullets |
| Strongest claim | If the resume says accessibility, the profile should show the evidence |
| Contact path | Portfolio, resume, GitHub, and LinkedIn should not send people in loops |
If your frontend developer portfolio shows product UI projects but LinkedIn talks mostly about certificates, the reviewer gets a weaker story. If your frontend developer GitHub profile has excellent READMEs but LinkedIn does not feature them, the best proof is buried.
Posting on LinkedIn can help, but you do not need to become a daily creator to have a good frontend profile. Use activity when it adds proof.
Good frontend activity:
Weak activity:
The profile should still work when someone ignores your feed. Activity is extra evidence, not the foundation.
LinkedIn lets you customize your public profile URL. Use a clean URL if available, then make the rest of the public details easy to trust.
Check these details:
| Detail | Good version | Problem version |
|---|---|---|
| Public URL | linkedin.com/in/first-last or a close professional form | Random numbers when a clean URL is available |
| Location | Current city, region, or remote preference | Stale location that does not match job search |
| Contact info | Portfolio, GitHub, email, or contact page | No way to continue the conversation |
| Open to work setting | Matches the role and location you actually want | Broad settings that invite irrelevant roles |
| Profile photo | Clear, current, professional enough for hiring context | Cropped group photo or no photo |
| Banner | Simple and non-distracting | Fake code, unreadable text, or visual clutter |
Then view the profile while logged out or in a private window. This catches broken links, hidden sections, stale banners, and visibility settings that you miss while editing.
The same profile sections can support different levels, but the proof should change.
| Level | What matters most | Avoid |
|---|---|---|
| Fresher | Complete projects, working links, honest basics | Claiming too many tools with no finished work |
| Junior developer | Finished flows, bug fixes, tests, UI-state handling | Only visual clones or tutorial repos |
| Mid-level | Product constraints, API states, ownership, quality | Vague responsibility bullets |
| Senior engineer | Scope, tradeoffs, migrations, mentoring, review work | A profile that reads like a beginner project list |
| Career switcher | Transferable context plus current frontend proof | Hiding the previous career or overclaiming level |
Freshers should make work inspectable. Senior engineers should make judgment inspectable. Both need proof; the difference is what kind of proof earns trust.
Open your profile in a private browser window and set a timer for 90 seconds.
Answer these questions without clicking more than three links:
If the answer is unclear, fix the top section before editing lower sections.
| Mistake | Why it hurts | Better fix |
|---|---|---|
| "Frontend developer" with no proof | The reader has to guess what you can build | Add Featured links to portfolio, GitHub, or a case study |
| Every tool listed as a skill | The strongest skill gets lost | Keep skills tied to target roles and visible evidence |
| Tutorial clones at the top | Looks unfinished even if newer work is better | Feature complete projects with demos and README notes |
| About section written like a cover letter | Too much story before proof | Use a short proof map with selected links |
| Experience copied from resume | Misses the space LinkedIn gives for context | Add frontend constraints, collaboration, and product impact |
| Broken demo or old portfolio link | Creates doubt fast | Fix it or remove it until it works |
| Activity feed louder than profile | Hiring proof gets buried under posts | Keep profile sections clean and use posts as extra proof |
Most profile improvements are subtraction: fewer weak claims, fewer stale links, fewer unrelated skills, and a clearer first click.
Use this when the profile feels messy and you do not want to rebuild everything.
Stop there. One clean top section is better than a half-edited profile from top to bottom.
Use this worksheet before editing.
| Question | Your answer should include | Weak answer to avoid |
|---|---|---|
| What role should the profile target? | One frontend lane and level | Any frontend job |
| What should a reviewer click first? | The single strongest project, repo, portfolio page, or case study | A long list of links |
| What frontend skill is visible? | Forms, API states, accessibility, performance, routing, testing, or component design | Modern UI |
| What stale item should be removed? | Broken demo, abandoned tutorial, outdated title, or noisy pinned repo | Nothing |
| What should match the resume? | Role target, project names, dates, links, and strongest claims | Different stories on each platform |
| What would an interviewer ask next? | A question about the proof you just made visible | I do not know |

Frontend developer side project ideas are useful only when they become proof. A good side project is not a random app name. It is a small product workflow with a user, constraints, messy states, and decisions you can explain.
This guide gives you sharper project ideas and a way to scope them so the finished work is reviewable, not another half-built clone.
The common mistake is choosing a familiar noun: dashboard, clone, weather app, tracker, e-commerce site. Those can be useful for practice, but the name does not tell a reviewer what you can do.
Before picking an idea, run it through this filter.
| Question | Good answer | Weak answer |
|---|---|---|
| Who is the user? | A specific person doing a specific task | "Everyone" |
| What state changes? | Status, filters, ownership, edits, history, permissions, files | Static cards |
| What can go wrong? | Empty data, invalid input, slow API, expired session, conflict | Happy path only |
| What can be inspected? | Live demo, README, state table, API shape, accessibility notes | Screenshot gallery |
| What makes it yours? | A custom constraint, workflow, dataset, or tradeoff | Same requirements as the tutorial |
| Can v1 ship in about two weeks? | One complete flow with proof states | A platform with no finish line |
If an idea fails this test, do not throw it away immediately. Narrow it until the user, flow, and constraints are clear.
Most public project lists repeat calculators, weather apps, carts, dashboards, and clones. Those are fine learning exercises. For a side project you want to show, pick a workflow with decisions.
| Project idea | Build this v1 | Add one constraint that makes it yours | Best for proving |
|---|---|---|---|
| Support triage inbox | Ticket list, detail view, priority, assignee, status, saved replies, empty/error states | Add keyboard shortcuts or optimistic updates with failed-save recovery | Dense UI, async state, product judgment |
| Job search command center | Saved jobs, pipeline columns, follow-up reminders, contacts, notes, CSV import/export | Add interview prep notes that change by company, role, or application stage | Personal workflow, forms, filtering, persistence |
| Appointment booking with time zones | Service selection, availability grid, timezone display, guest details, confirmation flow | Add reschedule/cancel states and explain how disabled times are calculated | Dates, validation, multi-step state |
| E-commerce returns portal | Order lookup, eligible items, reason selection, refund estimate, shipping label step | Add business rules for final sale, expired return windows, and partial returns | Branching flows, unhappy paths, state modeling |
| Subscription audit dashboard | Manual entries, renewal calendar, cost summary, categories, reminders, mobile view | Add price-change history or "cancel by" tasks with date-based urgency | Data modeling, charts, dates, empty states |
| Transaction review tool | CSV upload, editable table, categories, split transaction, undo, spending summary | Add rule suggestions and a review queue before categories are applied | File handling, table UX, derived state, performance |
| Feature flag admin console | Flag list, environment switcher, rollout percentage, audit log, permission states | Add guarded editing so risky changes require review or confirmation | Internal tools, permissions, forms, state ownership |
| Form builder with conditional logic | Field editor, preview mode, required rules, conditional sections, validation summary | Add a JSON schema view so reviewers can inspect the data model | Nested state, accessibility, validation, component design |
| Component behavior lab | Modal, tabs, combobox, toast, field errors, usage examples, keyboard checklist | Add a state matrix for focus, disabled, error, loading, and mobile behavior | Design-system roles, component APIs, accessibility |
| Design QA review board | Screenshot upload, checklist, annotation pins, severity, breakpoint notes | Add comparison notes for desktop, tablet, and mobile with one fixed-bug example | Layout judgment, review process, communication |
| Local-first research library | Save links, notes, quotes, tags, reading status, local persistence, import/export | Add offline recovery and a broken-link state | Browser storage, search UX, data recovery |
| AI answer review queue | Prompt input, candidate answers, diff view, approve/reject, feedback tags, retry state | Add confidence labels, source checks, and an audit history for rejected answers | Human review UX, trust, error handling, decision history |
The best idea is not the biggest one. It is the one where you can explain the user, the constraint, the state model, the edge case, and the tradeoff without sounding like you followed a tutorial.
Use the table below if you are choosing between several ideas.
| If you want to prove... | Choose one of these ideas | Include evidence of... |
|---|---|---|
| Product UI depth | Support triage inbox, returns portal, booking flow | Empty/error states, branching rules, mobile decisions |
| Data-heavy frontend | Transaction review tool, subscription dashboard, research library | Sorting, filtering, import/export, derived state, performance |
| Forms and validation | Form builder, booking flow, feature flag console | Validation rules, disabled states, review step, server error |
| Accessibility | Component behavior lab, form builder, support inbox | Keyboard paths, labels, focus order, visible focus |
| Internal tools | Feature flag console, design QA board, transaction review tool | Permissions, audit log, bulk actions, undo |
| AI product thinking | AI answer review queue | Review workflow, trust signals, retry, rejection history |
Do not build the biggest version first. A complete v1 is more useful than a half-built platform.
| Version | Goal | Example: support inbox project |
|---|---|---|
| v1 | Main user flow works | List tickets, filter by status, open a ticket, change priority, handle no data |
| v2 | Quality becomes visible | Keyboard shortcuts, optimistic status updates, loading/error states, mobile view |
| v3 | Advanced proof becomes clear | Saved replies, assignment rules, activity history, before/after performance note |
Stop after v2 if the project already proves the skill you need. Add v3 only when it creates a better story for your target role.
Most weak side projects fail because every demo assumes good data, fast network, valid input, and a desktop screen. Add these states first.
| State | What to implement | What it tells a reviewer |
|---|---|---|
| Loading | Skeleton, spinner, pending button, or optimistic row | You understand async waiting |
| Empty | Useful empty copy and next action | You can design recovery, not only success |
| Error | Retry path, inline field error, or failed-save message | You know what happens when the API fails |
| Invalid | Validation, disabled submit, clear messages | You can protect user input |
| Unauthorized | Signed-out, permission, or expired-session view | You understand access boundaries |
| Slow or large | Debounce, pagination, virtualization, or measurement | You can prevent obvious performance problems |
| Mobile | Usable layout, reachable controls, no clipped text | You can build beyond desktop screenshots |
| Keyboard focus | Logical tab order, focus contained in modal dialogs, visible focus | You can support non-mouse use |
For accessibility-heavy projects, compare your modal, tabs, menu, and combobox behavior against the WAI-ARIA Authoring Practices Guide. For performance-heavy projects, use web.dev's Web Vitals guide to pick measurable signals instead of vague claims.
A reviewer should understand the project without cloning it first. GitHub's README guidance is a good baseline: explain what the project does, why it is useful, how to run it, and where to get help.
For a side project, use this shorter README shape:
Project nameLive demoWhat this project provesCore user flowData model or API shapeStates handledAccessibility checksPerformance note, if relevantTradeoff madeWhat I would improve next
Do not write a long essay. Write enough that another engineer can inspect your claim.
You do not need a challenge platform to build a strong side project. You need a clear problem, constraints, a finished flow, and visible proof.
Structured challenges help when the blank page is slowing you down. A brief, design target, assets, or API contract can give you useful constraints. If you use GreatFrontEnd Projects challenges, treat them as ingredients for a larger idea, not as the whole project.
Good ways to use them:
The rule is simple: if many people can build the same brief, your added constraint and explanation are what make the project yours.
Before adding a side project to your resume or portfolio, prepare the follow-up questions it will create.
| Project type | Interview question it should create | Your answer should mention... |
|---|---|---|
| Auth flow | How did you handle invalid credentials and disabled submit? | Validation, pending state, error copy, accessibility |
| Product listing | How are filters, sorting, pagination, and URL state related? | Source state vs derived state, query params, empty states |
| Kanban board | Where does state live and how do card updates work? | Normalized data, reorder logic, optimistic UI, undo boundaries |
| Image uploader | How did you validate files before upload? | File type, size, preview, progress, failed upload recovery |
| Modal or tabs | How does keyboard navigation work? | Focus order, escape key, labels, selected state |
| Performance rewrite | What changed after measurement? | Baseline, tool used, bottleneck, before/after tradeoff |
If you cannot answer these, improve the project before adding another one.
| Reader situation | Best side project direction |
|---|---|
| You are early in frontend | Booking flow, support inbox, subscription dashboard |
| You know React but lack depth | Transaction review tool, feature flag console, form builder |
| You have no experience | Two complete projects with live links, READMEs, and proof states |
| You are switching from backend | Feature flag console, subscription audit dashboard, API-backed support inbox |
| You are aiming for senior roles | Component behavior lab, design QA board, performance-focused data tool |
The MDN Curriculum is a useful checklist for the base skills behind these projects: semantic HTML, CSS layout, JavaScript, accessibility, version control, performance, testing, and frameworks.
Use this when you want progress without turning the project into a forever build.
Two or three finished projects are enough for most early portfolios. Each one should prove a different skill. If two projects prove the same thing, improve one of them instead of adding both.
No. Clone projects are bad only when they copy the screenshots and skip the decisions. Change the data model, add failure states, write your own README, and explain one tradeoff.
Use the stack that helps you finish and explain the project. A boring React or vanilla JavaScript project with good states is stronger than a trendy stack used only for the name.
Open the live demo and add the first missing proof state: empty data, failed request, invalid form, mobile layout, keyboard focus, or a README tradeoff note.
Pick one side project, make it reviewable, and finish the uncomfortable states. That is where the value is.

Frontend project ideas for your resume should help a reviewer answer one question: can this person build product UI beyond a tutorial? The best project creates a live demo, readable repo, clear README, and a bullet you can defend under follow-up.
Do not collect project names. Pick one hiring signal, build a small version, then add proof states: loading, empty, error, invalid, permission, slow network, keyboard, mobile, or performance. GitHub README docs are a useful baseline for making the repo reviewable. Pair this guide with Frontend Developer Portfolio when deciding which projects deserve case studies.
Use this table as a chooser. A good resume project is not impressive because the idea is unusual. It is impressive because the demo, repo, and README make one frontend skill visible.
| Project idea | Best for | What it proves | Resume bullet direction |
|---|---|---|---|
| Job application tracker | Freshers and juniors | Forms, filters, local persistence, empty states | Saved applications with URL filters, reminders, and mobile table layout |
| API-backed product catalog | Freshers and juniors | Async data, search, sorting, pagination, retry states | Catalog browsing with debounced search, category filters, and error recovery |
| Booking or checkout flow | Freshers, juniors, mid-level | Multi-step state, validation, review flows | Booking wizard with calendar state, validation, confirmation, and rollback |
| Admin data table | Juniors and mid-level | Dense UI, derived state, permissions, responsive views | Table with sorting, filtering, bulk actions, permissions, and mobile fallback |
| Accessible component set | Any level | Semantics, keyboard behavior, focus management | Modal, tabs, menu, and form errors documented with keyboard checks |
| Design-system slice | Juniors, mid-level, seniors | Component API design, variants, docs, reuse | Button, Input, Dialog, Tabs, and Toast components with states and examples |
| Performance rewrite | Mid-level and seniors | Measurement, rendering cost, before/after reasoning | Reduced list rendering cost with measurement notes and a documented tradeoff |
| Offline reading list | Freshers and juniors | Storage, invalid data handling, sync thinking | Saved articles locally with stale-data cleanup and offline-friendly states |
| Analytics dashboard | Juniors, mid-level, seniors | Charts, loading states, filters, data modeling | Dashboard with date filters, skeletons, empty states, and chart fallbacks |
| Issue triage board | Juniors, mid-level, seniors | Drag/drop or workflow state, optimistic updates | Workflow board with status transitions, optimistic UI, and conflict handling |
Choose the project by the kind of proof missing from your resume. If every project says "built with React," the reviewer still has to guess whether you can handle product behavior.
| If your resume needs proof of... | Start with | Add next |
|---|---|---|
| First-role readiness | Job tracker, product catalog, booking flow | Live link, README, empty/error states, mobile layout |
| Data-heavy frontend work | Dashboard, admin table, search app | URL filters, pagination, loading states, performance notes |
| Accessibility | Modal, tabs, menu, form validation | Keyboard behavior, focus management, test notes |
| Performance awareness | Large-list rewrite, image gallery optimization | Before/after measurement and a tradeoff note |
| Senior scope | Design-system slice, migration note, audit app | Reuse constraints, API choices, ownership decisions |
Keep the scope versioned: v1 is the main flow, v2 adds visible quality states, and v3 adds the advanced signal such as tests, performance, optimistic UI, roles, or sync. A complete v1 with a clean README is better than a huge app that never reaches a live URL.
The rest of this article gives you project briefs you can use directly. Start with one, not all of them.
If you want a real brief instead of a blank page, start from GreatFrontEnd Projects challenges. The challenges give you project-style constraints such as a brief, design target, API shape, or component behavior. Use them as the base layer, then add your own product decisions so the work becomes resume-ready.
For full app-shaped practice, use the GFE Projects Apps track. It is useful when you want a portfolio project that feels closer to product UI: feeds, media apps, AI chat, API-backed clients, and multi-screen flows.
Good ways to adapt them:
The resume value comes from the extra layer you add: README notes, edge states, tradeoffs, deployment, and the follow-up questions you can answer from your own code.
A job tracker is one of the safest frontend project ideas for a fresher resume because it matches the reader's own workflow and creates practical product decisions.
Build saved applications with company, role, location, source, status, deadline, notes, and next action. The useful version is not a spreadsheet clone. It should help a user decide what to do next.
| Scope | What to build |
|---|---|
| v1 | Add, edit, delete, and filter applications by status |
| v2 | Persist data locally, keep filters in the URL, add empty and invalid states |
| v3 | Add reminders, CSV export, stale application warnings, and mobile table cards |
Resume bullet:
Built a job application tracker with URL filters, local persistence, editable status stages, empty states, and a responsive table/card layout.
Interview follow-ups to prepare:
Practice slices on GreatFrontEnd: Contact Form for validation and Data Table for sorting and derived state.
A product catalog proves async UI without forcing you to build a full backend. It is also easy for a reviewer to inspect because the flow is familiar: search, browse, filter, view detail, recover from failure.
Use a public API, a small mocked API, or local JSON served through your app. The important part is the frontend behavior around the data.
| Scope | What to build |
|---|---|
| v1 | Product list, category filter, search, detail view |
| v2 | Loading skeletons, empty states, retry button, pagination, responsive cards |
| v3 | Debounced search, stale-response guard, URL filters, saved items, test cases |
Resume bullet:
Built an API-backed product catalog with debounced search, category filters, pagination, loading and error states, stale-response handling, and responsive card/table views.
Interview follow-ups to prepare:
Practice slices on GreatFrontEnd: Autocomplete for async search depth and Data Table for derived views.
Booking and checkout flows are good resume projects because they force state transitions. The reviewer can see whether you handle invalid input, review steps, disabled actions, and failed submissions.
Pick one domain: appointment booking, event registration, course checkout, rental reservation, or mock e-commerce checkout.
| Scope | What to build |
|---|---|
| v1 | Multi-step form with user details, item selection, review, and confirmation |
| v2 | Field validation, disabled submit, edit-from-review, error recovery |
| v3 | Optimistic or pending state, unavailable inventory, persisted draft |
Resume bullet:
Built a booking flow with multi-step state, form validation, review/edit behavior, unavailable-slot handling, and a confirmation state.
Interview follow-ups to prepare:
Practice slice on GreatFrontEnd: Contact Form for validation discipline.
An admin table is better than another landing page because it shows how you handle dense information. It can be a user management table, inventory table, invoice table, support ticket table, or content moderation queue.
The point is not visual complexity. The point is whether the table remains usable when filters, columns, permissions, and mobile layout collide.
| Scope | What to build |
|---|---|
| v1 | Rows, columns, sorting, filtering, pagination |
| v2 | Empty states, loading state, column visibility, row detail, mobile card layout |
| v3 | Bulk actions, role-based disabled actions, optimistic update, audit note |
Resume bullet:
Built an admin table with sortable columns, filters, pagination, bulk actions, permission-aware controls, and a mobile card fallback.
Interview follow-ups to prepare:
Practice slice on GreatFrontEnd: Data Table.
Accessibility projects are valuable because many portfolios claim accessibility while showing only color contrast or alt text. A better project proves interaction behavior: keyboard navigation, focus management, labels, errors, and screen-reader-friendly structure.
Build a small component set: modal dialog, tabs, menu, form field, toast, and combobox or autocomplete if you want a harder version. Use semantic HTML first, and use the WAI-ARIA Authoring Practices Guide when native semantics do not cover the interaction.
| Scope | What to build |
|---|---|
| v1 | Modal, tabs, and form field with labels and errors |
| v2 | Keyboard behavior, focus return, escape handling, visible focus |
| v3 | Docs page with usage notes, disabled/error states, and test checklist |
Resume bullet:
Created an accessible component set with modal, tabs, menu, and form errors, documenting keyboard behavior, focus management, and disabled states.
Interview follow-ups to prepare:
Practice slices on GreatFrontEnd: Tabs and Modal Dialog.
A design-system slice is useful when you want to show component API thinking. Keep it small. A good Button, Input, Dialog, Tabs, and Toast set tells a reviewer more than 30 inconsistent components.
The resume signal is not "I used Tailwind" or "I used Storybook." The signal is that your components have clear props, variants, states, docs, and examples.
| Scope | What to build |
|---|---|
| v1 | Button, Input, Dialog, Tabs, Toast with default styles |
| v2 | Variants, disabled/loading/error states, composition examples |
| v3 | Docs page, accessibility notes, visual regression or component test notes |
Resume bullet:
Built a design-system slice with Button, Input, Dialog, Tabs, and Toast components, including variant APIs, disabled/error states, usage docs, and accessibility notes.
Interview follow-ups to prepare:
A performance project is strongest when it starts with a slow UI and ends with measured improvement. Do not claim the app is faster because it feels faster. Record the before state, change one bottleneck, then explain the result.
Good options: a large product list, image gallery, dashboard, chat transcript, or table with expensive row rendering.
| Scope | What to build |
|---|---|
| v1 | A deliberately heavy list, gallery, or dashboard with visible slow interaction |
| v2 | Measurement notes, memoization or render split, image sizing, code splitting |
| v3 | Virtualized rows, before/after screenshots, tradeoff note, repeatable test script |
Resume bullet:
Measured and reduced slow interaction in a large product list by splitting expensive row rendering, deferring non-critical UI, and documenting before/after results.
Interview follow-ups to prepare:
For browser performance concepts, web.dev performance guidance is a useful reference while you write your README note.
An offline reading list is a small project with surprisingly good depth. It proves local storage, data validation, stale data handling, empty states, and sync thinking without requiring a complex server.
Build saved articles or resources with title, URL, tags, read/unread state, notes, and last-updated time.
| Scope | What to build |
|---|---|
| v1 | Add, edit, delete, tag, search, and mark items as read |
| v2 | Local persistence, invalid URL messages, empty state, import/export |
| v3 | Offline indicator, stale data cleanup, conflict note for future sync |
Resume bullet:
Built an offline reading list with local persistence, tag filtering, import/export, invalid data cleanup, and empty states for saved resources.
Interview follow-ups to prepare:
A dashboard proves data modeling and UI judgment if it does more than render charts. The useful version handles dates, filters, empty data, loading, slow requests, and chart fallbacks.
Pick a domain with understandable metrics: job applications, expenses, habits, content performance, study progress, or support tickets.
| Scope | What to build |
|---|---|
| v1 | Summary cards, table, chart, date range filter |
| v2 | Loading skeletons, empty states, invalid date ranges, responsive chart layout |
| v3 | Saved filters, comparison period, export, metric definition notes |
Resume bullet:
Built an analytics dashboard with date filters, metric cards, chart fallbacks, empty states, responsive layouts, and documented metric definitions.
Interview follow-ups to prepare:
An issue triage board is useful if you want to prove workflow UI: status transitions, drag/drop or move actions, optimistic updates, permissions, and conflict handling.
Build a board for bugs, support tickets, content review, job applications, or study tasks. The domain matters less than the state transitions.
| Scope | What to build |
|---|---|
| v1 | Columns, cards, create/edit issue, move issue between statuses |
| v2 | Filters, assignee, priority, optimistic move state, undo |
| v3 | Permission-aware actions, conflict message, audit trail, keyboard movement |
Resume bullet:
Built an issue triage board with status transitions, filters, optimistic move actions, undo, permission-aware controls, and conflict handling.
Interview follow-ups to prepare:
Clones can teach layout, but clone-only projects rarely create good resume bullets. Upgrade the clone by changing the data model, adding failure states, and documenting decisions.
| Clone starter | Weak version | Resume-ready upgrade |
|---|---|---|
| Netflix clone | Static movie cards | Search, filters, empty states, saved list, keyboard navigation |
| Spotify clone | Static player UI | Playlist editing, queue state, disabled controls, persisted preferences |
| Amazon clone | Product grid and cart only | Unavailable inventory, checkout validation, currency formatting, cart sync |
| Dashboard template | Hardcoded cards and charts | Date filters, metric definitions, loading, empty data, chart fallbacks |
| Portfolio template | Pretty homepage with no repo | Case studies, README links, tradeoff notes, live demos |
Use this sequence before adding the project to your resume:
Every serious project needs a live URL, GitHub repo, README, and one or two bullets that describe behavior rather than tools. A reviewer should understand the project without a private explanation.
| Proof item | What good looks like |
|---|---|
| Live demo | Opens without login, has sample data, and works on mobile |
| README | Explains problem, features, setup, decisions, limitations |
| Screenshots | Shows main flow plus one empty, error, or mobile state |
| Code structure | Has clear component, state, data, and utility boundaries |
| Tradeoff note | Explains one decision you would defend in an interview |
| Resume bullet | Names the flow, behavior, constraint, and technical decision |
| Follow-up notes | Answers two or three questions an interviewer is likely to ask |
Weak: Built product catalog using React.Better: Built an API-backed product catalog with debounced search, URL filters, pagination, retry states, and responsive card/table views.Weak: Made a dashboard.Better: Built an analytics dashboard with date-range filters, metric definitions, loading skeletons, empty states, and chart fallbacks for missing data.Weak: Created reusable components.Better: Built Button, Input, Dialog, Tabs, and Toast components with variant APIs, disabled/error states, keyboard behavior notes, and usage examples.
Use this shape: built [flow] for [user task], handling [state or constraint], with [technical decision].
Use this plan to finish one project instead of browsing lists forever.
One strong project is better than four weak ones. Freshers can usually list two or three projects if each has a live link, readable repo, and clear bullet. Experienced engineers should be more selective and use projects only when they add proof that work experience does not show.
Use the stack that matches the role you want, but do not let the stack become the whole story. A React job tracker with thoughtful state and error handling is better than a Next.js app that only proves routing and styling.
Clones are fine for practice. They become weak resume projects when they copy the layout without adding product behavior. Upgrade a clone with custom data, failure states, accessibility checks, mobile behavior, and a README that explains your decisions.

A good frontend developer resume does not prove that you know React, CSS, or TypeScript. It proves that someone can trust you with the next frontend problem: a product flow, a component system, a data-heavy screen, a performance issue, an accessibility gap, or a messy migration.
That is the difference between a generic software resume and a strong frontend resume. Generic advice says "quantify impact" and "use action verbs." Useful frontend advice asks: what UI behavior did you ship, what state did it handle, what broke before, what tradeoff did you make, and what can a reviewer verify?
Use this guide with Frontend Developer Portfolio, Frontend Project Ideas for Your Resume, and the Front End Interview Playbook resume chapter. This article focuses on the resume itself.
Most frontend resumes are scanned in two passes. The first pass asks whether the resume is relevant enough to keep reading. The second asks whether the claims are believable enough for an interview.
| Reviewer question | Frontend evidence that answers it |
|---|---|
| What kind of frontend work is this? | Product UI, design systems, dashboards, accessibility, performance, platform, or early-career UI |
| Can this person build real screens? | Forms, async data, routing, loading, empty, error, permission, and responsive states |
| Can they work with product reality? | User flows, design collaboration, backend contracts, analytics, release notes, or support feedback |
| Can they make sound tradeoffs? | State ownership, component boundaries, browser constraints, performance choices, rollback plans |
| Can I verify the claim? | Live demo, GitHub, portfolio case study, shipped feature, metric, review artifact, or README |
Public role definitions back this up. The U.S. Bureau of Labor Statistics profile for web developers and digital designers includes site layout, integrated applications, usability, compatibility, performance, and collaboration with designers and other developers. O*NET's web and digital interface designer profile also names browser and device testing, accessibility standards, user research, documentation, and web architecture choices. These are broad US occupational profiles rather than a checklist for every frontend opening, but they show why a resume needs more than a stack list.
Do this before rewriting bullets. Open a note and collect raw proof from your work and projects.
| Proof source | What to look for | Resume use |
|---|---|---|
| Shipped features | Screens, flows, states, release scope, user groups, bug reports | Experience bullets |
| Project repos | README, setup, screenshots, tradeoffs, tests, deployment, open issues | Project bullets and links |
| Pull requests or RFCs | Decisions, review comments, migration plan, compatibility notes | Seniority, collaboration, architecture evidence |
| Performance work | LCP, INP, CLS, bundle size, render cost, API latency, profiling notes | Performance bullets |
| Accessibility work | Labels, focus management, keyboard behavior, error announcements, APG patterns | Accessibility bullets |
| Design-system or UI library | Component APIs, variants, docs, adoption, migrations, usage examples | Design-system bullets |
| Testing and quality | Unit tests, component tests, visual checks, regression fixes, manual test matrix | Reliability bullets |
| GFE projects or challenges | Component behavior, edge cases, constraints, interview follow-ups | Early-career project proof |
GitHub's README guidance says a README should explain what a project does, why it is useful, and how to get started. For hiring, add what GitHub's baseline does not require: screenshots, handled states, tradeoffs, test notes, and known limitations.
Your resume gets stronger when it is aimed at a role lane. You can still apply broadly, but the top third of the page should not sound identical for every job.
| Target lane | Lead with | De-emphasize |
|---|---|---|
| Product frontend engineer | Product flows, forms, API state, routing, user-facing quality | Decorative UI without product behavior |
| React or TypeScript engineer | Component boundaries, typed data, state ownership, async behavior | A raw list of hooks and packages |
| Dashboard or internal tools | Tables, filters, permissions, pagination, bulk actions, dense UI | Landing pages and static cards |
| Design systems | Component APIs, tokens, docs, accessibility, adoption, migration support | Random components with no usage story |
| Performance-focused frontend | Measurement, bottleneck, fix, tradeoff, field or lab verification | "Optimized website" with no metric or diagnosis |
| Accessibility-focused UI | Semantics, keyboard support, focus recovery, accessible names, test notes | Color contrast only |
| Frontend platform | Build tooling, shared patterns, test reliability, migration paths | Individual feature screenshots only |
| Fresher or junior | Finished projects, live links, GitHub, fundamentals, debugging discipline | Dozens of tools with no inspectable project |
If you are early-career, read Frontend Developer Resume for Freshers after this. If you are 5+ years in, read Senior Frontend Developer Resume for the senior version.
There is no universal frontend resume structure. Use the order that puts the strongest proof first.
| Profile | Strong section order |
|---|---|
| Fresher | Header, headline, skills, projects, education, extras |
| Junior with internships | Header, headline, experience, projects, skills, education |
| 2-4 years | Header, summary, experience, selected projects, skills, education |
| Career switcher | Header, summary, frontend projects, transferable experience, skills, education |
| Senior | Header, senior summary, experience, selected impact, skills, education |
| Design-system or platform track | Header, summary, relevant experience, systems/platform work, skills, talks/docs if useful |
Harvard's resume guide is a useful generic baseline: resume language should be specific, active, fact-based, quantified when possible, and written for people or systems that scan quickly. For frontend resumes, "fact-based" means naming the actual interface behavior and the constraint behind it.
Weak bullets describe tasks. Strong bullets describe shipped behavior and why the work mattered.
Shipped [user-facing behavior or system change] for [user/product/context]by [technical decision], handling [state/constraint/risk],resulting in [measured or observable outcome].
You do not need every part in every bullet, but every important bullet should contain at least three of these:
| Generic bullet | Stronger frontend bullet |
|---|---|
| Built dashboard using React | Built a React operations dashboard with URL filters, paginated API data, row actions, loading states, and retry handling |
| Worked on performance | Reduced product-page LCP by prioritizing the hero image, removing unused client JavaScript, and verifying the change in tools |
| Made forms | Built checkout forms with field validation, disabled submit states, server error recovery, and mobile review step |
| Created reusable components | Designed typed Input and Dialog APIs with error, disabled, loading, focus-return, and usage examples for three product areas |
| Fixed search bugs | Fixed stale search results by adding request cancellation and response ordering guards around async queries |
| Made website responsive | Reworked table and action-bar layout so filters, row actions, and empty states remained usable on narrow screens |
| Improved accessibility | Added labels, keyboard navigation, focus recovery, and error announcements to account settings dialogs |
| Wrote tests | Added component tests for invalid form states, API error recovery, and permission-disabled actions |
The React docs' Thinking in React guide is a good mental model for bullet depth: component hierarchy, visual states, one-way data flow, minimal state, and where state should live. A frontend bullet that mentions those ideas through real work is more convincing than "used React hooks."
Do not invent numbers. Frontend work often has valuable evidence even when conversion, revenue, or traffic metrics are private or unavailable.
| Instead of fake impact | Use defensible evidence |
|---|---|
| "Increased revenue by X%" | "Released checkout review step with validation, server error recovery, and analytics" |
| "Improved performance greatly" | "Reduced blocking render work after profiling a slow 500-row table" |
| "Enhanced UX" | "Moved filters into the URL so users could share and restore exact table views" |
| "Made app more accessible" | "Documented modal focus behavior and added keyboard checks to the release checklist" |
| "Led frontend architecture" | "Split server state, URL state, and local UI state during dashboard refactor" |
| "Improved code quality" | "Removed duplicated validation logic from four forms and added shared test cases" |
Good non-business metrics include:
For performance claims, use current web language. Google's Core Web Vitals are LCP for loading, INP for interactivity, and CLS for visual stability, with recommended thresholds of 2.5 seconds, 200 milliseconds, and 0.1 respectively at the 75th percentile. If you cannot measure those, name what you did measure, such as render cost, bundle size, or lab tooling.
Skills should be scannable, truthful, and connected to bullets. A long tool dump creates interview risk.
Frontend: JavaScript, TypeScript, React, Next.js, HTML, CSSUI behavior: forms, routing, async data, responsive layouts, accessibility basicsTesting: React Testing Library, Playwright, VitestTooling: Git, npm/pnpm, Vite, Chrome DevToolsDesign collaboration: Figma, component specs, design QA
Use these rules:
For example, "Accessibility" in the skills section should connect to bullets about labels, keyboard behavior, focus management, error messages, semantic structure, or WAI-ARIA Authoring Practices Guide patterns.
For freshers, juniors, career switchers, and candidates with private work, projects can carry the resume. A project is resume-worthy when it creates interview questions you can answer.
| Project type | Resume-worthy proof |
|---|---|
| Product catalog | Search, filters, pagination, loading/empty/error states, stale-response handling |
| Job tracker | Forms, URL filters, local persistence, edit/delete flow, mobile table/cards |
| Checkout or booking flow | Multi-step state, validation, review/edit step, unavailable item handling, failed submit |
| Admin table | Sorting, filtering, bulk actions, permissions, row detail, responsive fallback |
| Component set | Button, Input, Dialog, Tabs, Toast with states, keyboard notes, and usage examples |
| Performance case study | Baseline, bottleneck, change, measurement, tradeoff, result |
| Accessibility slice | Native semantics where possible, ARIA only where needed, keyboard support, focus behavior |
Do not list five tiny projects when two complete projects would be stronger. A live demo, GitHub repo, and README with setup, screenshots, tradeoffs, and limitations are more valuable than another project card.
Applicant tracking systems are a reason to write clearly, not a reason to keyword-stuff.
Use this practical checklist:
The resume still needs to read well to humans. A keyword match may get the resume opened; evidence gets it kept.
Tailoring should take 15-30 minutes, not a full rewrite from scratch.
Example:
| Job asks for | Move up | Rewrite around |
|---|---|---|
| React, TypeScript, dashboards | Data-table or internal-tool work | URL filters, typed rows, async states, permissions |
| Design-system engineer | Shared component work | component API, accessibility, docs, migration, adoption |
| Performance-focused frontend | Slow page, table, image, or bundle work | metric, bottleneck, fix, tradeoff, verification |
| Early-career frontend with GitHub | Best complete project | live demo, README, edge states, responsive behavior |
| Product frontend with forms | Checkout, signup, settings, booking, onboarding, or support workflow | validation, error recovery, disabled states, server integration |
For most early and mid-level frontend roles, one page is enough. Use two pages only when the second page adds real proof.
NameCity | email | phone | GitHub | LinkedIn | PortfolioFrontend engineer focused on [lane] with experience building [strongest proof],including [state/data/accessibility/performance/testing signal].SkillsFrontend: JavaScript, TypeScript, React, Next.js, HTML, CSSUI: forms, async data, routing, responsive layouts, accessibilityTesting/tooling: Playwright, Vitest, React Testing Library, Git, Chrome DevToolsExperienceCompany, Frontend Developer, Month Year - Present- Shipped [flow/screen] with [UI behavior], handling [constraint] and improving [result].- Reworked [state/data/component/performance area] by [decision], reducing [risk/friction].- Collaborated with [design/backend/QA/product] to [release/test/migrate/document] [scope].ProjectsProject Name | Live | GitHub- Built [project] with [states], [routing/data/forms], and [responsive/accessibility/testing proof].- Documented [setup/tradeoff/limitation] so reviewers can inspect the project quickly.EducationDegree, school, year
Before sending, read the resume as if you have one minute and no context.
Cut lines that make the resume look bigger but weaker:
Space is expensive. Spend it on proof that you can build, debug, explain, and ship interfaces.
If the whole resume feels overwhelming, do only this:
That single improvement often changes how the whole resume reads. A shortlisted frontend resume is specific, inspectable, and honest. It tells the reviewer what you built, what made it non-trivial, and why you can be trusted with the next frontend problem.

A senior frontend developer resume should make your level obvious without forcing the reader to count years. At 5+ years, the question is no longer "Can this person build React screens?" It is "Can this person own frontend problems with ambiguity, tradeoffs, other teams, production risk, and long-term maintainability?"
That means a senior resume needs different evidence from a junior or mid-level resume. It should show scope, judgment, risk reduction, technical direction, product context, and influence through other people or shared systems.
If you need the all-level version first, read How to Write a Frontend Developer Resume. This article is specifically for senior frontend engineers, senior UI engineers, frontend platform engineers, design-system engineers, and senior product engineers whose work touches the frontend deeply.
The senior resume is not a longer mid-level resume. The strongest bullets shift from implementation output to ownership quality.
| Mid-level signal | Senior signal |
|---|---|
| Built a feature | Owned a product flow, migration, system, or quality bar |
| Used React and TypeScript | Chose component, state, routing, and API boundaries under constraints |
| Fixed bugs | Reduced a recurring class of regressions |
| Improved performance | Diagnosed bottlenecks, chose tradeoffs, measured impact, prevented regression |
| Collaborated with design and backend | Aligned design, backend, product, QA, and support before release |
| Mentored juniors | Created review habits, docs, pairing patterns, or migration guides |
| Created reusable components | Drove adoption of a component API across product areas |
| Worked on accessibility | Established keyboard, semantics, focus, and release checks for risky flows |
The U.S. Bureau of Labor Statistics software developer profile describes software developers as people who analyze users' needs, design software, ensure software functions through maintenance and testing, document systems, and work on teams. For senior frontend developers, your resume should show those responsibilities through browser-facing systems: UI architecture, interaction quality, performance, accessibility, and release safety.
Senior frontend roles are not interchangeable. A resume for a design-system role should not lead with the same proof as a resume for a product UI or frontend platform role.
| Senior lane | Lead with | Weak lead |
|---|---|---|
| Senior product frontend | End-to-end flows, product tradeoffs, async state, release coordination | Component snippets with no product context |
| Design-system engineer | Component APIs, accessibility, tokens, docs, migration, adoption | A gallery of unrelated components |
| Frontend platform engineer | Build tooling, test reliability, shared patterns, migration paths | Individual UI features only |
| Performance-focused frontend | LCP, INP, CLS, profiling, bundle strategy, render cost, monitoring | "Optimized app" without diagnosis or verification |
| Accessibility-focused frontend | Semantics, keyboard support, focus recovery, accessible names, review gates | Color contrast only |
| Staff-leaning frontend engineer | Cross-team architecture, standards, technical strategy, incident prevention | One-team delivery with no wider influence |
| Senior full-stack with frontend | Frontend ownership plus API contracts, data modeling, observability, release | Backend-heavy bullets with frontend reduced to "built UI" |
Your summary should name the lane and the proof.
| Weak senior summary | Stronger senior summary |
|---|---|
| Senior frontend developer with 7 years of experience | Senior frontend engineer owning React product flows, typed component APIs, and performance work for data-heavy dashboards |
| React and TypeScript developer with leadership skills | Senior UI engineer leading checkout and account flows across design, backend, QA, and release coordination |
| Experienced frontend developer focused on clean code | Frontend platform engineer reducing duplicated app patterns through shared state, testing, and migration tooling |
| Senior developer with design-system experience | Design-system engineer driving accessible component APIs, documentation, and adoption across product squads |
Senior bullets should show what changed because you owned the problem.
Owned [scope] under [constraint/risk],chose [technical/product direction],and produced [measured or observable result].
Or, for leadership and influence:
Created [artifact/system/review habit] that helped [team/users]make [decision/action] more reliably across [scope].
Good senior bullets usually include:
| Weak bullet | Stronger senior bullet |
|---|---|
| Led frontend development | Led checkout settings refactor across web, design, backend, and QA, preserving URL contracts while replacing duplicated form state |
| Worked on app architecture | Split dashboard state into URL filters, server data, and local UI state so users could share views and refresh without losing context |
| Improved performance | Reduced slow table interactions by profiling render cost, virtualizing long lists, and moving expensive derived data behind memoized inputs |
| Built design system | Owned Dialog, FormField, and Toast APIs with focus behavior, error states, docs, and migration examples used by three product teams |
| Mentored developers | Turned repeated review feedback on forms, loading states, and accessibility into a checklist used during frontend code review |
| Improved accessibility | Standardized modal focus return, keyboard escape behavior, accessible labels, and error announcements after regressions in account flows |
| Reduced bugs | Removed a class of stale async-result bugs by introducing request cancellation and response-ordering guards for search and filtering screens |
| Coordinated migration | Migrated shared table actions behind a compatibility wrapper, enabling gradual adoption without blocking active feature work |
Notice what these bullets do: they show judgment. They name the problem shape, the technical decision, and the operational result.
A senior resume should not explain your whole system. It should expose the decisions an interviewer will want to discuss.
Useful architecture proof includes:
| Architecture area | Resume evidence |
|---|---|
| State ownership | URL state, server state, local UI state, derived state, optimistic state, persisted draft state |
| Data contracts | API versioning, typed responses, pagination, stale responses, partial failures, permission boundaries |
| Component APIs | controlled/uncontrolled behavior, composition, variants, accessibility, adoption constraints |
| Routing | shareable filters, deep links, auth redirects, route ownership, backward compatibility |
| Migration strategy | compatibility wrapper, staged rollout, codemod, review checklist, deprecation plan |
| Quality gates | component tests, visual checks, accessibility checks, monitoring, rollback plan |
| Design collaboration | design tokens, Figma parity, component specs, release QA, edge-state review |
React's official Thinking in React guide frames UI work around component hierarchy, visual states, data flow, minimal state, and state location. Senior bullets should show how you applied those ideas under production constraints instead of repeating them as theory.
"Improved performance" is one of the easiest senior claims to weaken. A credible performance bullet names the user pain, the measurement, the bottleneck, the fix, and the tradeoff.
Use current web performance language:
| Performance claim type | Strong evidence |
|---|---|
| Loading | LCP, image priority, font loading, render-blocking resources, server response, route-level bundles |
| Interactivity | INP, long tasks, expensive renders, unnecessary re-renders, input delay, hydration cost |
| Visual stability | CLS, reserved image dimensions, ad/embed shifts, skeleton layout, async content placement |
| Runtime UI | table virtualization, memoized derived data, debounced search, worker offload, chart rendering |
| Monitoring | field data, real-user monitoring, alerting, dashboards, regression checks |
Google's Core Web Vitals currently focus on LCP, INP, and CLS. If you mention those metrics, be prepared to explain whether you used lab tools, field data, or both. If the metric is private, anonymize the number or describe the verified direction without leaking confidential details.
Weak:
Improved dashboard performance.
Strong:
Reduced slow dashboard interactions by profiling long render paths, virtualizing 1,000+ row tables, and moving expensive derived filters out of keystroke updates.
Even without a public metric, the second bullet creates a real interview path.
Accessibility is not a decoration line. At senior level, it should show interaction ownership.
The WAI-ARIA Authoring Practices Guide emphasizes accessible semantics, common widget patterns, functional examples, keyboard support, and accessible names. Translate that into resume evidence:
| Accessibility area | Resume evidence |
|---|---|
| Forms | labels, instructions, validation, error identification, error announcement |
| Modal dialogs | initial focus, Tab contained within the dialog, focus return, escape behavior, accessible name |
| Tabs and menus | keyboard navigation, selected/active/disabled states, semantics |
| Async UI | loading announcements, status messages, retry paths, preserved focus |
| Design system | accessible defaults, usage docs, review checks, examples, testing notes |
| Release process | keyboard QA checklist, screen reader smoke checks, regression notes |
Weak:
Improved accessibility across the app.
Strong:
Standardized dialog and form accessibility across account flows, documenting focus return, keyboard escape behavior, labels, error messages, and release checks.
Senior design-system work is not "built buttons." It is API design, quality, migration, and adoption.
| Design-system proof | Strong resume detail |
|---|---|
| Component API | controlled props, composition model, variants, disabled/loading/error states |
| Accessibility | keyboard behavior, focus management, semantic defaults, labelled examples |
| Documentation | usage guidance, do/don't examples, migration notes, design token mapping |
| Adoption | teams or product areas migrated, duplicate patterns removed, compatibility support |
| Governance | review checklist, contribution model, release notes, versioning, deprecation process |
| Quality | visual regression, component tests, Storybook examples, accessibility checks |
Better bullet:
Owned shared Dialog and FormField APIs across three product areas, adding accessibility defaults, usage docs, migration examples, and review checks that reduced repeated implementation drift.
"Mentored juniors" is weak because it tells the reviewer nothing about how your influence scaled. Senior leadership is easier to believe when it left artifacts.
| Leadership claim | Stronger evidence |
|---|---|
| Mentored engineers | pairing plan, review checklist, onboarding guide, repeated feedback turned into docs |
| Led a project | RFC, migration plan, rollout stages, risk log, stakeholder alignment, release checklist |
| Improved code quality | lint rule, shared pattern, test helper, component API, refactor guide |
| Raised team standards | accessibility checklist, performance budget, PR template, design QA routine |
| Cross-team influence | adopted by multiple squads, shared in engineering forum, used in release process |
Strong:
Created a frontend review checklist for forms, async states, accessibility, and mobile layouts that converted repeated senior review comments into reusable team guidance.
This says more than "excellent communication skills."
Senior work often has private numbers. You can still write useful bullets without exposing sensitive data.
| Confidential detail | Safer resume version |
|---|---|
| Revenue or conversion | "improved checkout completion for a high-traffic purchase flow" |
| Exact customer count | "used by enterprise customers" or "used by internal support teams daily" |
| Private incident details | "after a production regression in account settings" |
| Internal project codename | "dashboard migration," "billing workflow," "content review tool" |
| Proprietary architecture | "split server state, URL state, and local UI state" without exposing implementation |
| Exact performance number | "reduced interaction delay after profiling render cost" if the number is not public |
Do not make the bullet so anonymized that it becomes meaningless. Keep the shape of the problem visible.
Most senior frontend resumes can be one or two pages. Two pages are fine when the second page contains real scope, not old task lists.
NameCity | email | phone | LinkedIn | GitHub | Portfolio or selected writingSenior frontend engineer focused on [lane], with experience owning [scope],[architecture/performance/accessibility/design-system signal], and cross-team delivery.Selected Impact- Owned [largest or most relevant senior proof].- Drove [system, migration, performance, accessibility, design-system, or platform result].- Created [artifact/process] adopted by [team/product area].ExperienceCompany, Senior Frontend Engineer, Month Year - Present- Owned [product/system scope] under [constraint], choosing [technical direction] and producing [result].- Reduced [risk/friction/regression class] by [architecture/testing/documentation/release decision].- Led [cross-functional/cross-team work] across [design/backend/QA/product/support], preserving [contract/quality].Company, Frontend Engineer, Month Year - Month Year- Show progression from feature ownership to system ownership.- Keep older bullets shorter unless they still support the target senior lane.SkillsFrontend: TypeScript, React, Next.js, HTML, CSSArchitecture: state modeling, component APIs, routing, async data, performanceQuality: Playwright, React Testing Library, accessibility checks, monitoringCollaboration: RFCs, design-system docs, technical mentoring, release planning
Use a "Selected Impact" section only if it helps the top third of the resume. If it repeats the first experience bullets, skip it.
These patterns can make a senior resume read mid-level:
If the resume could describe a mid-level feature contributor, add ownership, constraint, and result.
Senior tailoring is not keyword stuffing. It is choosing the right senior proof for the company problem.
| Job description signal | Move up | Rewrite around |
|---|---|---|
| "Design systems" | Component API, tokens, docs, adoption, accessibility | migration, governance, usage examples, review process |
| "Performance" | LCP/INP/CLS, profiling, bundle work, runtime UI | measurement, bottleneck, fix, tradeoff, monitoring |
| "Highly collaborative product team" | Cross-functional flow ownership | design/backend/QA/product alignment and release decisions |
| "Data-heavy UI" | Dashboards, tables, filters, permissions, server state | URL state, pagination, stale data, virtualization, error recovery |
| "Frontend platform" | Tooling, test reliability, shared patterns, migrations | adoption, developer experience, compatibility, release safety |
| "Accessibility" | Keyboard behavior, semantics, focus recovery, design-system defaults | APG patterns, review gates, release checks |
| "Tech lead" | RFCs, cross-team migration, mentorship artifacts, risk management | decision quality, sequencing, alignment, follow-through |
Keep one master resume with all senior proof. For each application, reorder and trim.
Every senior bullet should create a real technical conversation. Before sending, ask:
If you cannot answer those questions, rewrite the bullet until it matches the work you can defend.
Read the resume for 90 seconds and check:
A senior frontend resume should not shout "senior." It should make the reader feel the shape of the problems you can own: ambiguous, cross-functional, user-facing, measurable, and hard to keep healthy over time.

Frontend interview preparation for freshers should start with proof. You need one or two projects you can explain, sound HTML, CSS, and JavaScript basics, and enough practice to reason when a question is unfamiliar.
You do not need to sound senior. You need to show that you can build a small UI, inspect mistakes, explain tradeoffs, and improve from feedback. That is the signal most fresher interviews are looking for.
For topic coverage, use MDN Curriculum and React Learn. Stack Overflow's published 2025 Developer Survey is useful ecosystem context because JavaScript, HTML/CSS, TypeScript, and React remain widely used by professional developers, but your interview will still be decided by the code and explanations you can defend.
For practice, pair this guide with the Front End Interview Playbook, JavaScript interview questions, React interview questions, and UI coding questions.
Most fresher frontend interviews are not trying to prove that you know every framework. They are checking whether you can handle beginner tasks without guessing randomly or hiding behind copied code.
| Interview signal | What the interviewer wants to see | Weak signal |
|---|---|---|
| Browser basics | Semantic HTML, forms, CSS layout, events, and responsive behavior | Only naming tags, classes, or Bootstrap utilities |
| JavaScript reasoning | Arrays, objects, functions, async behavior, DOM events, and debugging | Memorized definitions with no example |
| UI state | Loading, empty, error, invalid, success, and mobile states | A happy-path screen only |
| Project ownership | User problem, state model, API/data behavior, tradeoffs, and one bug fixed | "I used React" and a tech stack list |
| Communication | Clarifies the problem, explains assumptions, and tests small pieces | Silence, bluffing, or jumping between ideas |
The fresher bar is not "knows everything." It is "can be trusted with small frontend work and can learn after review."
Before applying widely, prepare a small proof set that can support your resume, portfolio, and interview answers. Two complete projects beat five cloned demos because interviewers can ask deeper follow-ups.
| Proof item | What it should show | Interview question it prepares you for |
|---|---|---|
| One main project | Forms, state, data, edge cases, responsive UI, and a README | "Walk me through your best project." |
| One bug story | What broke, how you inspected it, and how you verified the fix | "Tell me about a bug you fixed." |
| One UI coding task | A component that handles states and keyboard or mobile behavior | "Build this small interface." |
| One JavaScript explanation | A concept explained with code and an edge case | "What happens in this snippet?" |
| One honest limitation | What you would improve next and why | "What would you change if you had more time?" |
If your strongest project is mostly static, improve the project before polishing the answer. Add a form, API-backed view, filter, modal, data table, or stateful workflow so there is real frontend behavior to discuss.
Freshers often spend too much time on framework trivia and too little time on browser fundamentals. Early frontend rounds still expose the same basics again and again.
| Area | Prepare | Proof you can show |
|---|---|---|
| HTML | Forms, labels, buttons, links, headings, tables, and landmarks | A form that works with keyboard and visible errors |
| CSS | Box model, Flexbox, Grid, responsive layout, overflow, and focus states | A page that does not break on mobile |
| JavaScript | Arrays, objects, functions, promises, fetch, DOM events, and error handling | Search, filter, modal, tabs, or API widget |
| React basics | Components, props, state, lists, effects, controlled inputs, and rendering | A project with forms and API states |
| Debugging | Console, Network tab, DOM inspection, and reading stack traces | A bug story you can explain clearly |
Do not list a tool unless you can survive a follow-up question about it. If your resume says TypeScript, React Query, Redux, Docker, or AWS, the interviewer can ask where you used it and what problem it solved.
Pick the project with the most frontend behavior, not the fanciest screenshot. A job tracker, product catalog, multi-step form, dashboard, expense tracker, or booking flow gives you more to explain than a static landing page.
Prepare a 90-second version before the interview:
My strongest project is [project].It helps [user] do [task].The main frontend behavior is [forms/API/state/layout].I handled [loading/error/empty/invalid/mobile state].One bug I fixed was [bug], and I fixed it by [specific change].If I had another week, I would improve [specific limitation].
For example, an expense tracker answer should not stop at "I used React and local storage." A stronger answer says:
The hardest part was keeping raw expenses separate from derived totals. I fixed a bug where deleted expenses still appeared in filtered totals by recalculating totals from the source list instead of storing duplicate total state.
That sentence gives the interviewer state modeling, debugging, and ownership in one answer.
Freshers lose points when an answer wanders. Use the same structure for most concept questions: definition, example, edge case, and project tie-in.
Question: What is event bubbling?1. Definition: after an event reaches its target, its bubbling phase proceeds through ancestors when that event type bubbles.2. Example: clicking a button can also trigger a click handler on its parent.3. Edge case: stopPropagation can block parent handlers, but it can also hide behavior.4. Project tie-in: event delegation can help a list handle many item clicks from one parent.
This works for closures, promises, controlled inputs, CSS specificity, useEffect, debouncing, and form validation. A short structured answer is better than a long answer that never reaches an example.
Frontend interviews reward candidates who can think through product states, not only code the happy path. Start with common UI prompts and write down the states before coding.
| Prompt | What to practice | Good next step |
|---|---|---|
| Contact form | Labels, validation, disabled submit, success, and error copy | Contact Form |
| Tabs | State, keyboard behavior, active panel, and semantics | Tabs |
| Modal | Escape key, backdrop, focus return, aria label, and cleanup | Modal Dialog |
| Data table | Sort, filter, empty state, loading state, and mobile fallback | Data Table |
| Autocomplete | Async results, debounce, stale response guard, keyboard selection | Autocomplete |
For each task, ask yourself: What is the empty state? What happens when data fails? What can be done with keyboard only? What changes on mobile? What would I test manually before saying it is done?
Many fresher candidates say "I debugged it" but cannot describe the inspection path. Make your debugging story concrete.
Use this sequence:
Example:
The product list showed old search results after I typed quickly. I reproduced it by searching "bag" and then "belt" before the first request finished. In Network, I saw the older request sometimes finished last. I fixed it with a request ID check so only the latest response updates state.
That answer is much stronger than "I fixed an API bug."
It is fine to use AI tools while learning, but the interview will test whether the code is yours in practice. If you cannot explain the state model, API call, CSS layout, or error handling, the project stops helping you.
| Weak preparation | Better preparation |
|---|---|
| Copied a React project | Rebuilt one feature from scratch and changed requirements |
| Used a generated README | Added setup commands, decisions, screenshots, and limitations |
| Memorized answers | Practiced explaining one concept through your project |
| Listed many tools | Listed fewer tools you can actually discuss |
If AI helped you build a project, make it defensible: rename vague components, remove unused code, write the README yourself, and rebuild the hardest feature without looking at the generated answer.
Do not freeze or bluff. Freshers are allowed to not know an API. The score comes from whether you can reason honestly from what you do know.
I have not used that exact API before. I would first check [docs/runtime behavior],then inspect [DOM/network/console/state]. Based on similar work, I think the issuecould be [hypothesis]. I would test it by [small verification].
For example, if you are asked about a CSS property you do not know, you can still say how you would inspect computed styles, check browser support, test a fallback, and verify the layout on mobile.
These answers sound prepared, but they do not give the interviewer enough evidence. Rewrite them into specific engineering decisions.
| Weak answer | Why it falls short | Better direction |
|---|---|---|
| "I followed a tutorial." | It does not show ownership. | Explain what you changed, broke, fixed, or extended. |
| "I know React, Node, MongoDB, Docker, and AWS." | Too many unsupported claims create doubt. | Show fewer tools connected to working projects. |
| "I do not know because I am a fresher." | Freshers can still reason from first principles. | Say what you would inspect first. |
| "The project is almost done." | Unfinished work is hard to evaluate. | Ship a smaller complete version with visible states. |
| "CSS is only styling." | Frontend roles need layout, accessibility, and interaction detail. | Explain structure, responsive behavior, focus, and overflow. |
Use this plan if you have one month before interviews. The goal is not to finish every resource. The goal is to produce interview evidence.
| Week | Practice | Output |
|---|---|---|
| 1 | HTML, CSS layout, forms, and JavaScript DOM basics | One form page and one interactive widget |
| 2 | Arrays, objects, promises, fetch, and debugging | API-backed search with loading, error, and empty states |
| 3 | React basics or your chosen framework | One deployed project with controlled inputs and component state |
| 4 | Timed UI tasks and project explanation | Two recorded project walkthroughs and one timed UI problem |
For Week 2, use JavaScript interview questions. For Week 3, use React interview questions. For Week 4, use UI coding questions and review every attempt instead of rushing to the next prompt.
Use a small checklist so nerves do not decide the round.
| Moment | What to do |
|---|---|
| Before the interview | Open your resume, project demo, GitHub repo, and notes on one bug story |
| When a question starts | Repeat the goal, ask one clarifying question, and name assumptions |
| While coding | Build the main path first, then add error, empty, loading, invalid, mobile, or keyboard states |
| When stuck | Say what you are checking and inspect one thing at a time |
| Before finishing | Run through one normal case and one edge case |
| After the interview | Write the topic you missed and one repair task for the next day |
Do not treat an interview failure as a personality review. Treat it as data: missing JavaScript concept, weak project explanation, slow UI coding, unclear debugging, or poor time management.
Use this rubric to review your own preparation before the interview.
| Area | Pass signal | Needs work |
|---|---|---|
| HTML/CSS | Can explain structure, layout, responsiveness, form behavior, and focus states | Only names tags or framework classes |
| JavaScript | Can reason through data, events, async behavior, and errors | Memorizes definitions but cannot debug |
| Project explanation | Names user problem, state, API behavior, edge cases, and one bug fixed | Says only "I used React" |
| UI coding | Builds the main path and handles at least two important states | Stops after the happy path |
| Honesty | Says what they would inspect when unsure | Bluffs or apologizes for every gap |
| Communication | Thinks out loud, checks assumptions, and names tradeoffs | Goes silent or jumps randomly |
Freshers often forget that they can ask useful questions too. Ask: "What would the first frontend task in this role likely look like?"
The answer tells you whether the team expects HTML/CSS fixes, React feature work, dashboard maintenance, testing, or broader product engineering. It also helps you connect your project proof to the role before the conversation ends.
Freshers do not need perfect answers. They need honest basics, visible project proof, and the ability to reason through small UI problems without pretending to know everything.

A frontend mock interview is useful only when it recreates the pressure of a real frontend round: unclear requirements, a ticking clock, UI states, edge cases, debugging, and follow-up questions.
Do not treat the mock as a trivia quiz. The goal is to practice the interview loop: clarify the task, choose a simple plan, build the important path, test visible behavior, explain tradeoffs, and leave with one repair task.
Use MDN Curriculum, React Learn, web.dev Learn Performance, and the WAI WCAG quick reference for topic coverage. Score the mock by what the candidate does under pressure, not by how many facts they can recite.
A good frontend mock should expose the same decisions a real interview exposes. If the prompt is too vague, too open-ended, or too easy to finish without explaining tradeoffs, the feedback will be weak.
| Signal | What the mock should reveal | Weak version |
|---|---|---|
| Clarification | Inputs, constraints, user states, and out-of-scope behavior | Starts coding from the first sentence |
| State model | Source data, derived values, and UI status are separated | Stores duplicate state until updates conflict |
| Async behavior | Loading, empty, error, retry, and stale response cases are handled | Treats failure and no results as the same state |
| Accessibility | Labels, focus, keyboard behavior, and visible errors are considered | Builds mouse-only controls |
| Debugging | Uses Console, Network, DOM, and state inspection deliberately | Changes random code until the issue disappears |
| Communication | Explains assumptions, tradeoffs, and next improvements | Goes silent or narrates every keystroke |
A mock needs a clock. Without timing, candidates often spend too long polishing setup and too little time on edge cases, accessibility, and explanation.
| Time | Activity | What to observe |
|---|---|---|
| 0-5 min | Read the prompt and ask clarifying questions | Did they find missing requirements? |
| 5-10 min | Sketch state, data, and UI plan | Can they choose a simple model before coding? |
| 10-40 min | Build the main path | Can they ship the core behavior without losing correctness? |
| 40-50 min | Add edge cases and test manually | Did they check empty, loading, error, invalid, keyboard, and mobile states? |
| 50-60 min | Review and answer follow-ups | Can they explain tradeoffs and what they would improve? |
For junior candidates, shorten the scope rather than removing the review. For senior candidates, keep the coding prompt small and spend more time on tradeoffs, failure modes, and system boundaries.
There is no universal frontend interview. Some companies start with DSA, some use UI coding, some ask React application design, and senior loops often include frontend system design. Practice the round you are likely to face.
| Target round | Mock format | Pass signal |
|---|---|---|
| JavaScript fundamentals | 30-45 minutes on arrays, async, closures, DOM, or browser APIs | Correct solution plus edge-case explanation |
| UI coding | 45-60 minutes building a component or small flow | Working states, accessibility, and readable state model |
| React application | 60 minutes with data fetching, forms, routing, or state ownership | Clear component boundaries and failure states |
| Frontend system design | 45-60 minutes of architecture discussion | Constraints, APIs, cache, sync, metrics, and rollout plan |
| Behavioral/project | 30 minutes on one shipped project and one conflict story | Specific ownership, impact, and honest tradeoffs |
The best prompts are small enough to finish and deep enough to reveal decision-making. A contact form, data table, typeahead, modal, file explorer, shopping cart, or dashboard widget can all test state, rendering, accessibility, and async behavior.
Before coding, ask the candidate to write the states they plan to support. For a typeahead, that might be idle, loading, success, empty, and error. For a modal, start with open and closed; then define behavior such as initial focus, tab containment, allowed dismissal methods, and focus restoration separately from state.
A candidate can sound polished and still miss the important frontend failure modes. Score with evidence from the session.
| Area | Score question | Repair example |
|---|---|---|
| Correctness | Did the main path and important edge cases work? | Add manual test cases before the next mock |
| Frontend behavior | Were loading, empty, error, invalid, keyboard, and mobile states considered? | Add one missing state to the same prompt |
| State design | Could the state values contradict each other? | Rebuild the state model from source data and derived values |
| Debugging | Did they inspect failures systematically? | Reproduce, inspect, form one hypothesis, then change code |
| Communication | Did they explain tradeoffs before and after coding? | Practice a 60-second plan before touching code |
| Depth | Did follow-up questions reveal understanding? | Prepare one deeper explanation from the failed topic |
Use a simple 1-5 score only after writing evidence:
| Score | Meaning | Next practice |
|---|---|---|
| 1 | Could not clarify or start | Practice reading prompts and asking requirement questions |
| 2 | Built a partial happy path | Practice smaller increments and state modeling |
| 3 | Main path worked with gaps | Add edge cases, accessibility, and manual tests |
| 4 | Good solution and explanation | Practice follow-ups and tradeoffs |
| 5 | Strong under pressure | Increase difficulty or simulate senior rounds |
Feedback should be specific enough to change the next practice session. Instead of "work on React," name the exact failure: stale response overwrote newer results, form errors were not connected to inputs, or filtered data was stored separately from the source list.
Ask the candidate to restate the bug and propose a fix. That reveals whether feedback became understanding or only polite agreement.
Feedback note:Problem: Typeahead request race.Evidence: Query "re" finished after "react" and overwrote the newer results.Fix next mock: Track requestId or use AbortController; ignore stale responses.Practice: Rebuild typeahead in 35 minutes, then add loading, empty, and error states.
Do not finish a mock and immediately collect another prompt. Pick one failure, rebuild that slice, and write the lesson. Repeated mocks without repair only rehearse the same weak habits.
Use this drill:
If the code did not finish, rebuild the smallest passing version within 20 minutes. If edge cases were weak, add only edge cases to the same solution. If communication was unclear, record the explanation again without coding.
Scenario prompts reveal whether a concept survives messy product work. Answer each one in two minutes, then write the follow-up question you would ask before choosing a solution.
| Scenario | A good answer should reason through |
|---|---|
| Build autocomplete with async results | Debounce, stale response guard, loading, empty, keyboard navigation, and error recovery |
| Implement a tabbed settings page | Semantics, URL state if needed, focus behavior, and reusable state model |
| Fix a slow list filter | Profiling, derived state, memoization only if needed, and virtualization tradeoffs |
| Review a modal bug in a pull request | Focus trap, escape behavior, scroll lock, aria labeling, and cleanup |
Watch the recording twice. On the first pass, write every missing state, bug, and unclear requirement. On the second pass, remove vague phrases and add concrete reasoning.
If you cannot explain why you chose a state shape, why an effect runs, or how an error is recovered, that is the next practice topic.
Suppose the prompt is autocomplete. You build the input and fetch results, but the interviewer notices stale results can appear when an older request resolves after a newer one.
Use this sequence:
The mock becomes valuable because it changes the next attempt. Next time, mention async ordering before being prompted and explain the bug plainly: "The UI should represent the newest query, not whichever request finishes last."
A good frontend mock interview leaves you with evidence and one repair task. If the only feedback is "you did okay," the mock was too vague to help.

Micro-frontends interview questions are architecture questions. A micro-frontend is an independently deliverable frontend slice, usually owned by one team, that is composed with other slices into one product. The interviewer is checking whether you can split frontend ownership without creating a slower, less consistent, harder-to-debug product.
The answer should not start and end with Module Federation. A good answer covers ownership boundaries, composition, shared dependencies, routing, design systems, observability, rollback, and user experience.
For current implementation vocabulary, use webpack Module Federation docs, single-spa microfrontends guide, and MDN import maps reference. Treat tools as options; the architecture decision comes before the bundler choice.
| Area | What to know | How to answer |
|---|---|---|
| Ownership | Team boundaries, deploy ownership, contracts | Split by business capability, not random component size |
| Composition | Route-level, component-level, iframe, Web Component, server composition | Pick composition based on isolation and UX needs |
| Runtime cost | Duplicate dependencies, waterfalls, error isolation | Explain how the shell loads, caches, and fails |
| Consistency | Design system, tokens, accessibility, analytics | Keep user experience coherent across teams |
| Data | Backend contracts, auth handoff, shared events, BFFs | Keep shared state rare, explicit, and versioned |
| Isolation | CSS leakage, globals, origins, security boundaries | Choose isolation based on trust and failure risk |
| Operations | Versioning, rollback, observability, incident ownership | Show how independent deploys stay safe |
Consider them when frontend ownership is already split across teams and release coordination is a bottleneck. They are most useful when product domains can be owned and deployed independently without breaking user journeys.
Good answer shape:
Common mistake: Choosing micro-frontends because the codebase feels large, before trying module boundaries, design-system discipline, or build improvements.
Common patterns include route-level composition, component-level runtime composition, iframes for isolation, Web Components for framework boundaries, and server-side composition. Each changes routing, performance, styling, and failure behavior.
Good answer shape:
Common mistake: Treating all micro-frontends as remote React components loaded by one host.
Module Federation lets separately built applications expose and consume modules at runtime. It can support independent deployment, but teams still need version contracts, shared dependency rules, error boundaries, and rollout strategy.
Good answer shape:
Common mistake: Assuming Module Federation automatically gives safe independent deploys.
Define shared dependency policy, compatible version ranges, and a design-system release process. Some teams share runtime dependencies; others isolate more strongly. The important part is measuring duplication and avoiding mismatched component behavior.
Good answer shape:
Common mistake: Sharing everything globally without ownership, or sharing nothing and shipping three versions of every core library.
The shell should own top-level navigation and route ownership. Each micro-frontend can own internal routes within its domain, but the URL contract, auth redirects, analytics, and not-found behavior need shared rules.
Good answer shape:
Common mistake: Letting every remote mutate global routing state in incompatible ways.
They can improve initial load if domain code is split and loaded only when needed. They can also hurt performance through runtime waterfalls, duplicate dependencies, larger shells, and slower cross-app boundaries.
Good answer shape:
Common mistake: Selling team autonomy while ignoring the user's loading and interaction cost.
Use error boundaries, remote load fallbacks, health checks, version rollback, and clear ownership. If one remote fails, the shell should preserve navigation and show a useful failure state for that domain.
Good answer shape:
Common mistake: Letting a failed remote crash the whole shell without a recovery path.
Use shared tokens, component standards, accessibility rules, analytics naming, content patterns, and review gates for cross-domain flows. Consistency is a governance problem as much as a package problem.
Good answer shape:
Common mistake: Assuming a shared component library alone prevents inconsistent product behavior.
Keep data ownership aligned with product ownership. The shell may provide session, locale, feature flags, and top-level auth handoff, but domain data should usually come from the owning micro-frontend or its backend contract. If multiple remotes need the same data, define the source of truth, cache rules, event contract, and compatibility window.
Good answer shape:
Common mistake: Putting every shared value into a global client store until the remotes are no longer independently deployable.
Micro-frontends do not have to be client-only. Content-heavy routes may use server-side or edge composition so crawlers and users receive meaningful HTML early. Interactive domains can still hydrate independently, but the team needs rules for data bootstrapping, streaming, caching, error boundaries, and what happens when one fragment fails.
Good answer shape:
Common mistake: Assuming Module Federation is the only implementation model, even for SEO-sensitive pages.
Use the isolation level that matches the trust boundary. Internal product remotes may share auth context through a narrow shell contract, while less-trusted or third-party UI may need iframe isolation, strict messaging, CSP, sandboxing, and separate origins. Validate cross-app events and avoid exposing long-lived tokens through globals.
Good answer shape:
Common mistake: Treating every micro-frontend as equally trusted because it appears inside the same product shell.
Practice by designing a migration, not a blank-slate architecture. Most micro-frontend interviews ask how to move a large app safely.
These answers sound prepared, but they do not give the interviewer enough signal. Rewrite them into specific engineering decisions.
| Weak answer | Why it falls short | Better direction |
|---|---|---|
| "Micro-frontends solve scaling" | They solve some team-scaling problems while adding runtime and governance cost | Name the exact organizational problem |
| "Each team can use any stack" | That can explode UX and dependency cost | Set constraints around browser support, tokens, telemetry, and shared libraries |
| "Deploy independently means no coordination" | Contracts still require coordination | Use versioned APIs, compatibility windows, and release notes |
| "Just split by component" | Component-level splitting can create coupling | Prefer route or domain boundaries unless shared embedding is justified |
Use these as spoken practice. A complete answer should be specific enough that another engineer can tell what you would inspect, change, and verify.
Avoid them when one team owns the UI, deployment coordination is simple, shared design is more important than independent releases, or the runtime cost would outweigh team autonomy. A good answer shows restraint: micro-frontends are an organizational tool with technical cost.
The shell usually owns routing, layout frame, global navigation, auth context, shared observability, error boundaries, and remote loading policy. It should not become a dumping ground for product logic. Keep the shell stable and boring.
Use shared tokens, accessible primitives, documented component APIs, versioning, visual regression, and migration guides. Teams can own product features independently while still sharing interaction standards and brand constraints.
Use contract tests for shared APIs and events, component tests inside each app, smoke tests for shell integration, and monitoring for remote load failures. Add rollback and compatibility windows because independent deployment does not remove integration risk.
Set a shared dependency policy, monitor bundles, avoid casual framework mixing, and plan upgrades. Module Federation can share dependencies, but runtime compatibility and version policy still need ownership.
| Level | What the answer should show |
|---|---|
| Mid-level | Understands composition choices, route ownership, shared dependencies, fallbacks, and the user cost of extra runtime loading |
| Senior | Can design a migration, define contracts, create testing and rollback strategy, and keep UX/accessibility consistent across teams |
| Staff/lead | Can decide whether micro-frontends are worth the organizational cost, set governance, and measure whether team autonomy improved delivery |
Checkout is a reasonable candidate because the business domain is clear and the release risk is high. The shell can own navigation, auth, and global layout while checkout owns its route, form states, payment provider integration, and error handling. The migration should keep the old route behind a flag, compare conversion and performance, and define rollback before traffic moves.
Treat remote load failure differently from render failure. If the remote entry cannot load, the shell should show a domain-specific fallback and log the version, route, and remote URL. If rendering throws, use an error boundary around the widget so the rest of the dashboard remains usable. In both cases, decide who is paged before the incident happens.
If three remotes ship different React or design-system versions, first measure bundle and runtime behavior. Then decide whether the drift is temporary during an upgrade, intentional isolation, or unmanaged duplication. The fix may be a shared dependency policy, not a bigger Module Federation config.
The shell should not accumulate domain logic just because multiple teams need it. Keep product rules, domain state, and feature-specific UI in the owning micro-frontend. The shell should stay stable: routing frame, auth/session handoff, global layout, observability, remote loading, and shared failure policy.
Use the smallest contract that preserves ownership. Route parameters, URL state, typed custom events, shared services, or backend APIs can work depending on the boundary. Avoid casual global stores that make independent deploys fictional.
Shared state should be rare and explicit. Session, locale, feature flags, and design tokens may be shell-provided. Domain state should stay with the owning app or server. If every remote needs to mutate the same client store, the boundary is probably wrong.
Test API schemas, shared events, route contracts, design-system versions, and shell integration separately. Add smoke tests for critical user journeys because independent deployment increases the chance that two correct pieces break together.
Use shared tokens and components for consistency, and use scoping strategies for isolation when teams ship independently. CSS Modules, Shadow DOM, naming rules, or build-time scoping can help, but design governance still matters.
Version remotes, keep compatibility windows, use feature flags where needed, and know whether rollback happens in the shell, remote, CDN, or deployment platform. A good answer names who owns the rollback before a production incident.
They can fragment accessibility if every team implements focus, modals, forms, and announcements differently. Shared primitives, review checklists, and cross-app smoke tests keep the product coherent for keyboard and assistive-technology users.
Measure deployment frequency, lead time, rollback frequency, integration incidents, bundle cost, user metrics, and developer onboarding. If team autonomy improves but user experience gets worse, the architecture is not a success yet.
Imagine a company has one large dashboard owned by four teams. Releases are slow because every team must coordinate in the same frontend repo. Micro-frontends might help, but only if the split follows ownership boundaries and the runtime cost is acceptable.
Use this sequence:
A strong answer says: "I would not split by arbitrary components. I would split by product domain, keep shared navigation in the shell, and require each remote to own its tests, monitoring, and fallback. I would also measure bundle duplication because micro-frontends can make the user experience worse if dependency policy is loose."
Do not sell micro-frontends as an automatic upgrade. The answer should show what problem they solve and what new problems they create.
Micro-frontends are a coordination tool. The interview answer should show when the coordination benefit is worth the runtime and governance cost.

Frontend web security interview questions test whether you can reason about browser boundaries. You should be able to explain where untrusted data enters, where it is rendered, which cookies are sent automatically, which origins can read responses, and which checks must happen on the server.
The best answers are scenario-based. A definition of XSS, CSRF, or CORS is only the first step. Interviewers want to know what you would inspect, what you would change, what tradeoff you would accept, and how you would verify that the fix works.
For technical accuracy, use official references as your source of truth: the OWASP Top 10 for broad application risk, the OWASP XSS Prevention Cheat Sheet, the OWASP CSRF Prevention Cheat Sheet, MDN Web Security, MDN CORS, MDN CSP, MDN Set-Cookie, and MDN postMessage. Interview-question lists are useful for prompts; official docs are better for the actual answer.
Most frontend security answers become clearer if you first name the boundary. Do not start with "make it secure." Start with what the browser, frontend, API, or server is allowed to trust.
| Boundary | Interviewer is testing | Good answer should include |
|---|---|---|
| User content to DOM | XSS, output context, safe sinks | Source, sink, execution context, escaping, sanitization when rich HTML is allowed |
| Other site to your API | CSRF, cookies, request side effects | Automatic cookie sending, server-side token or origin validation, no side effects on GET |
| Other origin to your frontend | CORS, same-origin policy | Scheme/host/port origin, preflight, credential rules, CORS is not auth |
| Browser storage to attacker script | Token theft, session handling | XSS risk, cookie attributes, refresh flow, short lifetimes, server enforcement |
| Third-party code to your page | Supply-chain and script risk | Data access, CSP, route scoping, SRI where possible, dependency review |
| UI permission to API permission | Authorization boundary | Frontend UX checks versus server-side enforcement, 401 and 403 behavior |
XSS happens when untrusted data is interpreted as executable code in a page. In frontend interviews, connect the answer to the exact rendering path: where the data came from, which DOM sink receives it, and which parsing context the browser uses.
A good answer:
innerHTML when the content should be text.Common mistake: saying React, Vue, or Angular makes XSS impossible. Framework escaping helps with text rendering, but unsafe HTML APIs, unsafe URL handling, third-party scripts, and server-rendered templates can still create XSS risk.
React escapes text by default, but React does not remove every XSS path. XSS can still happen through dangerouslySetInnerHTML, untrusted markdown or HTML renderers, direct DOM manipulation through refs, dangerous URL schemes passed to DOM APIs, third-party scripts, and HTML created outside React.
Interview-ready answer:
"I would first check whether the value is rendered as text or HTML. If it is plain text, React's escaping is usually the right path. If the product needs rich text, I would sanitize with an allowlist, validate link protocols, avoid dangerous DOM sinks, and add tests for stored payloads. I would also check CSP and any third-party scripts on that page."
Validation checks whether input has the expected shape. Encoding changes output so the browser treats it as data in the current context. Sanitization removes or rewrites unsafe parts of content that is intentionally allowed to contain markup.
Example:
https:// or an allowed app route.Common mistake: saying "sanitize all input" as a universal answer. For plain text, the safer answer is usually to render it as text. Sanitization is for cases where the product intentionally allows rich content.
A safe sink treats data as text or a controlled value instead of executable markup. Examples include textContent, form value, and framework text interpolation when used normally.
The caveat matters: a sink is safe only for the right context. Hardcoded safe attribute names are different from dynamically choosing an attribute name. A URL attribute is different from normal text. An interviewer will often follow up with innerHTML, href, markdown, or rich-text examples to check whether the answer survives detail.
CSRF tricks a logged-in browser into sending a state-changing request that includes the user's cookies. The attacker may not be able to read the response, but the server may still receive and process the request.
Defenses include:
SameSite cookies as defense-in-depth.Origin or Referer checks for sensitive state-changing requests.GET.Common mistake: thinking HttpOnly prevents CSRF. HttpOnly prevents JavaScript from reading a cookie; it does not stop the browser from automatically sending that cookie.
HttpOnly, Secure, and SameSite protect?Cookie attributes reduce different risks, so name the risk instead of calling a cookie "secure."
| Attribute | Browser behavior | Does not protect against |
|---|---|---|
HttpOnly | Prevents JavaScript from reading the cookie through document.cookie | Requests caused by injected script or automatic cookie sending |
Secure | Sends the cookie only over secure connections, except for defined localhost handling | XSS, CSRF, or broken authorization |
SameSite=Lax or Strict | Restricts when the cookie accompanies cross-site requests | Every CSRF case or same-site attacks |
SameSite=None; Secure | Allows cross-site cookie use while requiring secure transport | CSRF, misconfigured CORS, or missing authorization |
Also mention that cookie Path is not an access-control boundary. It controls when the browser sends a cookie; it should not be treated as protection from unauthorized reading.
CORS is a browser-enforced mechanism that controls whether frontend JavaScript from one origin may read a response from another origin. An origin is the scheme, host, and port.
Good answer:
OPTIONS request.Access-Control-Allow-Origin: *.Common mistake: debugging React code when the actual problem is the API's CORS headers. If a request works in Postman but fails in the browser, check browser security rules before rewriting components.
There is no universal answer. The right storage choice depends on the auth model, threat model, and product constraints.
| Option | Useful when | Main risk |
|---|---|---|
| HttpOnly Secure cookie | You want to reduce token theft by JavaScript | Needs CSRF defenses for cookie-auth flows |
| Memory | You want less persistence after refresh or XSS cleanup | Harder refresh flow and tab coordination |
sessionStorage | You need tab-scoped persistence | Readable by injected scripts in that tab |
localStorage | Simpler persistence for bearer-token apps | Readable by injected scripts and often overused for long-lived tokens |
An interview-ready answer names the tradeoff: cookies reduce JavaScript token theft but require CSRF thinking; browser storage can simplify bearer-token flows but makes XSS theft more damaging. Short lifetimes, rotation, least privilege, refresh strategy, and server-side revocation matter more than declaring one storage location always safe.
CSP lets a site restrict which resources a page can load and which scripts can execute. It can reduce XSS impact by blocking inline scripts, javascript: URLs, unexpected script sources, and dangerous APIs such as eval() when the policy is strict enough.
A good answer:
unsafe-inline unless there is a temporary migration reason.Content-Security-Policy-Report-Only before enforcing a risky policy.Common mistake: adding a loose policy with unsafe-inline and many wildcard sources, then treating the page as protected.
postMessage be used safely?window.postMessage lets a window communicate with another window it references, such as a popup, parent, or iframe, even across origins. Workers and MessagePort objects have related messaging APIs, but targetOrigin and event.origin checks here apply specifically to window-to-window messaging.
Use a specific targetOrigin when sending. On receive, validate event.origin, check event.source when you expect one particular window, validate the message shape, and avoid sending secrets through generic message channels. If the message changes auth state, payment state, or account data, treat the receiver like any other trusted integration and document the expected source, origin, and schema.
Common mistake: using * for sensitive messages or trusting every message event because it arrived in the browser.
Frontend checks are useful for UX and accidental misuse, but they are not authorization. The frontend can hide irrelevant actions, disable impossible actions, validate form shape, and show useful auth recovery states. The server must enforce permissions and input validation for every sensitive operation.
Good answer:
401 as unauthenticated and 403 as authenticated but not allowed.Clickjacking tricks a user into interacting with a page framed by another site. The modern answer is to control who can embed the page, usually with the CSP frame-ancestors directive. Older systems may still use X-Frame-Options.
When your app embeds other content, review iframe sandbox permissions, allowed origins, postMessage contracts, and whether sensitive pages can be framed at all.
Common mistake: talking only about CORS. CORS controls browser script access to responses; it does not decide whether another site can frame your page.
Frontend code often runs with access to page content, user interactions, tokens in browser storage, and API responses. A third-party script, tag manager, or compromised dependency can therefore expand the damage from a single bug.
Good answer:
No real secret should be shipped to the browser. Anything in JavaScript bundles, source maps, HTML, network requests, local storage, or devtools-visible config should be treated as public.
Public API keys can be acceptable when the provider expects browser use and enforces restrictions server-side, such as allowed origins, scopes, quotas, and abuse controls. Private API keys, signing secrets, database credentials, and service-role tokens belong on the server.
For OAuth in SPAs, mention PKCE. PKCE improves public-client authorization flows, but it does not turn a browser into a place where client secrets can be hidden.
Service workers can intercept requests, cache responses, and keep behavior active beyond the current page. Review their scope, update strategy, cached sensitive data, logout behavior, and whether an old worker can serve stale authenticated content.
A good answer separates caching bugs from authorization bugs. Clearing UI state on logout is not enough if a service worker can still return sensitive cached responses.
Definitions help, but scenarios show judgment. Practice these as two-minute spoken answers.
| Scenario | What a good answer should reason through |
|---|---|
| User profile bio supports rich text | Plain text versus rich text, allowlist sanitizer, URL protocol validation, safe render path, stored XSS tests, CSP backup |
| Banking transfer uses cookie auth | CSRF token or equivalent server validation, SameSite, Origin/Referer checks, re-auth for high-risk action, no side effects on GET |
Dashboard calls api.example.com from app.example.com | Exact allowed origin, credential mode, preflight behavior, server auth, non-browser client caveat, 401/403 UI |
| Product wants a tag manager on every page | Data exposure, page scoping, CSP impact, consent rules, performance cost, incident response if the script is compromised |
| Admin button is hidden for normal users | UI convenience, server authorization, direct API test, 403 handling, audit logging |
| SPA uses bearer tokens | Storage tradeoff, XSS impact, refresh token flow, rotation, logout, multi-tab behavior |
| Comment preview renders markdown | Sanitized output, unsafe URLs, markdown library defaults, stored payload tests, final DOM inspection |
| App embeds a payment iframe | iframe origin, sandbox permissions, postMessage schema, no wildcard target for sensitive messages |
"Users can write comments with links, bold text, and lists. How would you prevent XSS?"
Start by asking whether the product needs plain text, markdown, or HTML. If plain text is enough, render it as text and avoid HTML. If rich text is required, define an allowlist of tags and attributes, sanitize on a trusted processing path, validate URL protocols, and avoid dangerous DOM sinks in the client.
Then verify the final DOM, not only the stored string. Test stored payloads, reflected payloads, image error handlers, unsafe links, malformed HTML, and post-sanitization mutations. CSP should reduce impact if something slips through, but it is not the primary fix.
Interview-ready answer:
"React escaping handles plain text, but rich text changes the problem. I would keep the allowed formatting small, sanitize with an allowlist, validate links so
javascript:cannot become anhref, render through a controlled component, and test stored comments in the rendered DOM. I would also check which third-party scripts run on the page because they change the impact of an XSS bug."
"The frontend at
https://app.example.comcallshttps://api.example.com/mewith cookies. It works in Postman but fails in the browser."
Postman is not enforcing browser CORS rules. The server must return CORS headers that allow the exact frontend origin. If credentials are included, the frontend request must use the right credential mode, and the server cannot rely on a wildcard origin for a credentialed response.
The API still needs authentication and authorization. CORS decides whether browser JavaScript may read the response; it does not prove that the user is allowed to access /me.
Interview-ready answer:
"I would check the browser console and network panel first. If credentials are involved, I would verify the request credential mode, the exact
Access-Control-Allow-Origin,Access-Control-Allow-Credentials, the preflight response, and the cookie attributes. After that, I would still check the API auth path because CORS is not authorization."
"The UI hides the Delete user button for non-admins. Is that enough?"
No. Hiding the button reduces accidental clicks and keeps the UI cleaner, but it does not enforce permission. A user can call the API directly, replay a request, use another client, or inspect the network request from an admin session.
The server should verify authorization for the delete action. The frontend should handle 403 deliberately, avoid leaking sensitive details, and keep the UI state consistent after a denied request.
Interview-ready answer:
"The frontend should hide the button for UX, but the API must enforce the permission. I would test by calling the endpoint directly as a non-admin and expecting
403. The UI should show a clear not-allowed state and not reveal extra data about the target user."
| Level | What the answer should show |
|---|---|
| Fresher | Can define XSS, CSRF, CORS, cookies, and why frontend validation is not authorization |
| Mid-level | Can identify unsafe rendering paths, token-storage tradeoffs, credentialed CORS risks, and 401/403 UI states |
| Senior | Can design layered defenses, review third-party script risk, coordinate frontend/backend responsibilities, and choose verification steps |
| Staff/lead | Can turn repeated mistakes into platform defaults, CSP rollout plans, dependency policy, review checklists, observability, and incident playbooks |
| Weak answer | Why it falls short | Better direction |
|---|---|---|
| "Use HTTPS" | HTTPS protects transport, not DOM XSS or broken authorization | Name the attack and the matching control |
| "React prevents XSS" | React escapes text by default, but unsafe sinks still exist | Explain the source, sink, and rendering context |
| "Store JWT in localStorage because it is easy" | XSS can read it | Discuss storage tradeoffs, token lifetime, refresh flow, and CSRF if cookies are used |
| "Disable CORS" | CORS is a browser access-control mechanism, not a bug to bypass | Fix the server policy and credential model |
| "Hide the button" | UI hiding is not authorization | Enforce permission on the server and handle 403 in the UI |
| "Add CSP and we are safe" | CSP is defense-in-depth | Keep safe rendering and sanitization as the primary XSS controls |
| "Sanitize input" | Too vague and often wrong for plain text | Say whether you validate input, encode output, or sanitize allowed HTML |
| Topic | Questions worth practicing |
|---|---|
| XSS | What are reflected, stored, and DOM XSS? What are unsafe sinks? How do HTML, attribute, URL, CSS, and JavaScript contexts differ? |
| React and frameworks | Why does React escaping help? How can dangerouslySetInnerHTML become risky? How should untrusted markdown be rendered? |
| Sanitization | When do you encode versus sanitize? Why is an allowlist safer than blocking known bad strings? What can go wrong after sanitization? |
| CSRF | Why do cookies make CSRF possible? How do CSRF tokens, SameSite, Origin checks, and avoiding side effects on GET work together? |
| Cookies | What do HttpOnly, Secure, SameSite, expiration, prefixes, and cookie scope protect against? What do they not protect against? |
| CORS and origins | What is an origin? What triggers a preflight? What changes when credentials are included? Why does CORS not replace authorization? |
| Tokens | Should tokens live in cookies, memory, sessionStorage, or localStorage? How do refresh tokens change the risk? |
| CSP | What does CSP block? What are nonces and hashes? Why can unsafe-inline weaken a policy? How do report-only rollouts help? |
| Trusted Types | What problem does Trusted Types reduce? Which dangerous DOM APIs does it affect? Why is migration work needed? |
| Browser APIs | How do postMessage, iframes, service workers, Web Storage, clipboard APIs, and Web Workers create review points? |
| Frontend permissions | What should the UI hide, what should it disable, and what must the server enforce? How should 401 and 403 states differ? |
| Third-party code | How do analytics, tag managers, embeds, npm packages, and CDN scripts change the damage from a frontend bug? |
| Secrets | Which values can be public in a browser bundle? Which values must stay on the server? Why does PKCE not hide a client secret? |
| Testing | How do you review a PR for XSS risk? What payloads would you test? What belongs in automated tests versus manual review? |
| Incident thinking | If a script was compromised, what data could it read, what actions could it trigger, and what would you rotate or revoke? |
Practice by drawing the boundary first: browser, frontend code, API, cookie jar, third-party script, user-controlled input, storage, or server permission check.
For each answer, name the browser or server boundary and one verification step. For example, an XSS answer should end with a rendered-DOM test; an authorization answer should end with a direct API call using an unprivileged account.

Web performance interview questions test whether you can connect a slow user experience to the browser work causing it. The best answers move from metric to likely cause, then to a measurement plan and a fix.
Do not prepare by memorizing one list of tricks. Practice explaining how loading, rendering, JavaScript, network priority, images, fonts, and user interactions affect the page.
Use current metric names. web.dev Web Vitals lists LCP, INP, and CLS as the current Core Web Vitals, with the 75th percentile target across mobile and desktop. web.dev Learn Performance and MDN critical rendering path are useful references for the browser pipeline behind those numbers.
| Area | What to know | How to answer |
|---|---|---|
| LCP | Largest contentful element, TTFB, render-blocking CSS, image priority | Name the likely LCP element and the first measurement you would check |
| INP | Input delay, event handlers, long tasks, rendering after input | Separate lab TBT from field INP and explain main-thread work |
| CLS | Image dimensions, ads, late fonts, inserted content | Find the moving element and remove unstable layout |
| Rendering | HTML parsing, CSSOM, layout, paint, compositing | Connect a UI symptom to the browser stage that can cause it |
| JavaScript cost | Bundle size, hydration, parsing, execution, memory | Explain what code can be delayed, split, or moved off the critical path |
| Tooling | DevTools, Lighthouse, WebPageTest, CrUX, RUM | Choose the tool based on whether you need lab diagnosis or field impact |
The current Core Web Vitals are LCP for loading, INP for interactivity, and CLS for visual stability. Good thresholds are LCP at 2.5 seconds or less, INP at 200 milliseconds or less, and CLS at 0.1 or less. Assess the 75th percentile of page loads separately for mobile and desktop; a combined number can hide a slow device segment.
Good answer shape:
Common mistake: Listing FID as the current Core Web Vital, forgetting the thresholds, or treating a single Lighthouse run as the whole performance story.
Start by finding the LCP element in field or lab tooling. If it is an image or hero text, check server response time, render-blocking CSS, image size, image priority, font loading, and whether client-side rendering delays the element.
Good answer shape:
Common mistake: Shrinking the JavaScript bundle before checking whether the LCP element is blocked by server latency or an unprioritized image.
INP is based on real interactions. Lighthouse can use lab signals such as Total Blocking Time, but it cannot reproduce every user click, device, extension, background task, and data state. Use Lighthouse for early diagnosis and field data for final confidence.
Good answer shape:
Common mistake: Saying the page is fine because Lighthouse is green while users still report delayed clicks.
It is the work the browser must do before pixels appear: parse HTML, discover resources, build the DOM and CSSOM, run blocking scripts when needed, calculate layout, paint, and composite. The interview value is knowing which resources block which step.
Good answer shape:
Common mistake: Repeating the pipeline but not explaining how a stylesheet, script, or font changes the user experience.
Measure the interaction first. Then reduce synchronous work in the handler, split large updates, avoid unnecessary rerenders, move expensive non-UI work to a worker when appropriate, and keep the visual response close to the input.
Good answer shape:
Common mistake: Adding debounce to every input without understanding whether the delay is network, rendering, or CPU work.
Use code splitting when code is not needed for the first meaningful task: route-level admin screens, heavy charts, editors, maps, or rarely used modals. Do not split tiny modules so aggressively that the page becomes a chain of extra requests.
Good answer shape:
Common mistake: Treating smaller initial JavaScript as automatically better when the app now stalls on many late chunks.
Images affect bytes, decoding, layout stability, and LCP. A good answer covers dimensions, responsive srcset, modern formats, lazy loading below the fold, priority for the LCP image, and avoiding layout shifts.
Good answer shape:
Common mistake: Lazy-loading the hero image that is likely to become the LCP element.
Translate the metric into user pain: slow first content, delayed tap response, or moving layout. Then propose a small fix with a measurable target and a product caveat, such as deferring a carousel or reducing a third-party script.
Good answer shape:
Common mistake: Saying "performance is important" without identifying the user task or business tradeoff.
Use the tool that matches the question. Lighthouse is useful for a repeatable lab snapshot. Chrome DevTools Performance and Network panels help inspect traces, long tasks, waterfalls, layout shifts, and resource priority. WebPageTest is useful for network waterfalls, filmstrips, and device/location testing. CrUX and RUM show whether real users are affected.
Good answer shape:
Common mistake: Saying "I would run Lighthouse" for every problem, including interaction bugs that only happen for real users.
Resource hints change when the browser discovers or prioritizes work. preconnect can warm up a critical third-party origin, preload can fetch a critical resource earlier, and fetchpriority="high" gives the browser a priority hint for the likely LCP image. Classic scripts use defer when order matters and async when it does not; module scripts are deferred by default. Route-level loading can keep code for later interactions off the initial path.
Good answer shape:
Common mistake: Preloading many files because it sounds faster, then making the real critical path noisier.
They reduce network cost, but they do not fix every performance problem. A CDN can reduce latency for static assets and cached HTML. HTTP caching can avoid repeat downloads. Compression reduces transfer size. These help loading metrics, but INP may still be poor if the page ships too much JavaScript or does expensive work after interaction.
Good answer shape:
Common mistake: Saying "put it on a CDN" without checking whether the bottleneck is network, server, rendering, or JavaScript execution.
Profile the interaction or update first. Look for a large rerender, expensive derived data, layout work, too many DOM nodes, heavy third-party components, or hydration cost. Then reduce the render scope, move expensive computation out of the urgent path, virtualize long lists when DOM size is the problem, and use memoization only when the profile shows repeated work.
Good answer shape:
Common mistake: Adding memo, useMemo, or useCallback everywhere before proving rerenders are the bottleneck.
Practice performance answers as diagnosis drills. Start with one symptom, ask what you would measure, then name a fix only after the likely cause is clear.
Answer these aloud as diagnosis exercises. Name the affected metric or user-visible delay, the trace or field signal you would inspect, and how you would verify the change after release.
Start by identifying the LCP element in field data or a trace. Then separate causes: slow TTFB, render-blocking CSS, late image discovery, missing image dimensions, slow image bytes, font blocking, client-side rendering delay, or long main-thread work. A good answer includes the fix order: measure first, improve the biggest bottleneck, ship one change, and compare field data after release.
Bundle size is only one input. INP can be hurt by expensive event handlers, synchronous validation, layout thrashing, rendering too many nodes, heavy third-party code, or state updates that cause a large subtree to rerender. Explain how you would capture a performance trace, find long tasks around the interaction, and move or split work without breaking the interaction.
Reserve dimensions for images, ads, embeds, and recommendation modules before they load. Avoid inserting content above the current viewport, avoid late font swaps that move text, and keep skeleton dimensions close to final dimensions. Mention CLS verification because visual stability problems are often introduced by asynchronous content.
Code splitting helps when it removes non-critical code from the first path. It hurts when critical UI waits for too many chunks, when shared dependencies are duplicated, or when route transitions show blank states because loading boundaries were not designed. A senior answer discusses both network waterfall and user experience.
Prioritize user-visible paths and measured bottlenecks. For example, improve LCP on landing/product pages, INP on search/forms/editors, and CLS on content feeds. Avoid optimizing everything equally. Tie each task to a metric, affected route, expected impact, risk, and verification method.
Interviewers do not expect the same answer from every level. Use this table to calibrate how much depth to add.
| Level | What the answer should show |
|---|---|
| Fresher | Knows the current metrics, can explain loading versus interaction versus layout shift, and can use DevTools or Lighthouse to start debugging |
| Mid-level | Can identify the likely bottleneck, use traces and field data, fix images/scripts/rendering issues, and explain the tradeoff of one optimization |
| Senior | Can prioritize across routes, coordinate backend/frontend ownership, set budgets, instrument RUM, and prevent regressions through release process |
| Staff/lead | Can connect performance to product strategy, architecture, third-party governance, design-system defaults, and observability across teams |
LCP can be split into server response time, resource load delay, resource load duration, and element render delay. That split prevents vague answers. If the image bytes are small but the browser discovers the image late, compressing it again will not fix the main issue. If the element is ready but render is delayed by hydration or CSS, prioritize the render path instead of the asset.
Total Blocking Time is a lab metric that estimates main-thread blocking between FCP and Time to Interactive. INP is a field metric based on real interactions and the next paint after those interactions. TBT can help during debugging, but a good answer says field INP is the user-impact metric.
CSS can block rendering, trigger layout work, create expensive selectors in very large documents, cause layout shifts when late styles arrive, and animate properties that require layout or paint. The practical answer is not "write less CSS"; it is "ship critical CSS carefully, avoid unstable layout, and animate compositor-friendly properties when possible."
Hydration can delay interactivity when the browser must download, parse, execute, and attach event handlers before a UI behaves correctly. A strong answer mentions partial hydration, islands, server components, progressive enhancement, or reducing client-only work, but only after explaining the actual bottleneck.
Tie budgets to routes and user tasks. A useful budget might track LCP image size, JavaScript bytes for the initial route, long tasks during key interactions, CLS from dynamic modules, and third-party script cost. Budgets should have owners and exceptions; otherwise they become ignored dashboards.
Measure its network cost, main-thread cost, and route coverage. Then ask whether it can load after consent, after interaction, on fewer routes, or through a lighter integration. If the business needs the script, isolate it, monitor regressions, and make the owner explicit.
Virtualization helps when the DOM size makes rendering or interaction slow. It adds complexity around keyboard navigation, screen-reader behavior, dynamic heights, scroll restoration, and testing. A good answer says to profile first and keep accessibility behavior intact.
Use the same segment that showed the problem: route, device class, geography, network, and release window. Compare field data after enough traffic has passed, watch error and conversion metrics, and keep a rollback path if the fix changes loading order or user-visible behavior.
Imagine an interviewer says a product detail page feels slow on mobile. A shallow answer jumps straight to "compress images" or "use lazy loading." A better answer asks what users experience and which metric is failing. If the page is slow before the main product image appears, start with LCP. If the page appears but taps lag, start with INP. If content jumps as reviews or recommendations load, start with CLS.
Use this sequence:
The final answer should sound like an investigation plan. For example: "I would first confirm whether LCP or INP is the problem. If LCP is poor and the LCP element is the hero image, I would check discovery, priority, dimensions, and bytes. If those are fine, I would look at TTFB and render-blocking resources. I would verify locally with a trace and later with field data." That answer gives the interviewer a real debugging path.
Avoid listing every performance trick. Interviewers are looking for sequencing and diagnosis. The best answer is usually smaller and more precise than a long checklist.
A good web performance interview answer is not a bag of tricks. It is a measured path from user symptom to browser cause to fix.

GraphQL interview questions test whether you can turn a typed API contract into a reliable UI. For frontend roles, the interviewer is not only checking syntax. They want to hear how a query maps to a screen, how mutations update visible state, how partial data and errors behave, and how client choices affect backend cost.
The best answers do not say "GraphQL is better than REST." They explain where GraphQL helps, where REST is simpler, and what can go wrong when a flexible query language reaches production.
Use GraphQL Learn, GraphQL over HTTP, GraphQL response format, GraphQL security guidance, GraphQL pagination, GraphQL caching, and Apollo Client cache docs for current terminology.
If REST comparison is your weak spot, pair this with REST API Interview Questions for Frontend Devs and practice explaining the same product screen in both API styles.
| Area | What to know | How to answer |
|---|---|---|
| Schema | Object types, fields, nullability, enums, interfaces | Explain how schema shape becomes frontend data shape |
| Operations | Queries, mutations, subscriptions, variables, fragments | Show how operations map to user actions |
| Caching | Normalized cache, IDs, field policies, invalidation | Explain what changes after a mutation |
| Errors | Partial data, errors, network failures | Separate GraphQL execution errors from transport errors |
| Security and cost | Auth, authorization, depth, persisted documents | Say GraphQL does not remove backend authorization |
Backend interviews may spend more time on resolver implementation, schema governance, and database access. Frontend interviews usually probe the client contract:
If your answer stays at the definition level, it will sound memorized. Tie every concept to a page, component, request, mutation, or production failure.
GraphQL gives the frontend a typed schema and lets the client select the fields needed for a view. That can reduce overfetching and underfetching, especially when one screen needs data that would otherwise require several REST endpoints.
The tradeoff is that GraphQL shifts work into schema design, resolvers, caching, authorization, and query cost control. A good answer says GraphQL improves the data contract when the graph is designed well, not that it automatically makes every request faster.
Common mistake: Saying "GraphQL prevents overfetching" without mentioning that resolvers can still fetch too much or do expensive backend work.
A query reads data. A mutation represents a write or state-changing operation. A subscription streams updates over a long-lived connection such as WebSocket or server-sent events.
For frontend work, the distinction matters because each operation drives different UI states. A query needs loading, empty, success, partial-error, and retry states. A mutation needs pending, optimistic, success, rollback, validation-error, and duplicate-submit handling. A subscription needs connection, reconnect, stale-event, and auth-expiry behavior.
Interview-ready add-on: A mutation should usually return the changed object or payload the UI needs to update the cache. Returning only success: true often forces a refetch or leaves the client guessing.
A schema is the typed contract exposed by a GraphQL server. It defines which operations are available, which object types exist, which fields can be selected, what arguments are accepted, and which values can be null.
For a frontend engineer, the schema is a working client contract. It shapes generated types, component fragments, form input objects, error handling, and the long-term compatibility of old clients.
type Product {id: ID!name: String!price: Money!imageUrl: String}type Query {product(id: ID!): Product}
Common mistake: Treating the schema as a JSON response shape. The schema is a contract with validation, nullability, field ownership, and evolution rules.
Scalars are leaf values such as String, Int, Float, Boolean, and ID. Object types contain fields that can be selected. Enums limit a value to a known set of names. Input types describe structured arguments for queries and mutations.
enum SortDirection {ASCDESC}input ProductFilterInput {query: StringinStockOnly: Boolean}
Input types are separate from output object types because output types can contain computed fields, relationships, interfaces, unions, or resolver-only behavior that does not make sense as input.
String! means the field must not be null. [Product!]! means the list itself is not null and none of its items are null. Nullability is a product contract because it decides whether the UI can show partial data or must treat the parent object as unavailable.
If a non-null field resolves to null during execution, GraphQL adds an error and bubbles the null up to the nearest nullable parent. Overusing ! can make a small resolver failure blank out a larger part of the response.
Good answer shape: Explain the UI consequence. A nullable imageUrl lets the product card render a placeholder. A non-null price says the card is not useful without price, so failure should be more visible.
Variables keep the GraphQL document stable while passing dynamic values separately. They are validated by type, easier to reuse, safer than string-building, and friendlier to operation caching or persisted documents.
query ProductDetails($id: ID!) {product(id: $id) {idnameprice {amountcurrency}}}
Variables:
{ "id": "product_123" }
Common mistake: Building query strings with template literals and user input. That makes validation, caching, escaping, and operation tracking harder.
Aliases rename response keys. They are useful when querying the same field more than once with different arguments, or when the UI needs two differently named versions of the same schema field.
query CompareProducts {left: product(id: "product_1") {idname}right: product(id: "product_2") {idname}}
Without aliases, both selections would produce the same product key and conflict.
Fragments define reusable field selections. They are most useful when components own their data needs, because the fragment can live near the component that renders those fields.
fragment ProductCardFields on Product {idnameimageUrlprice {amountcurrency}}
Fragments improve consistency, but they can also hide overfetching if teams create one large shared fragment for every screen. A useful fragment represents what a component actually renders.
Directives annotate parts of a GraphQL document or schema to change behavior. Common built-in directives include @include, @skip, and @deprecated.
query ProductDetails($id: ID!, $showReviews: Boolean!) {product(id: $id) {idnamereviews @include(if: $showReviews) {ratingbody}}}
For frontend interviews, explain directives as part of the data contract. @include can conditionally fetch a field, while @deprecated tells clients a schema field is being phased out.
A resolver is the server-side function that returns the value for a schema field. It may read from a database, call another API, compute a value, or delegate to another service.
Frontend engineers are not always expected to implement resolvers, but they should understand the cost boundary. One GraphQL request can trigger many resolver calls behind the scenes, so a harmless-looking nested query can be expensive.
const resolvers = {Query: {product: (_parent, args, context) => {return context.productService.findById(args.id);},},};
Good answer shape: Mention the usual resolver inputs: parent value, field arguments, request context, and execution info. Then connect that to auth, batching, and observability.
A GraphQL response can contain data and errors together. Request errors, such as syntax, validation, or variable-coercion errors, stop execution and do not include data. Execution errors happen at a response position and may produce partial data; older GraphQL material often calls these field errors.
{"data": {"product": {"id": "product_123","name": "Everyday Backpack","inventory": null}},"errors": [{"message": "Inventory service unavailable","path": ["product", "inventory"]}]}
The UI should not automatically turn every GraphQL error into a full-page failure. If the missing field is non-critical, render the safe data and show a local fallback. If the failed field is essential, show a stronger error state.
Authentication identifies the caller. Authorization decides whether that caller can read or mutate a resource or field. Both must be enforced on the server side, usually in resolvers or the service layer behind them.
Do not rely on hiding fields or buttons in the UI. A user can still send a GraphQL operation manually. The frontend can improve UX by hiding unavailable actions, but the server must remain the source of truth.
Good answer shape: Mention authentication headers or cookies, per-resource authorization, consistent error behavior, and audit logging for sensitive mutations.
GraphQL does not define client caching by itself. Many clients normalize objects by type name and ID, then store fields separately so different queries can reuse the same entity.
For example, Apollo Client can identify an object using __typename plus id by default. That means a product returned by a list query and a product returned by a detail query can point to the same cache entity.
Cache correctness depends on stable IDs, selected fields, field arguments, pagination merge policy, and mutation responses. If a mutation response omits the changed object's ID, the client may not know which cached entity to update.
A mutation should return enough data for the client to update the UI safely. That might be the changed entity, affected list metadata, aggregate counts, or a domain-specific payload with validation errors.
mutation UpdateCartLine($input: UpdateCartLineInput!) {updateCartLine(input: $input) {cart {idtotalItemssubtotal {amountcurrency}}line {idquantity}userErrors {fieldmessage}}}
Returning only success: true is weak because the frontend still needs to refetch, guess, or manually patch state without enough information.
Optimistic UI updates the interface before the server confirms the mutation. It works well when the operation is likely to succeed and the rollback behavior is clear.
For a cart quantity change, the frontend can show the new quantity immediately, disable repeated clicks while the mutation is pending, and roll back if the server rejects the change because of stock, price, or permission rules.
What to say in interviews: Name the visible states: optimistic value, pending indicator, duplicate-submit protection, rollback, validation error, and final cache consistency.
Use cursor-based pagination for feeds and changing lists when possible. The connection pattern returns edges, node, cursor, and pageInfo, which lets the UI request the next page without relying on unstable offsets.
query ProductList($first: Int!, $after: String) {products(first: $first, after: $after) {edges {cursornode {idname}}pageInfo {hasNextPageendCursor}}}
The frontend still needs to dedupe by stable ID, preserve scroll position, handle empty states, and avoid appending stale pages after filters change.
N+1 happens when resolving a list triggers one extra backend call per item. The frontend sees one GraphQL request, but the server may perform many database or service calls.
Example: a products query returns 40 products, and each product's seller field calls the seller service separately. That becomes one call for the list plus 40 calls for sellers.
Common fixes include batching, request-scoped caching, joining data earlier, or using loader patterns. A frontend engineer should know enough to ask whether expensive fields are batched and whether resolver timing is observable.
Use layered demand control. For first-party clients, trusted documents can allow only known operations in production. For broader APIs, use pagination, depth limits, breadth or alias limits, batch limits, query complexity analysis, rate limits, timeouts, and monitoring.
Turning off introspection is not enough. It can reduce schema discoverability in non-development environments, but it does not replace authorization, input validation, trusted documents, or operation limits.
Interview-ready answer: "I would control both who can call the API and how expensive each operation can be."
Persisted documents store approved GraphQL operations on the server and let clients send an operation ID, often a hash, instead of the full document. Trusted documents go further by treating those stored operations as an allowlist for first-party production clients.
They can improve security and operations because the server can reject unknown arbitrary documents, track known operation names, and reason about query cost ahead of time.
They do not remove authorization. A known operation can still request data the current user should not see unless the server checks permissions during execution.
Introspection lets clients query the schema itself. It powers tools, autocomplete, documentation, and code generation.
Disabling introspection can reduce schema discoverability for a private first-party API, but it also removes standard tooling and is not a security boundary. Public GraphQL APIs often leave it enabled. In either case, production security must rely on authorization, trusted documents where appropriate, demand control, and safe error messages.
GraphQL can support file uploads through conventions, but uploads often fit better as direct-to-storage or REST-style flows. The frontend concern is not only "can GraphQL upload a file?" It is progress, cancellation, retry, size limits, auth, virus scanning, and what happens if metadata succeeds but upload fails.
A practical answer says: use GraphQL for metadata and signed upload coordination when that keeps the API clean, but do not force large binary transfer through GraphQL just to be consistent.
Use a custom scalar when a value needs domain-specific serialization or validation, such as DateTime, URL, EmailAddress, or JSON.
The tradeoff is that clients need to know the runtime format. A schema can say DateTime, but frontend code still needs documentation or generated scalar mappings that explain whether the value is an ISO string, number, or custom object.
An interface defines shared fields that multiple object types must implement. A union says a field may return one of several object types without requiring shared fields.
Use an interface when the types share a real contract:
interface Node {id: ID!}
Use a union for heterogeneous results such as search:
union SearchResult = Product | Article | Brand
In frontend code, both usually require checking __typename before rendering type-specific fields.
Prefer additive schema evolution. Add new fields, deprecate old fields, track operation usage, migrate clients, and remove fields only after the compatibility window.
Avoid changing a field's meaning in place. That is worse than removing it because old clients may keep working syntactically while showing wrong data.
Whole endpoint versioning can exist, but it is usually a last resort for major compatibility breaks. The normal GraphQL path is schema evolution through additive changes and deprecation.
Generated types catch mismatches between operations and frontend code. They help when a selected field can be null, when a union needs narrowing, or when a mutation input changes.
They can hurt if teams treat generated types as architecture. Codegen cannot fix a vague schema, unstable IDs, unsafe nullability, or a mutation that returns too little data. It is a guardrail, not a replacement for schema ownership.
Subscriptions keep a long-lived connection open and push updates when server-side events happen. They are useful for chat, notifications, live dashboards, collaborative editing signals, and other flows where the user benefits from immediate updates.
Avoid subscriptions for data that can be refreshed on navigation, loaded on demand, or polled cheaply. Subscriptions add connection state, auth renewal, reconnect logic, ordering concerns, and server resource cost.
GraphQL is not tied to one transport, but HTTP is commonly used for queries and mutations. Many GraphQL APIs send operations to a single endpoint such as /graphql. Servers commonly support POST, and some support GET for queries when it is safe and useful for caching.
Do not assume HTTP status codes carry every application outcome. A well-formed operation that executes can return HTTP 200 and still contain GraphQL execution errors alongside partial data. Parse or validation failures and transport failures use different status handling, so the client must inspect both the HTTP result and the GraphQL response body.
REST can be simpler for file downloads, uploads, public cacheable resources, small CRUD APIs, webhook-style integrations, and teams without schema governance. REST also maps naturally to HTTP caching and resource URLs.
That does not make GraphQL bad. It shows judgment. Choose GraphQL when the product has multiple clients, nested data needs, strong schema benefits, or frontend screens that suffer from overfetching, underfetching, and endpoint coordination.
Start at the user-visible symptom, then split the problem:
A good answer does not blame GraphQL first. It follows the request from component to client cache to network to resolver execution to render.
Choose one screen with clear ownership and measurable behavior. Keep the existing REST flow as a reference, design the GraphQL query around the fields the screen actually renders, generate types, handle loading and error states, compare response size and latency, and verify analytics or logs before removing the old path.
Do not migrate every endpoint just because GraphQL is available. A safe migration proves that the schema, cache, authorization, and observability work for one product flow first.
These examples are short on purpose. In an interview, explain the tradeoff around each one instead of only reading the syntax.
fragment ProductCardFields on Product {idnameimageUrlprice {amountcurrency}}query ProductGrid($query: String!, $first: Int!, $after: String) {products(filter: { query: $query }, first: $first, after: $after) {edges {cursornode {...ProductCardFields}}pageInfo {hasNextPageendCursor}}}
What this shows: stable variables, component field ownership, cursor pagination, and a response shape the UI can merge page by page.
mutation SaveProduct($input: SaveProductInput!) {saveProduct(input: $input) {product {idisSavedsavedCount}userErrors {fieldmessage}}}
What this shows: the mutation returns the changed entity and fields the UI needs. The client can update a product card, detail page, and saved counter without guessing.
{"data": {"product": {"id": "product_123","name": "Everyday Backpack","recommendations": null}},"errors": [{"message": "Recommendation service timed out","path": ["product", "recommendations"]}]}
What this shows: the product page can render core product data while showing a fallback for recommendations. The answer should depend on whether the failed field is critical.
Use scenarios because they reveal whether the concept survives messy product work. Try answering each prompt in two minutes, then name what you would inspect before choosing a solution.
| Scenario | What a good answer should reason through |
|---|---|
| A feed query gets slower as users follow more accounts | Resolver cost, pagination, batching, query limits, cache policy, and backend indexes |
| A field cannot be removed because old clients use it | Deprecation, schema evolution, operation tracking, and migration windows |
| A frontend needs partial data with errors | GraphQL response shape, nullable fields, error handling, and UI fallback states |
| A public GraphQL endpoint is abused | Trusted documents, auth, rate limiting, cost analysis, and monitoring |
| A mutation updates several visible screens | Return shape, optimistic update, cache write, invalidation, rollback, and refetching |
Each answer below hides the schema, client, or failure decision the interviewer needs to hear. Replace the slogan with the concrete GraphQL tradeoff.
| Weak answer | Why it falls short | Better direction |
|---|---|---|
| "GraphQL is always better than REST" | The tradeoff depends on clients, caching, team ownership, and server cost | Explain when GraphQL helps and when REST is simpler |
| "No overfetching means no performance problem" | Resolvers can still do expensive work | Mention demand control and backend query planning |
| "Disable introspection and it is secure" | Security cannot rely on hiding the schema | Use auth, authorization, operation limits, and monitoring |
| "Fragments are only for reuse" | Fragments also affect cache consistency and component data contracts | Discuss colocated data needs and maintenance |
| "Types mean the API is safe" | Type checks do not prove business permission or runtime availability | Mention auth, nullability, resolver failures, and testing |
| Level | What the answer should show |
|---|---|
| Fresher | Can write queries and mutations, understands schema basics, variables, loading states, and error states |
| Mid-level | Can reason about cache updates, pagination, fragments, partial data, and when REST is simpler |
| Senior | Can discuss schema evolution, resolver cost, N+1, security, observability, generated types, and migration strategy |
| Staff/lead | Can design GraphQL boundaries across teams, define governance, protect the API from expensive operations, and keep client contracts sane |
A product listing page is a useful GraphQL interview example because it touches schema design, client state, pagination, caching, authorization, and performance. A shallow answer says GraphQL prevents overfetching. A stronger answer explains the data contract and the cost of each field.
Start with the schema shape:
type ProductConnection {edges: [ProductEdge!]!pageInfo: PageInfo!totalCount: Int}type ProductEdge {cursor: String!node: Product!}type Product {id: ID!name: String!imageUrl: Stringprice: Money!inventoryStatus: InventoryStatus}type Query {products(filter: ProductFilterInputsort: ProductSortInputfirst: Int!after: String): ProductConnection!}
Then explain the frontend flow:
A good spoken answer might be:
I would query products with filter, sort, first, and after variables. Product cards would own a fragment for id, name, image, price, and inventory status. I would keep filters in the URL, merge cursor pages by product ID, and show a local fallback if a non-critical field fails. Before shipping, I would check resolver timing for inventory and recommendation fields because one GraphQL request can still trigger expensive backend work.
When practicing, make one answer concrete enough to draw: the operation, response shape, cache entries affected, error state, and server check. That exposes missing reasoning faster than another definition drill.

Experienced Next.js interviews in 2026 test whether you can choose the right rendering boundary for a product route: static, request-time, client-interactive, server-only, cached, revalidated, or protected before the page renders.
Mid-level and senior candidates are expected to go beyond definitions. Strong answers connect App Router APIs to product constraints: SEO, freshness, secrets, authentication, bundle size, error states, migration risk, and deployment behavior.
If you are preparing for entry-level interviews, start with Next.js Interview Questions for Freshers. This guide assumes you already know the common terms and need to explain tradeoffs, failure modes, and design choices.
The official Next.js App Router docs now show Next.js 16 as the latest line, while many company codebases still run Next.js 14 or 15. Use App Router vocabulary by default, but ask which version and caching setup the team uses before giving version-specific answers.
| Prompt | What it tests | Weak answer |
|---|---|---|
"Build /products/[id]." | Dynamic routes, params, metadata, not-found states | Only describing React Router |
| "Where should this API key live?" | Server-only code and secret handling | Calling the private API from the browser |
| "Why is this page stale?" | Caching, revalidation, dynamic rendering | Saying only "clear cache" |
| "Why did hydration fail?" | Server/client markup mismatch | Blaming CSS or React without naming the mismatch |
| "Add a cart button to a product page." | Server and Client Component boundaries | Marking the whole route as client-rendered |
"Protect /dashboard." | Auth checks before protected UI renders | Redirecting only in useEffect |
Next.js is a React framework for building web applications. It adds file-system routing, server rendering, static generation, Server Components, data fetching conventions, metadata, route handlers, image and font optimization, and deployment patterns around React.
React gives you the component model. Next.js gives you the application structure.
The App Router is the modern routing system based on the app/ directory. It uses route segments, page.tsx, layout.tsx, Server Components, Client Components, loading UI, error boundaries, Route Handlers, metadata APIs, and caching controls.
For new projects, answer App Router questions with app/ concepts first. Mention the Pages Router only when comparing older APIs such as getStaticProps or getServerSideProps.
In the App Router, folders define route segments. A segment becomes publicly routable when it has a page.tsx file.
app/page.tsxproducts/page.tsx[id]/page.tsx
This creates /, /products, and /products/:id.
page.tsx and layout.tsx?page.tsx renders the UI for a route. layout.tsx wraps a route segment and its children. Layouts are useful for shared navigation, sidebars, shells, and providers that should persist across navigation.
Use a page for route-specific content. Use a layout for shared structure.
Dynamic routes capture variable URL segments with folder names such as [id], [slug], [...slug], or [[...slug]].
export default async function ProductPage({params,}: {params: Promise<{ id: string }>;}) {const { id } = await params;return <h1>Product {id}</h1>;}
In current App Router examples, params may be treated asynchronously. Follow the project's Next.js version and conventions.
For an interview, say the important part out loud: [id] is the dynamic segment, params.id is the value from the URL, and the page should still handle a missing or invalid product with notFound().
Server Components render on the server and do not ship their component code to the browser. They are useful for reading server-only data, calling databases, using secrets, rendering static content, and reducing client JavaScript.
They cannot use browser-only APIs, event handlers, or client hooks such as useState.
Client Components run in the browser. Add "use client" at the top of the file to create a client boundary.
Use Client Components for click handlers, local state, effects, browser APIs, focus behavior, and interactive widgets.
"use client"?Use "use client" only for the component subtree that needs browser interactivity. A product page can be a Server Component while an AddToCartButton inside it is a Client Component.
The common mistake is marking the entire route as client-rendered because one button needs onClick.
Yes. A Server Component can import and render a Client Component, passing serializable props. This is the normal pattern for mixing server-rendered content with small interactive islands.
A Client Component should not directly import a Server Component. Instead, pass server-rendered children from a server parent when needed.
Hydration is the process where React attaches client-side behavior to server-rendered HTML. Hydration warnings happen when the initial client render does not match the HTML produced on the server.
Common causes include rendering Date.now(), Math.random(), locale-dependent text, or localStorage-dependent values during the first render.
Use notFound() when route data does not exist, and define a not-found.tsx file when the route needs custom not-found UI.
Do not silently render an empty page for a missing product. A missing resource is a route-level state.
Use redirect() when server-side route logic determines the user should go elsewhere, such as after an auth check or missing required setup.
Client-side navigation is still useful for user-initiated interactions, but protected content should not flash before a redirect.
Use static metadata for fixed route metadata and generateMetadata() when metadata depends on route params or fetched data.
For a product detail page, use the product title, description, canonical URL, and open graph image from server-side data.
loading.tsx?loading.tsx defines route-level loading UI. It works with Suspense boundaries so the user can see an immediate loading state while route content is prepared.
Good answers mention the user experience: a slow product page needs a stable loading layout, not a blank screen.
error.tsx?error.tsx defines an error boundary for a route segment. It must be a Client Component because error boundaries need client behavior.
Use it for recoverable route errors. Do not treat it as a replacement for validating data and handling expected empty states.
Use static generation when the route can be generated ahead of time and stale content is acceptable until the next build or revalidation. Use request-time rendering when the response depends on user-specific data, cookies, headers, permissions, or strict freshness.
Most product pages are mixed: marketing copy can be cached, while inventory or price may need revalidation or dynamic fetching.
In newer Next.js projects, you may also hear about Cache Components. The interview answer is still the same at the product level: decide which parts may be reused, which parts are request-specific, and where Suspense should show a loading state while uncached data resolves.
generateStaticParams()?generateStaticParams() returns params for dynamic routes that should be generated at build time.
Use it for known blog slugs, documentation pages, marketing pages, or product pages where the catalog is known and build-time generation is reasonable.
fetch()?Next.js extends server-side fetch() with persistent caching and revalidation controls. Defaults have changed across recent versions and can also depend on project configuration, so avoid giving a memorized default as if it were universal. Interviewers usually care more about whether you make freshness explicit for important data.
For example, product copy can be cached longer than stock status. A dashboard for the signed-in user should not share cached private data across users.
Useful answer pattern:
Revalidation updates cached data after time passes or after an event. Time-based revalidation is useful for data that can be a little stale. On-demand revalidation is useful after a CMS publish, catalog update, or admin action.
Name the freshness requirement before choosing a revalidation strategy.
Search routes often depend on query params, user input, ranking, and freshness. Avoid treating every possible search query as a prebuilt static page.
Depending on SEO and interactivity needs, render results on the server, fetch them on the client, or combine a server-rendered shell with client-side refinements.
Route Handlers let you define server endpoints inside the App Router, commonly with route.ts. Use them for API endpoints, webhooks, server-side data access, and integrations that should not expose secrets to the browser.
Secrets should stay on the server: Server Components, Server Functions, Route Handlers, or other server-only code. Do not expose private API keys through Client Components or public environment variables.
Server Functions run on the server and can be called from application code for mutations or server-side work. In interviews, explain why the server boundary matters: validation, authorization, secret access, and trusted writes belong on the server.
Validate on the client for fast feedback and on the server for trust. Client validation improves UX. Server validation enforces the rule.
A good form answer includes pending state, duplicate-submit prevention, error display, and accessible field messages.
Read auth state from trusted server-side signals such as cookies or headers. Redirect unauthenticated users before rendering protected UI when the server has enough information.
For broad route gates, Next.js docs now use the term Proxy for pre-route logic. Some teams may still say Middleware because older versions and codebases used that term.
Do not put all authorization logic only in Proxy. It is useful for early routing decisions, but data access and mutations still need server-side authorization at the point where protected data is read or changed.
Keep non-interactive UI as Server Components, put "use client" at small boundaries, avoid importing large browser-only libraries into shared parents, and measure bundle output before optimizing blindly.
Next.js provides an Image component and image optimization features. In interviews, explain the product reason: reserve layout space, serve appropriately sized images, avoid unnecessary downloads, and protect performance metrics such as LCP.
Next.js font optimization helps load fonts with less layout shift and clearer ownership. The key interview point is that font loading affects perceived performance, layout stability, and branding consistency.
Split the problem:
Then measure with framework build output, browser DevTools, server logs, and Web Vitals.
Development mode optimizes feedback for engineers. Production builds optimize output for users. Caching, bundling, minification, image behavior, and deployment environment can differ.
If a bug appears only in production, check environment variables, build-time assumptions, server/runtime differences, and caching.
Use a Server Component page for product data and SEO. Generate metadata from product data. Return notFound() for missing products. Keep interactive parts such as cart controls, variant selectors, and wishlist buttons in small Client Components. Make freshness explicit for price and inventory.
Check auth before rendering protected UI. Fetch user-specific data on the server with request-aware credentials. Avoid sharing cached private responses. Split slow widgets with Suspense when possible. Keep URL state for filters that users expect to share or revisit.
Do not rewrite everything at once. Migrate route by route, learn the new data and rendering model, replace getStaticProps and getServerSideProps with App Router patterns, move shared shells into layouts, and identify Client Component boundaries.
The biggest migration risk is carrying old mental models into Server Components and caching.
Good migration answers also mention compatibility work: routes may coexist during migration, shared components may need "use client" boundaries, API routes may move to Route Handlers only when there is a reason, and tests should cover redirects, metadata, and not-found behavior before and after the move.
Make the first client render match the server HTML. Avoid browser-only reads during render. Move browser-only logic into effects, use cookies when the server needs the value, or render a deliberate client boundary for values that cannot be known on the server.
Use server fetching when data improves SEO, uses secrets, depends on trusted auth, or should avoid extra client waterfalls. Use client fetching for highly interactive data, browser-only context, live updates, or user actions after initial render.
The answer should include the UI states either way.
Do not let one optional widget break an entire page. Use route error boundaries for route-level failures, local error UI for widget-level failures, and server-side validation for expected missing data.
Name which failures are fatal and which can degrade.
Separate route concerns: data loading, metadata, UI composition, interactive client widgets, mutation boundaries, and error states. Avoid making page.tsx a long file that owns every detail.
Test pure utilities with unit tests, components with user-focused component tests, and critical flows with E2E tests. For framework-specific behavior such as redirects, route handlers, and server boundaries, test at the route or integration level where possible.
Check whether secrets stay on the server, client boundaries are small, route states are handled, metadata is correct, caching is explicit where freshness matters, and loading/error/not-found states match the product requirement.
Use this structure:
That pattern turns framework knowledge into product judgment, which is what stronger Next.js interviews usually measure.

Vue.js interviews in 2026 test whether you understand Vue 3 reactivity, component communication, Single-File Components, Composition API, routing, state, and performance tradeoffs.
The strongest answers do not sound like API lists. They explain how data changes, which component owns it, how the template updates, and what happens when the app grows.
The official Vue introduction documents Vue 3 as the current guide, with Single-File Components for build-tool projects and Composition API plus SFCs as the recommended path for full applications.
| Prompt | What it tests | Common mistake |
|---|---|---|
| "Why did this template update?" | Vue reactivity | Saying "Vue watches everything" without explaining refs/proxies |
| "Should this be a prop, emit, or store value?" | State ownership | Putting all shared data in a global store |
| "Build a reusable modal." | Slots, emits, accessibility, lifecycle | Hard-coding content and forgetting focus behavior |
| "Fetch data for this route." | Lifecycle, async state, routing | Ignoring loading, empty, and error states |
| "This list is slow." | Keys, computed data, virtualization | Re-rendering a huge list and blaming Vue |
Vue.js is a JavaScript framework for building user interfaces. It uses components, declarative templates, reactive state, directives, and an ecosystem for routing, state management, tooling, and server rendering.
Vue can be used progressively on part of a page or as the framework for a full application.
A Single-File Component, usually a .vue file, keeps a component's template, script, and styles in one file.
<template><button @click="count++">Clicked {{ count }} times</button></template><script setup lang="ts">import { ref } from 'vue';const count = ref(0);</script>
SFCs are common in build-tool-enabled Vue projects because they keep the component's UI, logic, and local styles close together.
The Composition API is a Vue API style where component logic is written with functions such as ref, reactive, computed, watch, and lifecycle hooks inside setup or <script setup>.
It is especially useful when a component has several related pieces of logic or when logic should be extracted into composables.
The Options API organizes a component into options such as data, methods, computed, watch, and lifecycle hooks.
It is still valid Vue. In interviews, avoid presenting Composition API as "new good" and Options API as "old bad." Explain the tradeoff: Options API is approachable for simpler components, while Composition API scales better for shared logic and complex features.
ref()?ref() creates a reactive value. In script, read and write the value through .value. In templates, refs are automatically unwrapped.
const count = ref(0);count.value += 1;
Use ref for primitives and often for values that may be replaced.
reactive()?reactive() creates a reactive proxy for an object.
const form = reactive({email: '',password: '',});
Use it for object state where you want to mutate properties. Be careful when destructuring because destructuring can lose reactivity unless you use helpers designed for that case.
For example, destructuring const { email } = form gives you the current value, not a reactive binding. Use toRefs() when separate reactive bindings are needed:
import { reactive, toRefs } from 'vue';const form = reactive({email: '',password: '',});const { email, password } = toRefs(form);
That is a common source of "why did my template stop updating?" interview questions.
computed and watch?Use computed for derived values that can be calculated from reactive state.
Use watch for side effects when a reactive value changes: calling an API, syncing with storage, logging analytics, or bridging to non-Vue code.
If you can calculate it during render, use computed instead of watch.
Directives are special template attributes that apply reactive behavior. Common examples include:
v-if for conditional renderingv-show for toggling visibility with CSSv-for for listsv-bind or : for binding attributesv-on or @ for eventsv-model for two-way form bindingsv-if and v-show?v-if conditionally mounts or unmounts DOM. Use it when the condition changes rarely or when the component should not exist until needed.
v-show keeps the element in the DOM and toggles display. Use it for frequent visibility changes where mount cost matters less than toggle speed.
v-for?Keys help Vue track item identity across list updates. Use stable IDs when rendering lists that can change order, insert, remove, or filter.
Index keys can preserve state on the wrong row when the list changes.
Props pass data from parent to child. A child should treat props as read-only input.
<script setup lang="ts">defineProps<{title: string;completed: boolean;}>();</script>
If the child needs to request a change, emit an event instead of mutating the prop.
Emits let a child component notify its parent that something happened.
<script setup lang="ts">const emit = defineEmits<{save: [id: string];}>();function saveTodo(id: string) {emit('save', id);}</script>
The parent owns the state change. The child reports the user action.
v-model on a component?v-model creates a two-way binding convention between a parent and child component. In Vue 3, component v-model is based on a prop and an update event.
Use it for form-like components such as inputs, selects, toggles, and date pickers. Avoid hiding complex business updates behind v-model when explicit events would be clearer.
In current Vue, defineModel() is the concise way to declare a component model in <script setup>. In older or more explicit code, the same idea appears as a modelValue prop and an update:modelValue event. Knowing both helps in interviews because many production codebases mix versions and styles.
Slots let a parent pass template content into a child component.
Use slots for layout components, cards, modals, tables, and reusable shells where the child owns structure but the parent owns content.
Scoped slots let the child expose data to the slot content.
They are useful for reusable list, table, or form components where the child manages behavior and the parent controls rendering.
provide and inject pass values through a component tree without prop drilling.
Use them for shared context such as theme, form context, table context, or dependency-style values. Do not use them to hide ordinary parent-child data flow.
Vue tracks reactive reads during rendering or effects, then updates the affected parts when reactive values change. In Vue 3, object reactivity is built on JavaScript proxies, while refs wrap individual values.
The interview goal is to explain dependency tracking and updates without claiming Vue "rerenders everything."
A composable is a function that uses Vue Composition API features to package reusable stateful logic.
import { onMounted, onUnmounted, ref } from 'vue';export function useWindowWidth() {const width = ref(0);function updateWidth() {width.value = window.innerWidth;}onMounted(() => {updateWidth();window.addEventListener('resize', updateWidth);});onUnmounted(() => window.removeEventListener('resize', updateWidth));return { width };}
Good composables clean up side effects and expose a small API.
Common Composition API hooks include:
onMounted for browser-only setup after mountonUpdated for reacting after DOM updates when neededonUnmounted for cleanuponErrorCaptured for handling descendant errorsDo not fetch data in a lifecycle hook by reflex. In router-based apps or SSR, data loading may belong at the route or framework layer.
watch and watchEffect?watch observes explicit sources and runs when those sources change. It is better when you want control over what triggers the effect.
watchEffect runs immediately and tracks reactive values used inside it. It is convenient, but less explicit.
Use watch when correctness depends on a specific source.
Vue Router maps URLs to components and supports route params, query params, nested routes, navigation guards, lazy-loaded route components, and active links.
A good routing answer includes user states: loading, not found, unauthorized, and error.
For a simple client-rendered component, use reactive request state and fetch data when the route or input changes. In a framework or SSR setup, prefer the framework's data-loading pattern.
Always model:
Pinia is the commonly used state management library in the Vue ecosystem. Use a store for state shared across unrelated components or routes, such as auth user, cart, preferences, or app-wide entities.
Do not move every local form value into a store. Local state is easier to reason about when only one component owns it.
A good Pinia answer also separates server cache from client state. If the data mainly comes from the server and needs refetching, invalidation, pagination, or background refresh, a data-fetching layer or framework loader may be a better fit than manually copying everything into a store.
Put shareable, bookmarkable state in the URL: search query, filters, sort order, page number, and selected tab when the user expects back-button behavior.
Keep ephemeral UI state local: open menus, draft input, hover state, and one-off loading flags.
Use v-model for field values, validation for user feedback, and explicit submit handling for server work.
A good form answer includes labels, accessible errors, disabled pending state, server-side validation, and duplicate-submit protection.
Use props for open state, emits for close/confirm events, slots for content, and lifecycle or composables for focus trapping, Escape handling, and scroll locking. Keep accessibility requirements explicit: role, label, focus return, and keyboard behavior.
First identify the bottleneck. Use stable keys, avoid expensive work directly in templates, move derived data to computed values, paginate or virtualize long lists, lazy-load heavy rows, and measure before changing architecture.
Also check whether the list reactivity is too deep for the job. For large immutable datasets, shallowRef() can be useful because Vue tracks replacement of the array rather than every nested property. That is a senior-level answer only when you can explain the tradeoff: nested mutation will not trigger updates the same way.
Group by feature when features are large enough: route, components, composables, store, API client, tests, and types. Keep truly shared UI in a shared component area. Avoid a giant global components folder full of one-off pieces.
Use props and emits for direct parent-child communication. Use provide/inject for context shared by a component subtree. Use Pinia when unrelated parts of the app need the same state or actions.
The decision is about ownership and reach.
Test user-visible behavior: rendered text, form interactions, emitted events, loading states, error states, and accessibility-critical behavior. Avoid tests that only assert implementation details such as private method calls.
Common mistakes include unstable keys, deep watchers on large objects, expensive computed work over huge arrays, unnecessary global state updates, rendering too many rows at once, and importing large dependencies into frequently used components.
Clean up event listeners, timers, subscriptions, observers, and third-party library instances in onUnmounted. Composables that create side effects should own their cleanup.
Know how to type props, emits, refs, computed values, composables, API responses, and store state. With <script setup lang="ts">, keep component contracts close to the component.
Do not type everything as any to make the template pass. That removes the main value of TypeScript.
Check state ownership, prop mutation, event names, stable keys, computed versus watcher usage, cleanup for side effects, accessible form and modal behavior, route states, and whether shared code is genuinely reusable.
Use this structure:
That keeps your answer practical whether the prompt is a small component, a route, or a senior architecture question.
Build these small features:
| Feature | Skills tested |
|---|---|
| Todo list | ref, computed, v-for, keys, emits |
| Search page | async state, route query, loading and errors |
| Modal | slots, emits, lifecycle, keyboard behavior |
| Data table | props, scoped slots, derived rows, pagination |
| Cart store | Pinia, actions, derived totals, persistence |
| Form wizard | v-model, validation, step state, accessibility |
Pair Vue practice with general frontend practice: JavaScript data work, CSS layout, browser APIs, accessibility, and UI problem solving. Vue gives you a productive component model, but interviews still reward careful frontend judgment.

Frontend developer jobs in Pune are a good target in 2026 if you can handle practical UI work: React or Angular, JavaScript, CSS, APIs, performance basics, and readable code.
Pune has a mixed frontend market. You will see product companies, IT services, GCC teams, agencies, industrial tech, banking and finance products, analytics products, trainee roles, and senior frontend openings. Some roles are modern React product work. Some are Angular enterprise work. Some are web developer roles where HTML, CSS, Bootstrap, jQuery, CMS, and quick delivery matter.
That means your preparation should not copy a Bangalore or remote-only plan. Pune rewards candidates who can identify the kind of frontend work behind the title and show matching proof. Start by asking: is this product UI, enterprise delivery, agency/CMS work, or junior implementation work?
For stack choices, use data as a guardrail. In the 2025 Stack Overflow Developer Survey, professional developers reported broad use of JavaScript, HTML/CSS, TypeScript, React, Next.js, Angular, and jQuery. That matches Pune's mix: modern frontend roles exist, but some web developer jobs still value practical CSS, DOM, CMS, and legacy-code comfort.
Use the job description to classify the role.
| Role pattern | Likely work | What to show |
|---|---|---|
| Front-End Developer, 1-3 years | websites, client-side pages, Bootstrap, basic React, responsive UI | polished layouts, JS interactions, deployed projects |
| React Developer | product screens, API data, components, app state | React project with forms, tables, routing, and error states |
| Angular Developer | enterprise apps, dashboards, forms, services, modules | Angular forms, typed services, reusable components |
| Junior React/JavaScript Developer | small features, bug fixes, REST APIs, some backend adjacency | clean JavaScript, React basics, API-backed project |
| UI / Front-End Developer | layout, CSS, browser behavior, design handoff | responsive CSS, accessibility basics, visual care |
| Senior Frontend Developer | architecture, performance, mentoring, cross-team work | case studies, tradeoffs, performance and review examples |
| Trainee Frontend Developer | HTML, CSS, Bootstrap, JS, jQuery, learning on the job | finished beginner projects and GitHub discipline |
The mistake is applying to all of these as if they are the same. A trainee role needs evidence of learning speed and clean basics. A senior React role needs ownership, state decisions, and product judgment.
| Company type | Frontend work you may see | Best preparation |
|---|---|---|
| IT services | client apps, feature delivery, framework variety, maintenance | one framework plus adaptable JavaScript and CSS |
| Product companies | dashboards, workflows, customer features, API integration | product-style projects with edge states |
| GCCs | larger codebases, code review, testing, maintainability | TypeScript, component boundaries, readable PRs |
| Agencies | websites, campaign pages, responsive UI, CMS work | visual polish, CSS, browser compatibility |
| Industrial and construction tech | data tables, reports, project tools, internal systems | table-heavy UI, filters, charts, permissions |
| Fintech and operations teams | forms, KYC-like flows, approvals, audit screens | validation, disabled states, error recovery |
Choose one primary lane and one backup lane. For example:
Or:
This keeps your resume and portfolio consistent. It also prevents the common Pune job-search mistake: applying to a senior Angular enterprise role, a React product role, and a trainee UI role with the same project order and the same resume bullets.
Pune's company mix is different from Bangalore and Hyderabad. It has strong services, enterprise product, banking/finance, industrial tech, SaaS, and GCC-style work. Treat the names below as research targets, not a universal ranking.
| Company group | Examples to research | Frontend angle to look for |
|---|---|---|
| Pune-headquartered or Pune-strong tech companies | Persistent Systems, PubMatic, Druva, BMC Software, Zensar, Cybage | product engineering, SaaS dashboards, enterprise UI, platform tools, frontend performance |
| Banking, finance, and enterprise GCCs | Mastercard, Barclays, UBS, Citi, Deutsche Bank, Credit Suisse/UBS teams | forms, approvals, data-heavy screens, security, reliability, internal tools |
| Industrial, engineering, and B2B tech | Siemens, PTC, Dassault Systemes, vConstruct, NICE | complex workflows, data visualization, internal platforms, domain-heavy UI |
| Services and consulting companies | Infosys, TCS, Cognizant, Capgemini, Accenture, Tech Mahindra, Wipro | client delivery, Angular/React projects, migrations, support, multi-stack exposure |
| Startups and local product teams | SaaS, edtech, logistics, developer-tools, and agency-style teams | faster ownership, practical UI delivery, broader responsibilities |
If you want frontend depth, read the job description carefully. A strong Pune role can come from a product company, a GCC, or a services team, but the work should include UI ownership: forms, tables, APIs, performance, accessibility, or reusable components. If the listing only names tools, ask what screen or workflow the team owns.
The safest stack is:
If you are choosing between React and Angular, do not treat it as an identity decision. Search current Pune openings, count which framework appears in your target lane, and build one serious project in that framework. Keep JavaScript and CSS separate from the framework; those are what keep you employable when the codebase is older than the job post.
If you are still learning, follow How to Become a Frontend Developer in 2026 before applying heavily.
Build projects that match the work Pune companies actually hire for.
This fits product, analytics, industrial, services, and GCC roles.
Include:
Add a README note explaining the table state, why filters live where they live, and how you would connect the screen to a backend.
Good options:
Include:
This project works well because it is closer to internal-tool frontend work than a clone of a streaming homepage.
Do not ignore CSS. Pune has many roles where layout quality matters.
Include:
This project helps for web developer, agency, and UI roles.
Write bullets that describe behavior.
| Level | Weak bullet | Better bullet |
|---|---|---|
| Fresher | Made React projects | Built a React job tracker with filters, local persistence, empty states, and responsive layout |
| 1-2 years | Worked on UI pages | Implemented reusable form fields with validation messages and API error handling |
| 3-5 years | Developed frontend features | Owned an Angular approval workflow with typed services, role-based actions, and release fixes |
| Senior | Managed frontend work | Led a React performance pass on a data-heavy page by reducing unnecessary renders and table work |
If you do not have work experience, use project bullets. If you do have work experience, prioritize shipped work over personal projects.
Expect a practical interview bar. You may get a mix of JavaScript, CSS, framework questions, project discussion, and UI coding.
Prepare:
map, filter, reduce, sort, and mutation pitfallsasync/awaitPrepare:
For React:
For Angular:
Practice JavaScript interview questions, React interview questions, Contact Form, Data Table, Modal Dialog, and Tabs.
| Days | Work |
|---|---|
| 1-5 | Pick primary lane: React product, Angular enterprise, UI/web, or fresher/trainee |
| 6-12 | Fix JavaScript and CSS gaps with small exercises |
| 13-22 | Build or improve one product-style project |
| 23-28 | Add TypeScript or Angular/React depth based on your target lane |
| 29-32 | Clean GitHub, READMEs, live links, and screenshots |
| 33-36 | Rewrite resume bullets for Pune roles |
| 37-45 | Apply daily, practice interviews, and record questions asked |
Use interview feedback as data. If three interviews expose weak CSS, stop adding React libraries and fix CSS. If every role asks for Angular and your target is enterprise Pune, adjust. If you get no callbacks, check whether your live demos work on mobile and whether your resume says what you actually built.
Freshers should not try to look senior. Look reliable.
Your first-role proof should include:
Apply to internships, trainee roles, junior UI roles, and small companies where you can learn. Read Frontend Developer Jobs for Freshers: How to Get Your First Role in 2026 for the full first-role process.
If you have experience, build your pitch around ownership:
Use Frontend Developer Career Path: From Junior to Senior in 2026 to map your examples to the next role.
Ask:
The answers help you decide whether the role builds the frontend career you want.
Frontend developer jobs in Pune reward practical preparation. Match the role type, build proof for that lane, prepare JavaScript and CSS seriously, and show that you can deliver UI that works beyond the first happy path.

Remote frontend developer jobs are available in 2026, but they are harder to win than local roles. You need frontend skill, written proof, async work habits, timezone fit, and a portfolio that earns trust quickly.
Remote hiring changes the signal. In an office interview, a team may rely on energy, conversation, and local availability. In a remote hiring process, the team looks for proof that you can work without constant supervision: clear writing, small deliverables, good questions, and code that can be reviewed asynchronously.
That matters for frontend because the work sits between product, design, backend, QA, analytics, and users. If your communication is vague, remote frontend work becomes slow quickly.
The opportunity is real, but not evenly distributed. In the 2025 Stack Overflow Developer Survey work section, respondents in India reported a mix of remote, hybrid, flexible, and in-person work rather than a remote-only market. Treat remote as a competitive role type, not as a shortcut around local hiring.
Remote frontend roles often ask for the same technical stack as local roles, but they add trust signals.
| Area | What companies look for | How to show it |
|---|---|---|
| Framework skill | React, Angular, Vue, Svelte, or Next.js experience | project or case study with complete flow |
| JavaScript and TypeScript | reliable app behavior and safer code | typed API data, state models, fewer assumptions |
| Product UI | forms, dashboards, user flows, API states | deployed demos with loading, empty, error, retry |
| Review habits | code that can be reviewed without a meeting | readable commits, README, pull-request notes |
| Async work | can explain decisions in writing | case studies, technical notes, issue-style project docs |
| Timezone overlap | can collaborate at predictable times | mention preferred overlap and location clearly |
| Ownership | can move work forward when requirements are incomplete | examples of questions, tradeoffs, and follow-through |
Remote does not mean easier. It often means the company has more applicants and less patience for unclear proof. A remote application has to answer doubts before the first call: Can this person write clearly? Can they ship without hand-holding? Can we review their code without a meeting?
Use more than one source. Remote roles appear and fill quickly.
Search terms:
remote frontend developerremote frontend engineerremote React developerremote Next.js developerremote Angular developerremote UI developerfrontend developer work from homefrontend engineer async remoteUse these channels:
| Channel | How to use it |
|---|---|
| Remote job boards | Apply quickly, but only when the stack and timezone fit |
| Company career pages | Search companies that already hire distributed teams |
| Filter by remote, then verify the company page | |
| Startup platforms | Look for early teams needing product frontend work |
| Communities | React, JavaScript, design-system, and indie-hacker communities |
| Previous network | Ask former coworkers about distributed teams |
Do not spend all day clicking easy-apply buttons. Remote hiring rewards targeted applications. Save the job post, because remote listings are edited or closed quickly and you may need the original requirements while preparing.
Some remote roles are excellent. Some are unclear, low-trust, or not truly remote.
| Filter | Good signal | Red flag |
|---|---|---|
| Timezone | clear overlap, such as IST plus Europe or IST plus US morning | "global remote" but no meeting expectations |
| Employment type | full-time, contract, freelance, or internship is explicit | vague role with unclear legal setup |
| Pay | range, band, or early compensation conversation | pay hidden until late rounds |
| Process | written docs, code review, async tools | constant online monitoring |
| Role scope | frontend ownership is described | only a stack list with no work context |
| Assignment | time-boxed and relevant | unpaid product work disguised as a test |
| Communication | official email, normal contract process | personal payment requests or strange onboarding |
If the role is remote but expects you to be online all day with no autonomy, evaluate whether it actually gives you the remote work you want.
Also verify the basics before sharing documents: company domain, recruiter identity, written offer process, payment schedule for contracts, equipment policy, and whether the role is remote from your country or only remote inside one country.
A remote portfolio should explain your work even when you are asleep in another timezone.
Include:
Your case studies should answer:
Add one remote-specific detail to at least one case study: the written issue, handoff note, README, pull-request description, or decision log you would use if teammates reviewed the work later. Remote teams hire for evidence that work can move while people are offline.
For experienced developers, read Frontend Developer Portfolio: What to Build and What to Show in 2026. For freshers, use How to Build a Frontend Developer Portfolio With No Experience (2026).
Remote teams judge writing early.
Your proof can include:
Use this project note structure:
Context:What the feature does and who uses it.Constraints:Time, API shape, device support, or design limits.Decisions:State ownership, component boundaries, data fetching, accessibility, or performance choices.Quality:Tests, manual checks, keyboard checks, known limitations.Next:What you would improve with more time.
This is not extra decoration. It shows how you would work in a distributed team. A short, clear note beats a long case study that never names the constraints.
Remote applications need to be more targeted because competition is wider.
Use this process:
Track timezone and country restrictions separately. Many listings say "remote" but require a legal entity, payroll setup, or working-hours overlap that may exclude you.
Application note:
Hi [Name],I am applying for the remote frontend role. The role mentions React, TypeScript, and dashboard work, which matches this project: [link].I built filtered tables, URL-based state, loading/error handling, and a responsive layout. I also wrote a short case study with the main frontend decisions: [link].I am based in [location] and can overlap with [timezone/window].
Customize the middle paragraph. If the job mentions design systems, point to component work. If it mentions SaaS dashboards, point to table and API work. If it mentions e-commerce, point to cart, checkout, and performance.
Prepare your environment and your thinking.
Practice:
Do:
Do not:
For take-home assignments, include a short README with scope, run commands, assumptions, tradeoffs, and what you would improve with more time. That README is part of the interview, not paperwork.
Practice front end system design questions, React interview questions, Data Table, Autocomplete, and File Explorer.
Freshers can apply to remote roles, but should be careful. Many remote fresher roles are internships, freelance tasks, or small-company work with limited mentoring.
Before accepting, ask:
If you have no experience, a good local or hybrid internship with real mentorship can be better than a poorly managed remote role. Remote fresher work is safest when there is a named reviewer, clear weekly feedback, and a written task process.
Experienced developers should prepare ownership stories.
Show:
Remote senior roles often care less about whether you can write a component and more about whether work moves forward when nobody is sitting beside you.
Good senior proof includes artifacts: a design note, migration plan, incident write-up, performance measurement, accessibility checklist, or pull-request description that shows how you reduce ambiguity for others.
Avoid:
Remote frontend jobs go to candidates who lower hiring risk.
Remote frontend developer jobs in 2026 require visible trust. Build proof that can be reviewed asynchronously, write clearly, filter roles carefully, and prepare to show how you move frontend work forward without constant supervision.

Frontend developer jobs in Bangalore are competitive because the city attracts many applicants, but the city also gives frontend engineers more role variety than most Indian markets: product companies, startups, GCCs, SaaS teams, fintech, agencies, and design-heavy web teams.
That mix is useful only if your preparation is targeted. A generic "HTML, CSS, JavaScript, React" resume will disappear quickly. A portfolio that proves product UI, API integration, state handling, performance awareness, and clear frontend decisions has a much better chance.
Bangalore rewards candidates who can move beyond course projects and talk like someone who understands product frontend work: designs change, APIs fail, requirements are incomplete, users make mistakes, and code has to survive review.
One current signal supports that stack choice: in the 2025 Stack Overflow Developer Survey, professional developers reported heavy use of JavaScript, HTML/CSS, TypeScript, React, Next.js, and Angular. Use that as a broad market signal, not as proof that every Bangalore job uses the same stack.
Bangalore has more role variety than most Indian cities. You will see:
This means there is no single Bangalore frontend path. A fresher applying to internships, a React developer with two years of experience, and a senior engineer targeting GCCs need different proof. Before you apply, classify the role: product frontend, UI implementation, enterprise app, design system, CMS/web, or frontend platform.
Do not treat this as a fixed ranking. "Top company" depends on whether you want product engineering, startup pace, enterprise scale, design systems, compensation, mentorship, or frontend depth. Use this list as a starting point for research, then check current openings and team fit.
| Company group | Examples to research | Frontend angle to look for |
|---|---|---|
| Global product and platform companies | Google, Microsoft, Amazon, Adobe, Intuit, Salesforce, Atlassian, Walmart Global Tech | product UI, platform tools, internal systems, design systems, performance, accessibility |
| Indian consumer and fintech product companies | Flipkart, PhonePe, Razorpay, Swiggy, Meesho, CRED, Groww, Zepto | high-traffic UI, checkout flows, payments, experimentation, mobile web, customer-facing product work |
| SaaS and B2B product teams | Freshworks, Zoho, BrowserStack, Chargebee, Postman, Whatfix | dashboards, admin tools, onboarding flows, developer tools, customer workflows |
| Services and consulting companies | Accenture, Infosys, Wipro, TCS, Cognizant, Capgemini, Thoughtworks | client projects, migrations, enterprise apps, Angular/React delivery, broader stack exposure |
| Design-led and agency-style teams | product studios, design agencies, brand-tech teams, marketing-tech teams | CSS, visual polish, responsive implementation, CMS, campaign pages |
Shortlist companies by role quality, not brand alone. A smaller product team where you own a checkout flow can be better for frontend growth than a famous company where you only maintain a narrow internal page.
When researching a company, look for three things before sending the resume: recent frontend openings, product screenshots or docs that reveal the UI complexity, and engineering posts or job descriptions that mention review, testing, accessibility, performance, or design systems. If all you can find is a stack list, treat the role as unverified until a recruiter or engineer explains the work.
Read the job description like an engineer, not like a keyword scanner.
| Listing signal | What the role likely involves | What to show |
|---|---|---|
| "front-facing experiences" or "user-facing features" | Product UI that customers use daily | Polished product screens with edge states |
| "reusable components and libraries" | Component architecture or design system work | Component APIs, variants, docs, and examples |
| "backend developers and product teams" | API integration and cross-team work | Loading, error, auth, retry, and data-shape handling |
| "AI-powered developer tools" | AI-assisted workflows or developer productivity UI | Good review habits and ability to verify generated code |
| "high-performance web applications" | Rendering, bundle, image, or interaction cost matters | Measured improvements and performance notes |
| "onsite contract" | Delivery speed and availability may matter more than brand | Clear scope, rate, duration, and ownership expectations |
| "webmaster" or "WordPress" | CMS, marketing sites, hosting, SEO, and plugin work | CSS, responsive pages, CMS experience, page-speed care |
If the job says React but the work is mostly WordPress, decide whether that is a good career move before applying. If the role says frontend engineer but the description talks about product, APIs, and ownership, prepare for deeper interviews.
For Bangalore, React is a practical default, but not the whole story. Angular remains relevant in enterprise teams, Vue appears in product and agency work, and strong JavaScript matters everywhere. Do not choose a stack from hype alone; choose it from the roles you are targeting this month.
Prepare in this order:
| Skill | Beginner proof | Experienced proof |
|---|---|---|
| JavaScript | can build search, filter, form, timer, and API widgets | can debug async bugs, stale state, race conditions, and data transforms |
| CSS | can build responsive pages with Flexbox and Grid | can fix layout bugs, overflow, stacking, theming, and design-system styles |
| React or Angular | can build complete flows | can design component boundaries and manage state safely |
| TypeScript | can type props and API responses | can model state transitions and remove unsafe assumptions |
| APIs | can fetch and render data | can handle auth, validation errors, retries, cancellation, and partial failures |
| Quality | can write simple tests | can decide which behavior needs tests and which checks belong in review |
| Performance | knows DevTools basics | can diagnose slow renders, large bundles, and expensive interactions |
If this list feels too wide, start with the Frontend Developer Roadmap. Then return to this article when you are ready to apply.
The best Bangalore portfolio is not a visual gallery. It is a proof system.
Build a dashboard that feels like something a B2B product team might ship.
Include:
What to write:
Build one of these:
Include validation, step navigation, saved progress, review screen, error summary, and success state. This helps because many actual frontend jobs involve forms, not only landing pages.
Build a small documented component set:
Show states: loading, disabled, error, focus, destructive, and empty. Add usage examples. If you are experienced, include tradeoffs around controlled/uncontrolled props, composition, and accessibility.
Your resume should tell the company why your proof matches their work. If the listing is for dashboards, lead with a dashboard project. If it is for UI implementation, lead with CSS and responsive polish. If it is for a GCC, lead with maintainability, tests, and review-ready code.
Do this:
Weak bullet:
Better bullet:
For experienced candidates:
That gives interviewers something concrete to discuss.
Do not apply randomly for two weeks and then conclude the market is bad. Run a deliberate pipeline and review it weekly.
| Step | What to do | Why it helps |
|---|---|---|
| Build a target list | 40-60 companies split by product, GCC, startup, agency, services | You stop reacting only to job boards |
| Map role types | React product, Angular enterprise, UI developer, design system, remote | You customize proof instead of blasting resumes |
| Prepare two resume variants | product/frontend engineer and UI/web developer | You do not undersell or oversell yourself |
| Send targeted notes | mention one matching project or case study | Recruiters see relevance quickly |
| Track outcomes | applied, response, test, interview, rejection reason | You learn which lane is working |
After 25-30 applications, inspect the pattern. No callbacks usually means the resume or portfolio proof is weak. Callbacks followed by failed tests usually means interview practice is the bottleneck. Interviews that end after project discussion usually mean you need clearer ownership stories.
Use How to Evaluate Companies as a Front End Engineer before accepting a role that sounds exciting but has unclear frontend ownership.
Bangalore interviews vary widely. Prepare for these patterns:
| Round | Common expectation | How to practice |
|---|---|---|
| JavaScript | arrays, objects, async, closures, event loop, DOM | timed questions and small browser exercises |
| React | state, effects, rendering, keys, forms, refs, composition | explain bugs from past projects |
| TypeScript | props, API types, unions, generics, narrowing | type a small API-backed feature |
| CSS | layout, specificity, responsive design, overflow, positioning | recreate product layouts without a UI kit |
| UI coding | build component or flow quickly | forms, tabs, modal, data table, autocomplete |
| System design | design a frontend feature at product scale | talk through state, data, caching, performance, accessibility |
| Project deep dive | prove you built the work | prepare decisions, bugs, tradeoffs, and next improvements |
Practice React interview questions, JavaScript interview questions, Data Table, Contact Form, Autocomplete, and File Explorer.
Freshers need proof that they can be onboarded quickly.
Do this:
Avoid filling your resume with advanced tools before you can explain JavaScript, CSS layout, and React state.
Read Frontend Developer Jobs for Freshers: How to Get Your First Role in 2026 for a full fresher path.
If you already have experience, your job search should center on scope.
Prepare examples for:
Your portfolio can use anonymized case studies. You do not need to expose private company code. You do need to explain what you owned.
Watch for:
Not every imperfect role is bad. Early-career roles can be messy and still useful. But know what tradeoff you are accepting.
Bangalore frontend developer jobs in 2026 reward targeted proof. Pick a role lane, build projects that match it, prepare JavaScript and UI coding seriously, and make each application about the frontend problems the company actually has.

Frontend developer jobs in Hyderabad are a good fit if you can build maintainable web apps, work with backend and design teams, and prove React, Angular, TypeScript, API, and UI debugging skill.
The important word is "maintainable." Hyderabad has startups, service companies, SaaS teams, media companies, fintech teams, cloud products, and large product engineering centers. Many frontend roles sit inside bigger engineering systems, so companies care about code that other people can read, review, test, and extend.
That changes your preparation. A flashy personal site helps only a little. A project that handles form validation, role-based screens, table state, API failures, and responsive layout says much more.
Use global stack data carefully. The 2025 Stack Overflow Developer Survey still shows JavaScript, HTML/CSS, TypeScript, React, Next.js, and Angular as common professional-developer technologies. That supports preparing those skills, but the exact framework still depends on the Hyderabad role: enterprise teams may be Angular-heavy, product teams may be React-heavy, and web roles may still ask for CMS or plain JavaScript work.
The same title can hide different jobs. Read the job description before deciding whether to apply.
| Job title pattern | What the work often means | How to prove fit |
|---|---|---|
| Front End Developer | General website or app UI work, often HTML, CSS, JavaScript, Bootstrap, WordPress, React, or Angular | Show polished UI, responsive layout, and basic interaction quality |
| ReactJS Developer | Product screens, dashboards, state, API integration, reusable components | Show React projects with forms, routing, async states, and TypeScript |
| Angular Developer | Enterprise apps, admin tools, forms, services, reusable modules | Show typed forms, validation, API services, and component structure |
| Front-End UI Developer | CSS-heavy implementation, design handoff, browser behavior, layout details | Show pixel-careful pages, responsive states, and accessibility basics |
| Senior Frontend Developer | Architecture, review, mentoring, performance, migration, design system work | Show case studies, tradeoffs, and production ownership |
| Frontend Intern or Fresher | Small components, bug fixes, HTML/CSS/JS tasks, simple framework work | Show two finished projects and clean GitHub repos |
Do not apply to all of these with the same resume. A WordPress-heavy role, a React product role, and an Angular enterprise role need different evidence. The fastest way to waste applications is to send a React-only resume to a UI/CSS role or a beginner project gallery to a maintainability-heavy enterprise role.
Think of the city as several markets, not one market.
| Company type | What they usually test | Good candidate signal |
|---|---|---|
| GCCs and multinational product teams | maintainability, code review, TypeScript, process, collaboration | You can explain component boundaries and edge states |
| SaaS and B2B product companies | dashboards, workflows, API integration, customer-facing product UI | You have built product flows, not only landing pages |
| Service companies | adaptability across clients, fast delivery, stack flexibility | You know one framework well and can pick up adjacent tools |
| Media and creator-tech companies | responsive UI, speed, web publishing, visual polish | You can build attractive pages without breaking performance |
| Fintech and operations products | tables, forms, permissions, audit-like flows, reliability | You handle validation, disabled states, and failure recovery |
| Early startups | broad ownership, quick iteration, debugging across the stack | You can clarify requirements and ship a small feature end to end |
If you are a fresher, your first target can be internships, trainee roles, junior UI roles, or small-company frontend roles. If you have 2-4 years of experience, aim for product UI roles where React or Angular is central. If you have 5+ years, look for roles that mention ownership, architecture, performance, design systems, or mentoring.
The Hyderabad list should not be read as a permanent ranking. It is a practical target map. Check current openings, team scope, and interview expectations before applying.
| Company group | Examples to research | Frontend angle to look for |
|---|---|---|
| Large product and platform companies | Microsoft, Google, Amazon, Salesforce, ServiceNow, Oracle, Qualcomm, Apple | large engineering systems, enterprise UI, internal platforms, cloud tools, product workflows |
| Finance, operations, and enterprise GCCs | Wells Fargo, JPMorgan Chase, Deloitte, EY, UBS, State Street, The Hartford | dashboards, forms, approvals, role-based UI, compliance-heavy workflows |
| SaaS, media, and product engineering teams | ValueLabs, EPAM, OpenText, Model N, Electronic Arts, Ivy | product screens, analytics UI, customer tools, design and backend collaboration |
| IT services and consulting companies | Accenture, Infosys, TCS, Cognizant, Capgemini, Tech Mahindra, Wipro | client apps, migrations, Angular/React delivery, frontend maintenance, broader stack exposure |
| Startups and local product teams | T-Hub ecosystem companies, fintech, healthtech, edtech, creator-tech teams | broader ownership, faster learning, product ambiguity, smaller frontend teams |
For freshers, do not apply only to famous names. Hyderabad has many smaller teams where a junior developer can get more frontend reps. For experienced developers, prioritize teams that mention product ownership, design systems, performance, testing, or platform work.
Before shortlisting a company, check whether the role describes the product surface, review process, and frontend ownership. A job post that says only "React, HTML, CSS, JavaScript" may still be useful, but you need to ask what you will actually build.
Do not begin by collecting every framework name. Prepare the skill stack that appears across many Hyderabad roles.
| Skill | What interviewers may ask | What to build |
|---|---|---|
| HTML and CSS | forms, semantic tags, responsive layout, Flexbox, Grid, specificity, overflow | settings page, pricing page, form flow, dashboard shell |
| JavaScript | arrays, objects, promises, closures, event handling, fetch, timers | search page, filterable table, debounced input, polling widget |
| React or Angular | state, props, effects or lifecycle, forms, routing, reusable components | customer dashboard, ticket system, onboarding form |
| TypeScript | props, API response types, unions, narrowing, event types | typed API client, typed form state, typed table rows |
| APIs | loading, empty, error, retry, auth failure, validation errors | API-backed dashboard with realistic failure states |
| Testing | user behavior, form submission, async UI, simple component tests | tests for filter, form, and save flows |
| Accessibility | labels, focus order, keyboard navigation, modal behavior, error text | keyboard-safe form, dialog, tabs, or menu |
| Performance | large lists, image loading, render cost, network waterfall | table with pagination, optimized image page, measured interaction |
Use the Frontend Developer Roadmap if you need a broader order. For this article, the practical target is simple: be able to build a useful product screen and explain why it works.
Job descriptions often mix must-haves, nice-to-haves, and recruiter filler. Translate them into actual preparation.
| JD phrase | What it probably means | What to prepare |
|---|---|---|
| "Collaborate with designers and backend developers" | You will receive designs and API contracts that are not always perfect | Practice asking API, loading, validation, and responsive questions |
| "Responsive web applications" | CSS and browser behavior matter | Build layouts that work at mobile, tablet, and desktop sizes |
| "Reusable components" | They care about maintainability | Prepare examples of component props, naming, and composition |
| "React and Next.js" | They may expect routing, rendering, data loading, and app structure | Build a route-based app, not only isolated components |
| "Angular/React" | The team may support older or mixed codebases | Show framework basics plus solid JavaScript |
| "Enterprise user interfaces" | Forms, tables, permissions, audit flows, and long-lived code | Build admin-style workflows with edge states |
| "Performance" | They may have slow pages, large lists, or heavy dashboards | Learn how to inspect renders, bundles, images, and network requests |
If a JD lists 20 technologies, do not panic. Find the work behind the list. Is it product UI? CMS work? enterprise dashboard work? migration work? Your application should answer that need.
Build projects that look like actual frontend work inside teams.
Include:
Write a project note explaining why filters live in the URL, how you avoid duplicate state, and what happens when the API fails.
Good examples:
Include:
This project helps because Hyderabad roles often involve form-heavy enterprise UI. A careful multi-step flow is better than another weather app because it reveals how you handle validation, recovery, keyboard use, and state across screens.
Include:
This shows product judgment. Many candidates can render a button. Fewer can explain who should be allowed to press it and what happens when the save fails.
Your resume should change with your level.
| Level | What the resume should prove | Example bullet |
|---|---|---|
| Fresher | You can finish small frontend projects and explain them | Built a React onboarding form with validation, review step, saved progress, and mobile layout |
| 1-2 years | You can deliver scoped features with review | Built reusable table filters and API error states for an internal dashboard |
| 3-5 years | You can own a product flow | Owned a customer settings flow across React components, API integration, validation, and release fixes |
| 5+ years | You can reduce technical and product risk | Led a frontend refactor that simplified state ownership and reduced repeated form bugs across three screens |
Weak resume bullets name tools. Better bullets name behavior.
Weak:
Better:
If your resume does not give an interviewer a concrete question to ask, rewrite it.
Not every company runs the same process, but Hyderabad frontend interviews often include these patterns:
| Round | What they are checking | How to prepare |
|---|---|---|
| Screening call | role fit, salary range, notice period, stack match | Have a 30-second summary and target role clear |
| JavaScript round | language basics and debugging | Practice arrays, promises, closures, object transforms, and async mistakes |
| Framework round | React or Angular depth | Prepare forms, state, lifecycle/effects, routing, reusable components |
| UI coding | can you build under time pressure | Practice forms, tabs, data table, autocomplete, modal, accordion |
| Project discussion | did you actually build the work | Prepare tradeoffs, bugs, constraints, and improvements |
| Hiring manager round | team fit and ownership | Explain how you clarify requirements and handle review feedback |
Practice JavaScript interview questions, React interview questions, and UI tasks such as Data Table, Contact Form, and Transfer List.
If you are targeting Angular roles, practice the same behavior in Angular: forms, validation, API state, component inputs/outputs, services, and routing.
Use this if you already know basic HTML, CSS, JavaScript, and one framework.
| Days | Work |
|---|---|
| 1-4 | Fix JavaScript gaps: arrays, objects, promises, closures, fetch, and event handling |
| 5-8 | Build or improve a form-heavy project with validation and accessibility checks |
| 9-13 | Build an API-backed dashboard with filters, loading, empty, error, and retry states |
| 14-17 | Add TypeScript to one project and remove unsafe any |
| 18-20 | Write READMEs and case notes for your best projects |
| 21-24 | Practice UI coding tasks under a timer |
| 25-27 | Rewrite resume bullets for Hyderabad role types |
| 28-30 | Apply to a targeted list and record interview feedback |
If the first two weeks produce no responses, do not add another certificate first. Check whether your strongest project is visible in the first half of the resume, whether the live link works, and whether the bullets match the role type.
Do not wait until day 30 to apply if you already have decent proof. Start applying once your best project is deployed and readable.
Ask questions that reveal the actual frontend work:
These questions help you avoid roles where the title sounds good but the work does not match your goal.
Hyderabad frontend developer jobs in 2026 reward candidates who can work inside larger product systems. Prepare the framework, but make your proof about maintainable UI: forms, tables, API states, TypeScript, review-ready code, and clear ownership.

To learn React in 2026, learn components, JSX, props, state, rendering, effects, forms, and data flow before you chase framework features. Then build enough UI that you can decide what belongs in local state, URL state, server state, or a parent component.
React is easier when JavaScript already feels familiar. If arrays, objects, functions, promises, and modules still feel shaky, spend time on JavaScript first.
| Stage | What to learn | What to practice |
|---|---|---|
| 1 | Components and JSX | Break a page into reusable pieces |
| 2 | Props and composition | Pass data down without duplicating UI |
| 3 | State and rendering | Build counters, toggles, tabs, accordions |
| 4 | Events and forms | Controlled inputs, validation, submit flows |
| 5 | Effects | Sync with browser APIs and external systems |
| 6 | Data fetching | Loading, empty, error, retry, stale responses |
| 7 | Routing and app structure | Multi-page product flows |
| 8 | TypeScript and testing | Safer props, user-focused tests |
The official React Learn docs are organized around the concepts beginners use daily: components, markup, data display, conditions, lists, events, state, Hooks, and sharing data between components. Use that sequence as your spine.
A React component is a function that returns UI. The hard part is not writing the function. The hard part is deciding the boundary.
Good beginner components usually answer one of these questions:
Start with static components:
function ProfileCard({ name, role }: { name: string; role: string }) {return (<article><h2>{name}</h2><p>{role}</p></article>);}
Then practice rendering lists, conditional UI, and nested components. Avoid splitting everything into tiny files too early. A component boundary should make the code easier to read or change.
Props are input. State is memory. Beginners often reach for state too quickly.
Use props when a component can be fully described by data from its parent:
function Price({ amount }: { amount: number }) {return <span>${amount.toFixed(2)}</span>;}
Use state when the component needs to remember something that changes because of user interaction, time, network responses, or browser state.
Ask this before adding state:
Derived state bugs are common in interviews and code reviews. If fullName can be calculated from firstName and lastName, calculate it during render instead of storing another state variable.
React rendering is a mental model, not only a syntax topic. A state update schedules a new render. During that render, React calls the component again and calculates the next UI.
Practice these cases:
When a new state value depends on the previous one, practice the updater form:
setCount((count) => count + 1);
That habit matters for event handlers that queue multiple updates, async callbacks, and components where several interactions can happen quickly.
For arrays, use IDs as keys when possible. Index keys are risky when users can insert, remove, sort, or filter items because React may preserve state for the wrong item.
Forms teach most of React's daily skills in one place: state, events, validation, async work, disabled states, error messages, and accessibility.
Build a signup form with:
Do not stop at alert("submitted"). Real forms fail. Users type invalid data. Networks slow down. Servers reject requests. A job-ready React developer knows how the UI behaves in each state.
useEffect is not the place to put every calculation. Use it when the component must sync with something outside React: the document title, a browser API, a subscription, a timer, or an imperative library.
Many beginner bugs come from unnecessary effects:
// Avoid storing derived data in state with an effect.const completedCount = todos.filter((todo) => todo.done).length;
If you can calculate a value during render, do that. If an effect starts a subscription, timer, request, or event listener, think about cleanup.
React itself does not prescribe one data fetching library for every app. Your learning goal is to understand the states around data:
Build a small search page and handle out-of-order responses:
import { useRef, useState } from 'react';type Product = {id: string;name: string;};function ProductSearch() {const [products, setProducts] = useState<Product[]>([]);const [status, setStatus] = useState<'idle' | 'loading' | 'error'>('idle');const latestRequestId = useRef(0);async function searchProducts(query: string) {const requestId = ++latestRequestId.current;setStatus('loading');try {const response = await fetch(`/api/products?q=${encodeURIComponent(query)}`,);const data = await response.json();if (requestId !== latestRequestId.current) {return;}setProducts(data.products);setStatus('idle');} catch {if (requestId === latestRequestId.current) {setStatus('error');}}}// Render the form, status message, and product list here.}
In a real app, a library may handle caching and request state for you. The interview skill is explaining the failure mode: an older request should not overwrite newer results. In production code, also consider AbortController so the browser can cancel work that is no longer needed.
After you can build React components and flows, learn routing. For many production apps in 2026, that means learning React through a framework such as Next.js, Remix, or React Router-based setups.
Learn these routing concepts:
If you choose Next.js, learn App Router concepts after React basics: Server Components, Client Components, layouts, route handlers, metadata, caching, and revalidation.
TypeScript helps you make component contracts explicit:
type ProductCardProps = {id: string;name: string;price: number;onAddToCart: (id: string) => void;};function ProductCard({ id, name, price, onAddToCart }: ProductCardProps) {return (<article><h2>{name}</h2><p>${price.toFixed(2)}</p><button onClick={() => onAddToCart(id)}>Add to cart</button></article>);}
Start with props, event handlers, form state, API response types, and union types for UI states. Avoid learning the deepest generic patterns before you can type ordinary components well.
Read TypeScript for React Developers when you want a focused bridge from React to TypeScript.
| Project | What it proves |
|---|---|
| Todo app with filters | State updates, derived data, forms |
| Product listing page | Lists, API fetching, loading, empty and error states |
| Multi-step form | Validation, state transitions, accessibility |
| Dashboard widgets | Component composition and data formatting |
| File explorer | Recursion, tree data, selection state |
| Autocomplete | Async search, keyboard interaction, stale-response handling |
For interview prep, practice React interview questions and UI tasks such as Tabs, Accordion, and Data Table.
The first mistake is treating React as magic. React is still JavaScript. If a callback receives stale data, if an array is mutated, or if a promise resolves late, React will expose that mistake.
The second mistake is putting all state in one place. Local UI state should often stay local. Server data should not be manually copied into many unrelated components. URL state belongs in the URL when the user expects refresh, share, or back-button behavior to preserve it.
The third mistake is learning Hooks as a list of APIs instead of learning why they exist. useState stores component memory. useEffect syncs with external systems. useMemo and useCallback are tools for specific rendering and identity problems, not default wrappers for every value.
The fourth mistake is using effects to mirror state. If filteredProducts can be calculated from products and query, calculate it during render or with useMemo when the calculation is actually expensive. Storing it separately creates another value that can go stale.
| Weeks | Plan | Output |
|---|---|---|
| 1-2 | Components, JSX, props, conditional rendering, lists | Static product page and reusable cards |
| 3-4 | State, events, forms, validation | Signup form and todo app |
| 5-6 | Effects, browser APIs, data fetching | API search page with full request states |
| 7 | Routing and URL state | Product listing with filters in the URL |
| 8 | TypeScript with React | Typed component library for your projects |
| 9 | Testing and accessibility | User-focused tests and keyboard behavior fixes |
| 10 | Interview practice and portfolio cleanup | Two projects explained clearly in READMEs |
You are job-ready for beginner React roles when you can build a small product flow, explain your component boundaries, handle failure states, and debug why the UI rendered the way it did.

The practical way to learn TypeScript in 2026 is to use it to make JavaScript contracts visible: function inputs, return values, object shapes, component props, API responses, and impossible UI states.
TypeScript is not a replacement for JavaScript. The TypeScript Handbook describes it as a static typechecker for JavaScript programs: it runs before your code and checks whether the types line up. That means JavaScript knowledge still comes first.
| Stage | What to learn | What it helps you prevent |
|---|---|---|
| 1 | Basic annotations and inference | Passing the wrong value type |
| 2 | Object types and interfaces | Missing or misspelled properties |
| 3 | Unions and narrowing | Unsafe access to values that may differ |
| 4 | Functions and callbacks | Wrong handler signatures |
| 5 | Generics | Reusable utilities without losing types |
| 6 | React and API types | Weak component and server contracts |
| 7 | Compiler options | Hidden any, null, and module mistakes |
Use the official TypeScript Handbook as your reference. It is designed for everyday programmers and covers the concepts you need before the more formal reference material.
TypeScript can infer many types from values:
const count = 0;const title = 'JavaScript Roadmap';const tags = ['javascript', 'react'];
You do not need to annotate those. Over-annotation makes code noisy and can hide the real contract.
Add types where they clarify a boundary:
function formatPrice(amount: number, currency: string): string {return new Intl.NumberFormat('en', {style: 'currency',currency,}).format(amount);}
The useful question is: "Where could another developer call this incorrectly?" Those places deserve explicit types.
Frontend code passes objects everywhere: API data, component props, form values, config, route params, and analytics events.
Start with object types:
type Product = {id: string;name: string;price: number;inStock: boolean;};function getDisplayName(product: Product) {return product.inStock ? product.name : `${product.name} (sold out)`;}
Learn optional properties carefully:
type User = {id: string;name: string;avatarUrl?: string;};
avatarUrl?: string means the property may be missing or undefined. Your UI needs a fallback before rendering an image.
Union types let you model choices:
type RequestState =| { status: 'idle' }| { status: 'loading' }| { status: 'success'; products: Product[] }| { status: 'error'; message: string };
This is more accurate than several booleans such as isLoading, hasError, and data, which can drift into impossible combinations.
Use narrowing to safely read the right fields:
function ProductResults({ state }: { state: RequestState }) {if (state.status === 'loading') {return 'Loading products...';}if (state.status === 'error') {return state.message;}if (state.status === 'success') {return `${state.products.length} products`;}return 'Search for a product';}
This pattern is useful in React, reducers, data fetching, form flows, and interview answers.
For larger unions, add an exhaustiveness check so future states cannot be forgotten silently:
function assertNever(value: never): never {throw new Error(`Unhandled state: ${JSON.stringify(value)}`);}function getStatusText(state: RequestState) {switch (state.status) {case 'idle':return 'Search for a product';case 'loading':return 'Loading products...';case 'success':return `${state.products.length} products`;case 'error':return state.message;default:return assertNever(state);}}
This is one of TypeScript's best frontend uses: the compiler helps you update all UI branches when a product state changes.
Many TypeScript bugs show up at function boundaries.
Practice typing:
Example:
type SaveProduct = (product: Product) => Promise<{ id: string }>;async function submitProduct(product: Product, saveProduct: SaveProduct) {const result = await saveProduct(product);return result.id;}
Do not use Function as a type. It throws away the contract. Name the parameters and return value.
Generics are for reusable code where the input and output types are connected.
function groupBy<T>(items: T[],getKey: (item: T) => string,): Record<string, T[]> {return items.reduce<Record<string, T[]>>((groups, item) => {const key = getKey(item);groups[key] ??= [];groups[key].push(item);return groups;}, {});}
The generic T keeps the item type through the function. If you pass Product[], the grouped values are still Product[].
Learn generics through small utilities before reading advanced type puzzles. Most frontend work needs clear generic functions, not clever type gymnastics.
Also learn satisfies for configuration objects. It checks that an object matches a type without throwing away the object's specific literal values:
type RouteConfig = Record<string, { title: string; requiresAuth: boolean }>;const routes = {home: { title: 'Home', requiresAuth: false },dashboard: { title: 'Dashboard', requiresAuth: true },} satisfies RouteConfig;
That is useful for design tokens, route maps, analytics event maps, and component variant configs.
TypeScript is especially useful for React props:
type ButtonProps = {children: React.ReactNode;variant: 'primary' | 'secondary';disabled?: boolean;onClick: () => void;};function Button({ children, variant, disabled, onClick }: ButtonProps) {return (<button data-variant={variant} disabled={disabled} onClick={onClick}>{children}</button>);}
This contract prevents invalid variants and makes the expected click behavior clear.
For React projects, learn:
TypeScript for React Developers is the better next step if your main goal is frontend work.
TypeScript can be weak or strict depending on your configuration. A project with too many any values gives less protection than it appears to.
Learn these options early:
strictnoImplicitAnystrictNullChecksnoUncheckedIndexedAccessexactOptionalPropertyTypesYou do not need every strict option on day one in an existing codebase. For a learning project, turn on strict and fix the errors. The compiler feedback is part of the lesson.
The best TypeScript project is not a blank file with type examples. Take a small JavaScript app and migrate it.
Good migration order:
.js to .ts and .jsx to .tsx.any values.Migration teaches you what TypeScript actually catches: misspelled fields, missing null checks, inconsistent return values, invalid component props, and weak API assumptions.
The first mistake is using any to silence the compiler. If a value is genuinely unknown, use unknown, validate or narrow it, then use it.
The second mistake is typing the wrong boundary. Typing every local variable is less useful than typing the API response, public function, or component prop.
The third mistake is believing TypeScript validates runtime data. It does not. If JSON comes from a server, TypeScript can describe what you expect, but runtime validation is still needed when the data is untrusted.
The fourth mistake is overusing advanced types. If nobody on the team can read the type, it may cost more than it saves. Prefer types that make product states and code contracts easier to understand.
The fifth mistake is treating generated API types as proof that the backend can never send bad data. Generated types are valuable, but a network boundary is still a runtime boundary. Validate user-controlled data, third-party API responses, webhooks, and anything that can drift independently of your frontend deploy.
| Week | Plan | Output |
|---|---|---|
| 1 | Inference, annotations, object types | Typed utility functions |
| 2 | Arrays, records, optional properties | Typed data transformation exercises |
| 3 | Unions, narrowing, discriminated unions | Request-state and form-state models |
| 4 | Functions, callbacks, async types | Typed API and event-handler examples |
| 5 | Generics and reusable utilities | groupBy, pick, sortBy, typed fetch wrapper |
| 6 | React or project migration | One JavaScript project converted to TypeScript |
You have learned TypeScript well enough for junior frontend work when you can type component props, API responses, event handlers, utility functions, and UI states without reaching for any as your default escape hatch.

Frontend developer jobs for freshers are possible in 2026, but a certificate is not enough. You need finished projects, clean GitHub repos, interview basics, and proof that you can complete small UI tasks.
The fresher market is crowded because many candidates look identical on paper: HTML, CSS, JavaScript, React, one course certificate, two cloned projects. Your job is to make the hiring team believe you can be onboarded into a real codebase and improve with review.
That does not require pretending to be senior. It requires specific proof. A hiring team should be able to open your portfolio and see, within two minutes, that you can build a form, handle data, write readable code, and explain one project without hiding behind course language.
The stack does not need to be exotic. The 2025 Stack Overflow Developer Survey still shows JavaScript, HTML/CSS, TypeScript, React, Next.js, and Angular as widely used among professional developers. For a fresher, that is a reason to learn the web basics deeply before chasing every new library.
Most fresher frontend roles do not expect architecture ownership. They expect a beginner who can learn quickly and avoid careless mistakes.
| Expectation | What it means in practice | How to prove it |
|---|---|---|
| HTML and CSS basics | Can build a page from a design and make it responsive | Build a polished responsive page with forms and states |
| JavaScript basics | Can handle user interaction and data changes | Build search, filter, timer, and API widgets |
| Framework basics | Can write simple React, Angular, or Vue components | Build a small product flow, not only a counter app |
| GitHub hygiene | Can share code for review | Add READMEs, live links, and clean project structure |
| Debugging attitude | Can inspect errors instead of guessing | Use DevTools and explain one bug you fixed |
| Communication in review | Can ask questions and respond to feedback | Write clear project notes and resume bullets |
Freshers lose interviews when they list too many tools and cannot explain the basics. Keep your skill list honest. If you wrote a project by following a tutorial, be ready to explain what you changed and which part you would rebuild differently.
Your first frontend role may not be titled exactly "frontend developer." Search across related titles.
| Role title | Good fit if | What to prepare |
|---|---|---|
| Frontend Developer Fresher | You have HTML, CSS, JS, and one framework project | portfolio, JavaScript basics, React or Angular basics |
| Frontend Intern | You are still learning and need supervised work | clean projects, learning attitude, availability |
| Web Developer Fresher | You are good with HTML, CSS, basic JS, CMS, or websites | responsive pages, forms, visual polish |
| UI Developer Trainee | You like layout and implementation from designs | CSS, browser behavior, accessibility basics |
| React Developer Intern | You have React projects and can explain state | React forms, lists, effects, API states |
| Full Stack Fresher | You know some backend too | basic frontend plus API and database fundamentals |
Do not reject a good internship because the title is not perfect. Your first role is about getting credible experience, feedback, and team exposure. A paid internship with code review can be more useful than a "frontend developer" title where nobody reviews your work.
Use this order. It keeps you from building a React app while still being weak at browser basics.
| Stage | Learn | Practice task |
|---|---|---|
| 1 | HTML structure, forms, labels, buttons, links, tables | build an accessible contact or signup form |
| 2 | CSS layout, Flexbox, Grid, responsive design, focus states | recreate a dashboard or settings page |
| 3 | JavaScript values, functions, arrays, objects, DOM, events | build search, filter, modal, tabs, accordion |
| 4 | Async JavaScript, fetch, promises, errors | build an API-backed search page |
| 5 | React or Angular basics | build a form-heavy product flow |
| 6 | Git, GitHub, deployment, README | publish projects and make them reviewable |
| 7 | Interview practice | solve JavaScript and UI tasks under time |
Use How to Become a Frontend Developer if you need a full roadmap before this job-search plan.
Do not build ten small clones. Build three projects that prove different frontend skills. Each project should have a deployed link, a GitHub repo, a README, and one short note explaining a decision you made.
Build one of these:
Include:
What it proves: forms, validation, state, accessibility, and attention to user mistakes.
What interviewers may ask:
Build one of these:
Include:
What it proves: async JavaScript, API handling, state changes, and user feedback.
What interviewers may ask:
Build one of these:
Include:
What it proves: state modeling, derived values, and product behavior.
What interviewers may ask:
Read How to Build a Frontend Developer Portfolio With No Experience (2026) for a deeper portfolio guide.
A fresher with clean GitHub is easier to review because many beginner repos are hard to run.
For each project:
README structure:
# Project NameShort description.## Live demoLink## Features- Search by keyword- Loading and error states- Responsive layout## Tech stackReact, TypeScript, CSS## Run locallynpm installnpm run dev## What I learnedOne or two specific lessons.
If your repo has no README, a reviewer has to work too hard. Do not make that their first impression. Also check that npm install and the documented run command work from a fresh clone; broken setup quietly kills many beginner applications.
Keep the resume simple and proof-heavy.
Recommended order:
Good headline:
Frontend developer fresher with React projects, JavaScript practice, and deployed UI work.
Weak headline:
Highly motivated individual seeking an opportunity.
Project bullet formula:
Built [project] using [stack] with [specific frontend behavior].
Examples:
Do not rely only on job boards.
Use five channels:
| Channel | How to use it |
|---|---|
| Job boards | Apply to fresher, intern, trainee, web developer, UI developer, and React intern roles |
| Company career pages | Apply directly when the role matches your project proof |
| Referrals | Send a specific project link and resume, not a vague request |
| LinkedIn posts | Reply early with a short note and portfolio link |
| Local/startup communities | Look for internships and small product teams |
Application note:
Hi [Name],I am applying for the frontend intern role. I built a React onboarding form with validation, review step, and responsive layout: [link].My portfolio and GitHub are here: [links].
Keep it short. The project link does the work. Do not attach five projects. Send the one that matches the role and make the next click obvious.
Prepare for four types of questions.
Practice:
async/awaitPractice:
For React:
For Angular:
Prepare answers for:
Practice JavaScript interview questions, React interview questions, Contact Form, Tabs, and Accordion.
| Days | Work |
|---|---|
| 1-7 | Fix HTML, CSS, JavaScript basics with small exercises |
| 8-18 | Build multi-step form and deploy it |
| 19-30 | Build API-backed search page and deploy it |
| 31-40 | Build cart, booking, or expense tracker |
| 41-45 | Clean GitHub, READMEs, screenshots, and live links |
| 46-50 | Build portfolio site and write project notes |
| 51-55 | Rewrite resume and LinkedIn profile |
| 56-60 | Apply daily and practice interview questions |
After day 60, do not disappear into another course. Keep applying, review interview feedback, and improve the weakest part of your proof.
Add one maintenance habit while applying: every Friday, open your own live links, run the projects locally, and fix one confusing README or UI state. A small broken detail can make a careful beginner look careless.
Avoid:
Your first job search is not about proving you know everything. It is about proving you can learn inside a team and finish small frontend work carefully.
Frontend developer jobs for freshers go to candidates who make their ability visible. Build three finished projects, clean up GitHub, write concrete resume bullets, and practice the basics until you can explain your own code calmly.

The best way to learn JavaScript in 2026 is to learn the language, then use it in the browser, then practice enough small programs that callbacks, promises, objects, arrays, and DOM updates stop feeling separate.
Do not start by memorizing every method on Array. Start by learning what runs, when it runs, what data changes, and what the page should show after each change.
| Stage | What to learn | What you should be able to build |
|---|---|---|
| 1 | Syntax, values, variables, functions | Small console programs |
| 2 | Arrays, objects, strings, dates, maps, sets | Data transformation utilities |
| 3 | Control flow and error handling | Input validation and retry logic |
| 4 | DOM, events, forms, accessibility basics | Interactive browser widgets |
| 5 | Async JavaScript, promises, fetch() | API-backed pages |
| 6 | Modules, tooling, linting, tests | Maintainable mini apps |
| 7 | Interview patterns and debugging | Explain and fix unfamiliar code |
MDN's JavaScript Guide is the best primary reference for the language because it covers grammar, control flow, functions, objects, promises, modules, and browser-facing usage in one place. Use it as reference material, not as a book you must finish before building.
Start with JavaScript as a programming language before jumping into React, Next.js, or Node.js.
Learn these topics in order:
null, undefined, objects, arraysconst, let, scope, reassignmentif, switch, loops, early returnstry, catch, throwing useful errorsThe goal is not trivia. The goal is to predict what a piece of code will do without running it.
For example, a beginner should be able to explain why this produces a new array and does not mutate prices:
const prices = [100, 200, 300];const discounted = prices.map((price) => price * 0.9);
That skill matters more than knowing ten array methods by name.
Most frontend work is data shaping. You receive API data, user input, configuration, or component props, then turn it into UI.
Practice with small exercises:
" React Developer " into "react developer"This is where methods like map, filter, find, some, every, reduce, sort, slice, toSorted, and Object.entries become useful.
Two details matter while practicing:
toSorted() where your browser support target allows it, or copy with [...items].sort(...) before sorting.=== is usually the default; == has coercion rules that are useful to understand but easy to misuse.Use GreatFrontEnd JavaScript interview questions when you want targeted practice. Interview-style questions are useful even before interviews because they force you to reason through edge cases.
Frontend JavaScript runs inside a browser. That means you need the DOM, events, forms, accessibility, layout interactions, storage, network requests, and browser limitations.
Build these without a framework first:
The important part is the connection between state and UI. When data changes, what should change on the page? When the user clicks, types, submits, or presses a key, which code should run?
Do not skip accessibility while learning DOM work. A button should be a button. A label should be connected to its input. Error text should be reachable by assistive technology. These habits are easier to learn early than to patch later.
Async JavaScript is where many beginners get stuck because the code does not run top to bottom in the way they expect.
Learn these concepts together:
async and awaitfetch()AbortControllerBuild a search page that calls an API when the user submits a query. Then add:
That one project teaches more useful async JavaScript than many syntax-only exercises.
For example, a plain JavaScript search flow can combine AbortController with a current-request check:
let currentController;async function runSearch(query) {currentController?.abort();const controller = new AbortController();currentController = controller;try {const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`, {signal: controller.signal,});const results = await response.json();if (controller !== currentController) {return;}renderResults(results);} catch (error) {if (error.name !== 'AbortError') {renderError('Search failed. Try again.');}}}
This is not only an API trick. It teaches the habit of asking which async result is still allowed to update the UI.
Do not install a build tool on day one. Wait until you feel the problem it solves.
You are ready for tooling when:
At that point, learn npm scripts, package installation, ES modules, Prettier, ESLint, and a test runner such as Vitest. You do not need to become a tooling expert, but you should understand what runs when you type npm run dev, npm test, or npm run build.
A good beginner project proves behavior, not only visual polish.
| Project | Skills it proves |
|---|---|
| Expense tracker | Forms, arrays, derived totals, validation |
| Weather app | fetch(), loading, errors, empty states |
| Kanban board | Drag behavior, state updates, persistence |
| Product filter page | URL state, sorting, filtering, responsive UI |
| Quiz app | Timers, scoring, state transitions |
| GitHub profile viewer | API calls, error handling, conditional rendering |
For each project, write a short README with:
That README trains you to explain technical work, which helps in interviews and portfolio reviews.
Add one small testable rule to every project. For an expense tracker, test that deleting an item updates the total. For a search page, test that the empty state appears when the API returns no results. You do not need a giant test suite as a beginner, but you should learn that behavior can be checked without clicking through the whole app by hand.
You do not need to master all JavaScript before React, but you should be comfortable with functions, arrays, objects, modules, async code, and DOM events first.
If you start React too early, every problem feels like a React problem. Many are JavaScript problems:
JavaScript gives you the mental model. React gives you a component model for building larger interfaces.
| Weeks | Plan | Output |
|---|---|---|
| 1-2 | Syntax, functions, arrays, objects | 30 small console exercises |
| 3-4 | DOM, events, forms | Counter, todo app, form validation |
| 5-6 | Async JavaScript and APIs | Search page with loading and error states |
| 7-8 | Modules, tooling, tests | Refactor one project into modules and add tests |
| 9-10 | Browser APIs and accessibility | Modal, tabs, local storage, keyboard behavior |
| 11-12 | Interview-style practice and portfolio cleanup | Two polished projects and JavaScript question practice |
Adjust the pace if you work or study full time. The sequence matters more than the calendar.
The first mistake is copying tutorials without changing the requirements. After finishing a tutorial, add one feature without instructions.
The second mistake is learning syntax without debugging. Use browser DevTools, breakpoints, console inspection, network logs, and error messages. Debugging is part of learning the language.
The third mistake is skipping edge cases. A form with only the happy path is unfinished. What happens with empty input, slow network, invalid data, duplicate clicks, or a user using only the keyboard?
The fourth mistake is treating interviews as separate from learning. Many interview questions test the same habits you need in product code: data transformation, async control, DOM behavior, and careful explanation.
You are ready for React, TypeScript, or deeper frontend work when you can:
map, filter, and reduce without guessingLearning JavaScript in 2026 is not about finishing every topic. It is about building the mental model that lets you read unfamiliar code, predict behavior, and turn browser events into correct UI.

A frontend developer portfolio should prove that you can build useful interfaces, explain your decisions, and handle the messy parts of frontend work: state, data, accessibility, performance, errors, and product tradeoffs.
The goal is not to collect pretty screenshots. A strong portfolio makes it easy for a hiring team to see what you can be trusted to own next.
If you have no experience yet, start with How to Build a Frontend Developer Portfolio With No Experience. This guide is for the main portfolio decision: what to build, what to show, and how to stand out once you have projects, internships, freelance work, open-source work, or professional experience to present.
A useful portfolio also avoids a trap: it does not turn every project into a perfect success story. Reviewers trust portfolios that explain constraints, tradeoffs, bugs, and follow-up work. Perfect narratives sound less believable than specific ownership.
Your portfolio should answer these questions:
| Question | Weak answer | Stronger proof |
|---|---|---|
| What kind of frontend work do you do? | "I build web apps" | product frontend, design systems, platform, performance, or UI engineering examples |
| What scope can you own? | "I worked on features" | scoped feature, product flow, migration, shared component, or cross-team project |
| How do you make decisions? | list of tools | tradeoffs around state, data, performance, accessibility, and maintainability |
| How do you handle quality? | "I write clean code" | tests, review notes, accessibility checks, performance measurements, bug prevention |
| How do you work with teams? | "team player" | examples of design, backend, product, QA, or stakeholder collaboration |
Do not use the portfolio only as a visual resume. Use it as evidence of your judgment. A reviewer should leave with a clear idea of your likely level: junior feature contributor, mid-level feature owner, senior product engineer, design-system specialist, platform engineer, or lead.
Keep the site simple. Many candidates overbuild the shell and under-explain the work.
Use this structure:
| Section | What it should do |
|---|---|
| Home | state your frontend lane, seniority, strongest work, and contact links |
| Selected work | 2-4 detailed case studies |
| Technical notes | short notes on architecture, performance, accessibility, or testing |
| Code samples | public repos, open-source work, or simplified demos |
| Experience | roles, product areas, scope, and stack |
| Contact | email, LinkedIn, GitHub, resume |
Do not bury the case studies behind animations. A hiring manager or engineer may only spend a few minutes on the site before deciding whether to talk to you.
Put the best case study above any long bio. The first click should answer: what did you own, what was hard, and why does this work matter?
Your portfolio should match the next role you want.
| Target role | What to emphasize | What to avoid |
|---|---|---|
| Senior frontend engineer | ownership, tradeoffs, code quality, mentoring, product flows | only pretty screenshots |
| Product frontend engineer | user flows, API states, UX details, release decisions | isolated components with no product context |
| Design systems engineer | component APIs, accessibility, docs, adoption, migration | a random set of UI elements with no usage examples |
| Frontend platform engineer | tooling, build speed, testing infrastructure, migrations | only app feature work |
| Performance-focused frontend engineer | measurement, diagnosis, render cost, loading strategy | vague claims about speed |
| Remote frontend engineer | async docs, case studies, self-directed delivery | portfolio with no written explanation |
| Lead frontend engineer | technical direction, review quality, cross-team decisions | only individual implementation details |
This does not mean you need seven portfolios. It means your homepage and selected work should point toward one main story. If you want senior frontend roles, do not lead with a landing page clone. If you want design-system roles, do not hide component API decisions behind screenshots.
Project cards say what you built. Case studies show how you think.
Use this case study structure:
Problem:What product or engineering problem existed?Context:What team, user, system, or constraint mattered?My role:What did I personally own?Frontend decisions:State, data fetching, component boundaries, routing, accessibility, performance, testing.Tradeoffs:What did I choose not to do and why?Quality:How did I check the work?Result:What changed for users, the team, or the codebase?Next:What I would improve with more time.
You can write a good case study even if you cannot share numbers. Be precise about the shape of the work.
Weak:
Built a dashboard using React and TypeScript.
Better:
Owned a customer operations dashboard with URL-based filters, paginated API data, row-level actions, and retry handling. Split table state from server state so filters could be shared through links and restored after refresh.
Most experienced developers cannot share private code. That is normal.
Use one of these options:
| Constraint | Portfolio solution |
|---|---|
| Cannot show code | write an anonymized case study |
| Cannot show screenshots | recreate a simplified UI with fake data |
| Cannot name company | describe the domain and team size generally |
| Cannot share metrics | describe the user or engineering problem that improved |
| Work was team-owned | state your exact contribution honestly |
| Work was internal tooling | explain workflow, users, and constraints |
Do not invent impact. Hiring teams can usually spot inflated stories. Honest, specific ownership is stronger than fake numbers.
Better than a fake metric:
If you need public proof, build demos that mirror professional frontend challenges.
Include:
Explain state ownership and API assumptions. Include at least one failure path; experienced frontend work is often judged by what happens when the data is late, missing, invalid, or unauthorized.
Include:
Explain component API decisions. This is where experienced frontend judgment shows. For example, state which props are intentionally supported, which variants you rejected, and how keyboard behavior is handled.
Build or document:
Do not say "optimized." Say what was slow, how you measured it, and how you changed it.
Write a short technical note about:
Technical writing can be portfolio proof if it shows practical judgment.
Your code samples do not need to be huge. They need to be reviewable.
Good code samples show:
Avoid:
A strong portfolio should show that you can keep code boring where boring is better. If a sample uses an abstraction, explain the repeated problem it solves.
Quality separates useful portfolios from project galleries.
| Area | Portfolio evidence |
|---|---|
| Accessibility | keyboard path, labels, focus management, semantic HTML, modal behavior |
| Performance | what you measured, what was slow, what changed |
| Testing | which behavior deserved tests and why |
| Maintainability | component boundaries, naming, data flow, documentation |
| Product thinking | empty states, permission states, recovery paths, error copy |
| Review | how you split work, handled feedback, or improved a pattern |
You do not need to show all of this in every case study. Pick the details that matter for the work. One strong accessibility note beats a generic claim that the whole site is accessible.
Your homepage should be direct.
Good:
Senior frontend engineer focused on React, TypeScript, design systems, and data-heavy product UI.
Good:
Frontend engineer building accessible dashboards, form-heavy workflows, and maintainable component systems.
Weak:
I create beautiful digital experiences that delight users.
The weak version could describe almost anyone. The good versions help a hiring team place you.
Before sharing the portfolio:
Send the portfolio to one engineer and ask: "What role do you think this portfolio is targeting?" If they cannot tell, tighten the story.
Avoid:
The point is not to impress everyone. It is to make the right hiring team trust you faster.
Before sending the portfolio, read it like a skeptical interviewer:
A frontend developer portfolio should prove scope. Show what you owned, how you made decisions, how you checked quality, and why your frontend work made the product, user flow, or codebase better.

To build a frontend developer portfolio with no experience, create a simple portfolio site, add three finished projects, write project notes, deploy everything, and make your GitHub easy to review.
This guide is for freshers who are staring at a blank page and do not know where to start. You do not need a job history, a famous open-source profile, or an award-winning design. You need proof that you can build small frontend projects carefully.
Your first portfolio should answer a simple question: "Can this person be trusted with a junior frontend task?" The answer should come from the work itself, not from adjectives in the About section.
A beginner portfolio is not a personal brand campaign. It is a hiring artifact.
It should prove:
If your portfolio does that, it is already better than many beginner portfolios.
The easiest review path is:
If any step breaks, fix that before redesigning the site.
Keep the site simple.
Required sections:
| Section | What to include |
|---|---|
| Home | name, target role, one-line summary, links |
| Projects | three selected projects with live links and GitHub links |
| Skills | only tools you can explain |
| About | short learning story and work preference |
| Contact | email, GitHub, LinkedIn, resume |
Good homepage headline:
Frontend developer fresher building React, JavaScript, and responsive web projects.
Good supporting text:
I build small product-style interfaces with forms, API data, responsive layouts, and clear project notes.
Avoid:
Motivated developer creating digital solutions.
That tells the reader nothing.
Three finished projects are enough for the first version. Finished means deployed, documented, responsive enough to inspect on a phone, and honest about what is mocked.
| Project | Skill it proves | Why it helps |
|---|---|---|
| Multi-step form | forms, validation, state, accessibility | many junior tasks involve forms |
| API-backed search or dashboard | fetch, loading, empty, error, retry | proves you can work with data |
| Cart, booking flow, or expense tracker | user actions, derived state, persistence | proves interactive app logic |
Do not copy a tutorial exactly. If you use a tutorial for learning, change the requirements until the project becomes yours. Add one feature the tutorial did not include, change the data shape, and write down the decision you had to make.
Build a form that feels like a small product workflow.
Good ideas:
Required features:
Review details that matter:
Beginner version:
Improved version:
Project note:
What I built:A three-step application form with validation and review.Frontend decisions:I kept all form values in one state object so the review screen could read them easily. I validate each step before moving forward.What I learned:Form UX is not only inputs. Disabled states, error messages, and review screens matter.What I would improve:I would add server-side validation and better autosave.
Build something that uses data.
Good ideas:
Required features:
Review details that matter:
Beginner version:
Improved version:
Project note:
What I built:An API-backed product search page with filters and details.Frontend decisions:I separated the search input from the submitted query so the API is not called on every keypress.What I learned:The empty state and error state need different messages.What I would improve:I would add pagination and cache repeated searches.
Build something where user actions change state.
Good ideas:
Required features:
Review details that matter:
Beginner version:
Improved version:
Project note:
What I built:An expense tracker with categories, filters, and monthly totals.Frontend decisions:I calculate totals from the transaction list instead of storing totals separately.What I learned:Duplicate state can create bugs when values change in multiple places.What I would improve:I would add import/export and charts.
Each project page should have more than a screenshot.
Use this structure:
| Section | What to write |
|---|---|
| What it is | one paragraph explaining the project |
| Features | short bullets |
| Screens | key screenshots or GIF |
| Frontend decisions | state, API, layout, validation, or accessibility |
| What I learned | specific lessons |
| What I would improve | honest next step |
| Links | live demo and GitHub |
This helps interviewers see that you understand the work behind the final screen. It also gives you better interview answers because you are not trying to remember vague project details under pressure.
Clean GitHub is part of the portfolio. A reviewer may open the repo before the live demo, so do not treat GitHub as an afterthought.
For each repo:
.env.example if needed.README template:
# Project NameShort description.## Live demoLink## Features- Feature 1- Feature 2- Feature 3## Tech stackReact, TypeScript, CSS## Run locallynpm installnpm run dev## NotesOne tradeoff or limitation.
Your resume should point to the same proof.
Project bullets:
Avoid:
The resume gets attention. The portfolio earns the interview.
Make the order match the role. If the job asks for React, put the React project first. If it asks for UI developer skills, put the responsive page or form first. Do not make the reviewer hunt for the relevant proof.
If you feel stuck, follow this plan.
| Days | Work |
|---|---|
| 1-2 | Create portfolio shell and deploy it |
| 3-7 | Build multi-step form |
| 8-12 | Build API-backed search or dashboard |
| 13-16 | Build cart, booking flow, or expense tracker |
| 17 | Write READMEs |
| 18 | Write project pages |
| 19 | Fix mobile layout and broken links |
| 20 | Add resume and contact links |
| 21 | Apply to 10 relevant roles |
Your portfolio will not be perfect after 21 days. That is fine. A real, reviewable portfolio beats a perfect portfolio that never ships.
Before applying, do one clean-room check: open each project in an incognito window, click the main flow, resize to mobile width, and follow the README run command in a fresh folder. This catches the boring failures reviewers notice first.
After the first version:
The portfolio should improve through feedback, not endless redesign.
Avoid:
The best beginner portfolio is honest. It shows your current level and gives a team confidence that you can grow. If something is mocked, say it is mocked. If a feature is incomplete, name the next step instead of pretending it is done.
A frontend developer portfolio with no experience should make your learning visible. Build three useful projects, explain them clearly, clean up GitHub, and apply before perfection becomes another way to procrastinate.

A frontend developer roadmap in 2026 should start with the browser, then move through JavaScript, TypeScript, one major frontend framework, app architecture, accessibility, testing, performance, and interview practice. The right roadmap is not a list of every tool. It is an order of learning that keeps you employable without drowning you in optional technology.
Use this roadmap as a skills map. If you are starting from zero and need the broader beginner journey, read How to Become a Frontend Developer in 2026. This article is the detailed map of what to learn, what to skip for now, and what evidence each stage should produce.
| Stage | Learn | Build | You are ready to move on when |
|---|---|---|---|
| Web platform | HTML, CSS, browser behavior, DevTools | Static pages, forms, responsive layouts | You can explain layout, semantics, events, and network requests without a framework |
| JavaScript | Data structures, async code, DOM, modules | Search, filters, timers, API-backed widgets | You can debug state and async behavior from the console |
| TypeScript | Props, unions, generics, narrowing, API types | Typed forms, typed API responses, reusable components | You can remove unsafe any instead of hiding errors |
| Frontend framework | Components, state, lifecycle, composition, routing basics | Stateful UI flows, dashboards, product screens | You know where state belongs and how the framework updates the page |
| App architecture | Routing, data fetching, auth states, server/client boundaries | A multi-page product workflow | You can handle loading, empty, error, permission, and recovery states |
| Quality | Accessibility, testing, performance, security basics | Tested flows, keyboard-safe forms, measured pages | You can prove the UI works, not only that it renders |
| Interview readiness | JavaScript, framework concepts, CSS, APIs, system design | Timed exercises and explainable projects | You can solve, explain, test, and improve under time pressure |
Do not learn all rows at the same depth. A junior candidate needs broad competence and proof through projects. A senior candidate needs deeper tradeoff judgment, architecture, debugging, and mentoring evidence.
The core order has not changed: HTML, CSS, JavaScript, and the browser still come first. The emphasis has changed.
Framework choice matters, but it should come after the platform basics. React, Angular, Vue, and Svelte can all be valid choices depending on your target companies, local market, team stack, and learning style. React is common in many frontend roles, Angular is still common in enterprise teams, Vue is popular for approachable product development, and Svelte is worth considering when you like compiler-driven UI with less runtime code. Pick one main framework and get good enough to build complete flows.
TypeScript is also harder to treat as optional. Many modern frontend teams use it for framework apps, design systems, API contracts, and shared component libraries. For frontend learning, that means TypeScript is not a senior-only skill anymore.
Build tooling has become more practical. Vite, framework CLIs, and faster bundlers mean most learners do not need to hand-configure Webpack early. Learn what frameworks solve, but do not let a framework hide weak browser knowledge.
AI belongs in the roadmap too. It can explain errors, generate test cases, suggest refactors, and compare approaches. It should not replace reading docs, debugging in DevTools, writing code by hand, or reviewing accessibility behavior.
The browser is the runtime for every frontend framework. Weak browser knowledge shows up later as layout bugs, broken forms, poor accessibility, hydration mismatches, and confusing network behavior.
Learn HTML as page structure:
Learn CSS as layout and state:
Learn browser behavior:
Practice target:
| Project | What to prove |
|---|---|
| Responsive settings page | Layout, form controls, validation text, keyboard behavior |
| Pricing page with FAQ | Semantic structure, responsive CSS, disclosure state |
| API-backed profile card | Fetch, loading state, error state, retry behavior |
Frontend JavaScript is not only syntax. It is data changing over time while the user clicks, types, waits, navigates, and loses network access.
Prioritize:
async/await, timers, cancellation, and race conditionsBuild small exercises that reveal mistakes:
| Exercise | Mistake it catches |
|---|---|
| Debounced search | stale closures, repeated requests, missing empty state |
| Sortable table | mutation bugs, unstable sorting, weak rendering logic |
| Todo app with persistence | storage assumptions, serialization, state recovery |
| Polling status widget | timers, cleanup, network errors, duplicate updates |
Interview practice should begin here. Start with JavaScript interview questions, then add timed practice once the basics are steady.
TypeScript helps most when it models the messy edges of UI: optional API fields, unknown JSON, reusable component props, form values, and state transitions.
Learn:
typeof, in, discriminated unions, and custom guardsunknown over any when data comes from outside the appDo not begin with advanced type puzzles. A frontend developer gets more value from correctly typing API boundaries and component contracts than from writing clever utility types nobody wants to maintain.
Practice target:
| Build | TypeScript skill |
|---|---|
| Typed API client | response types, error types, unknown data |
| Reusable input component | props, event handlers, controlled values |
| Filter state model | unions, literals, derived state |
| Form result type | success/error states, discriminated unions |
Use TypeScript interview questions when you can explain the types in your own words. If you choose React, read TypeScript for React Developers for framework-specific practice.
A frontend framework helps you organize state, components, routing, forms, data loading, and shared UI. The important skill is deciding what changes, where it lives, and how the page should respond. React is a practical default for many learners, but it is not the only correct choice.
Pick based on your goal:
| Framework | Good choice when |
|---|---|
| React | Your target jobs mention React, Next.js, design systems, dashboards, or UI coding interviews |
| Angular | Your target jobs are enterprise teams with larger applications, strict conventions, and TypeScript-heavy codebases |
| Vue | You want an approachable framework with clear templates, component structure, and a strong product-app ecosystem |
| Svelte | You prefer compiler-driven UI, less boilerplate, and a smaller mental model for reactive components |
Whichever framework you choose, learn the same core ideas:
Learn:
For modern frameworks, add:
Do not make every project a framework project too early. A framework should clarify UI state, not compensate for weak HTML, CSS, or JavaScript.
If you choose React, practice with React interview questions, then build UI tasks such as Contact Form, Data Table, Tabs, and Image Carousel. If you choose Angular, Vue, or Svelte, build the same kinds of components in that framework: forms, tables, tabs, autocomplete, modal flows, dashboards, and data-backed product screens.
Many candidates can write components. Fewer can build a full product flow that behaves carefully under failure.
Learn:
Framework and meta-framework choice:
| Choice | When it makes sense |
|---|---|
| React with Vite | Learning React, building dashboards, practicing UI coding, smaller client-heavy apps |
| Next.js | React teams that need routing conventions, server-rendered pages, content-heavy apps, or auth-heavy products |
| Angular | Enterprise apps that benefit from built-in conventions, dependency injection, routing, forms, and TypeScript-first structure |
| Vue with Vite or Nuxt | Product apps where template clarity, component ergonomics, and progressive adoption matter |
| Svelte or SvelteKit | Smaller apps or teams that prefer compiler-driven reactivity and less runtime ceremony |
| Remix or React Router framework mode | React apps that lean heavily on routes, forms, data loading, and web-standard request handling |
Pick one main framework after the browser and JavaScript basics. Learn enough of the others to read job descriptions, not enough to split your attention across three stacks.
Accessibility is not a separate phase you bolt on before launch. It affects the elements you choose, the state you expose, the keyboard behavior you support, and the way errors are announced.
Learn:
Project checks:
Use accessibility practice inside every project. A keyboard-broken form is not job-ready UI.
Testing is easier to learn when the goal is clear: protect important behavior from regressions.
Learn:
Good first tests:
| Feature | Useful test |
|---|---|
| Form | Shows validation errors, disables submit while pending, recovers after API failure |
| Data table | Applies filters, preserves sorting, handles empty results |
| Auth-gated page | Redirects unauthenticated users and preserves destination |
| Upload flow | Shows progress, handles cancellation, displays retry |
Testing should not wait until a project is finished. Add tests when behavior becomes easy to break.
Frontend performance in 2026 is not only bundle size. It includes loading, rendering, images, fonts, third-party scripts, data waterfalls, server response time, and main-thread work.
Learn:
Performance practice:
Do not start with advanced micro-optimizations. A missing image size, a client-only marketing page, or a slow API waterfall usually matters more than a premature useMemo.
Tools should reduce friction. They are not the roadmap.
| Tool area | Learn first | Learn later |
|---|---|---|
| Editor | VS Code or your preferred editor, debugger, TypeScript errors | custom editor automation |
| Package manager | npm or pnpm basics, scripts, lockfiles | workspace tuning |
| Build tool | Vite or framework CLI | custom bundler configuration |
| Version control | Git branches, commits, pull requests, conflict resolution | advanced rewriting workflows |
| Styling | CSS modules, Tailwind, or the team's system | design token pipelines |
| Data fetching | fetch, framework loaders/actions, TanStack Query basics | custom cache layers |
| Testing | Vitest/Jest, Testing Library, Playwright | visual regression infrastructure |
| Deployment | Vercel, Netlify, Cloudflare, or company CI basics | multi-region rollout strategy |
The best tool choice is the one that lets you build and debug more product behavior. If a tool takes a week to configure before you understand the problem it solves, postpone it.
A roadmap needs proof. Courses and notes are not enough.
| Stage | Project | Requirements |
|---|---|---|
| Platform | Accessible form flow | labels, validation, keyboard behavior, responsive layout |
| JavaScript | Searchable data table | fetch, sorting, filtering, pagination, empty state |
| TypeScript | API-backed dashboard | typed response, error model, reusable chart/list components |
| Framework | Product settings app | tabs, forms, optimistic save, dirty-state warning |
| App architecture | Mini SaaS workflow | auth states, routing, permissions, billing-like flow, tests |
| Quality | Performance and accessibility pass | measured before/after notes, keyboard audit, critical tests |
| Interview | Timed UI challenges | explainable solution, edge cases, cleanup, follow-up improvements |
Each finished project should include a short engineering note:
That note matters. It turns a demo into hiring evidence.
The roadmap changes by target level.
| Target | Spend more time on | Spend less time on |
|---|---|---|
| Beginner | HTML, CSS, JavaScript, one framework, portfolio projects | framework debates, advanced state libraries, microfrontends |
| Internship or junior | forms, API integration, debugging, TypeScript basics, GFE practice | complex architecture diagrams |
| Mid-level | feature ownership, state modeling, tests, accessibility, performance | copying tutorials |
| Senior | tradeoffs, system design, migrations, cross-team contracts, production debugging | isolated toy apps |
| Specialist | one depth area such as design systems, frontend infrastructure, accessibility, or performance | trying to specialize before shipping product UI |
For senior growth, pair this roadmap with Senior Frontend Developer Skills. For title progression and scope, use Frontend Developer Career Path.
Interview preparation should track the same skill order, but with tighter feedback loops.
| Area | Practice |
|---|---|
| JavaScript | arrays, objects, promises, closures, timers, event loop, DOM manipulation |
| CSS | layout, responsive behavior, specificity, positioning, accessible states |
| Framework | state, lifecycle, forms, rendering, component design |
| APIs | REST, status codes, pagination, caching, retries, error states |
| TypeScript | component props, unions, generics, narrowing, API data |
| UI coding | forms, tables, autocomplete, tabs, carousel, file explorer |
| System design | data flow, component boundaries, performance, accessibility, failure modes |
Start with quiz questions for fast feedback. Use user interface coding questions when you need implementation practice. Add front end system design questions once you can build product flows without getting stuck on basic component state.
The most common mistake is learning tools in the order they look exciting instead of the order they remove real bottlenecks.
Avoid these traps:
The fix is simple but not easy: build smaller complete flows, test the uncomfortable states, and explain the tradeoffs.
This timeline assumes consistent part-time study. Move faster if you already program. Move slower if the projects feel shallow.
| Timeframe | Main goal | Output |
|---|---|---|
| Weeks 1-4 | Browser, HTML, CSS, JavaScript basics | responsive form and API widget |
| Weeks 5-8 | JavaScript depth and DOM behavior | searchable table, debounced search, persisted state |
| Weeks 9-12 | TypeScript and framework basics | typed components and form flow |
| Months 4-5 | App architecture | multi-page app with routing, auth states, API data, and error handling |
| Month 6 | Quality pass | tests, accessibility fixes, performance notes, deployed portfolio |
| Months 7-12 | Interview and depth | GFE practice, system design, stronger projects, specialist exploration |
The calendar is only useful if the outputs are real. A shallow six-month checklist is weaker than three carefully built projects that show product judgment.
A good frontend developer roadmap in 2026 is selective. Learn the platform, add JavaScript depth, use TypeScript to model risk, choose one frontend framework, build full product flows, and prove quality through accessibility, tests, and performance work.
The goal is not to know every frontend tool. The goal is to become the person a team can trust with user-facing behavior.

In India, a typical frontend developer salary in 2026 sits around Rs 6.4-7.5 LPA in broad self-reported datasets, but product companies, GCCs, and senior roles can pay much higher. Treat averages as a starting point, not your ceiling.
Salary data is messy because every site measures a different slice of the market. Some report base pay, some total pay, some job listings, and some self-reported profiles.
| Source | Latest accessible data | What it says | Caveat |
|---|---|---|---|
| Glassdoor India | Updated May 9, 2026, 4.1K salary submissions | Median total pay around Rs 6.4 LPA, with a reported range around Rs 4.49-10.2 LPA and 90th percentile around Rs 18.67 LPA | Self-reported and title-dependent |
| PayScale India | Updated May 14, 2026, 927 profiles | Average base salary Rs 6.57 LPA, base range around Rs 2.91-20 LPA | Base salary focus; sample skews by profile submissions |
| Indeed India | Updated May 8, 2026, 205 salary reports | Average base salary around Rs 6.86 LPA, with higher city and company examples | Smaller sample and listing/reporting mix |
| NodeFlair India | Updated June 8, 2026 | Median base salary Rs 62,500 per month, or about Rs 7.5 LPA; range Rs 37,500-1,87,500 per month | Mixes verified salaries and curated job listings |
| Adecco India Salary Guide 2026 | 2026 guide | Front-end developer bands from about Rs 5-10 LPA at 0-3 years to about Rs 40-73 LPA at 15+ years | Recruiter/employer guide, not a self-reported salary dataset |
| AmbitionBox | Latest accessible frontend software developer result was updated in 2025 | Historical context showed roughly Rs 2-16 LPA for less than 1 year to 4 years | Treat as 2025 context unless the page refreshes with 2026 data |
For early-career roles, Rs 6-8 LPA is a reasonable broad-market midpoint. Strong product companies, GCCs, funded startups, and senior specialist roles can move much higher.
The sources are useful, but none of them should be treated as a perfect salary truth.
Glassdoor and PayScale are good broad-market anchors because they have salary-profile samples and publish clear ranges. They are weaker for top-of-market product roles because titles and seniority can be inconsistent.
Indeed is useful for live market signals because it connects salaries, job postings, cities, and companies. Its current average is based on a smaller sample than Glassdoor, so use it to compare patterns rather than as the final number.
NodeFlair is useful because it presents monthly base salary and current job-listing context. The caveat is that recently submitted salaries may lag behind the page update date, so the salary range should be treated as a directional benchmark.
Adecco is useful as an employer-side salary guide. It is especially helpful for experience bands, but it is not a self-reported employee salary database.
AmbitionBox is useful for India-specific company and title context. For this article, the latest accessible frontend software developer result is still 2025, so it should support historical context only, not the headline 2026 number.
Glassdoor, PayScale, Indeed, NodeFlair, Adecco, and AmbitionBox are not measuring the same thing.
The biggest differences are:
Two developers with the same title can see very different numbers. A frontend developer building marketing pages at a services firm and a frontend engineer owning a high-traffic product surface at a GCC are both "frontend developers" in salary databases. They are not the same market profile.
Use these bands as a practical reading of the 2026 market, not as guaranteed offers.
| Experience | Broad-market expectation | Product company or GCC upside | What usually changes pay |
|---|---|---|---|
| 0-2 years | Rs 3-7 LPA | Rs 5-10 LPA | Web foundations, React basics, TypeScript, portfolio quality, internships |
| 2-5 years | Rs 6-14 LPA | Rs 10-22 LPA | Owning features, API integration, testing, performance awareness |
| 5-8 years | Rs 12-24 LPA | Rs 18-40 LPA | UI architecture, design systems, accessibility, mentoring, delivery judgment |
| 8-12 years | Rs 20-35 LPA | Rs 32-60 LPA | Leading frontend scope, cross-team decisions, performance and reliability ownership |
| 12+ years | Rs 30-50+ LPA | Rs 45-75+ LPA | Staff, principal, lead, architect, or manager scope; company type matters heavily |
The jump from mid-level to senior is where frontend pay starts to diverge. Years alone do not create the jump. Scope does.
Employer type often matters more than the title.
| Employer type | Typical pattern |
|---|---|
| IT services firms | More structured bands, slower compensation growth, title inflation possible |
| Indian product startups | Higher upside when the company is funded and frontend is core to the product |
| GCCs and multinational product teams | Stronger pay for engineers who meet a higher interview bar |
| Agencies | Wide range; depends on client quality and technical depth |
| Early-stage startups | Cash may be lower or uneven, but scope can be high |
| Large consumer internet companies | Strong pay for complex UI, performance, and product ownership |
If you are comparing two offers, do not stop at CTC. Ask what the frontend team owns. A lower-looking offer can be better if the work builds stronger experience, but only if compensation, mentorship, and company quality are still reasonable.
Use How to Evaluate Companies as a Front End Engineer before accepting a role.
Indeed's 2026 India salary page surfaced higher average salaries in Hyderabad, Gurgaon, Bengaluru, Pune, and Ahmedabad. Its current city examples include Hyderabad around Rs 11.12 LPA, Gurgaon around Rs 8.55 LPA, Bengaluru around Rs 7.96 LPA, Pune around Rs 7.92 LPA, and Ahmedabad around Rs 7.40 LPA. Treat city data carefully because sample sizes can be small, and one or two high-paying employers can move the average.
In practice:
Location helps, but it does not replace skill and company selection.
One common mistake in India salary research is mixing base salary, CTC, total pay, bonus, stock, and monthly take-home as if they were the same thing.
When reading an offer, separate:
A Rs 18 LPA CTC offer with a high variable component can be weaker than a Rs 15 LPA offer with cleaner fixed pay. A startup ESOP component can be meaningful, but only if you understand vesting, strike price, liquidity, and whether the company has a realistic path to an exit.
For negotiation, ask for the compensation breakup before comparing offers. Averages from salary sites are useful only after you know what number you are comparing against.
The highest-paid frontend engineers are rarely paid only for React syntax. They are paid because they reduce product and engineering risk.
Skills that move compensation:
If your CSS is weak, fix it. The top CSS mistakes made by front-end engineers are still common in paid work.
To build interview evidence for higher-paying frontend roles, practice TypeScript interview questions, React interview questions, UI coding questions, and system design questions. These are closer to the signals product companies and GCCs usually test than generic portfolio pages.
Do not walk into negotiation with one average number. Use a range and explain your fit.
Better negotiation evidence includes:
For example, "Glassdoor and PayScale put the broad market around Rs 6.4-6.6 LPA, but this role is a TypeScript-heavy product role with design system ownership. Based on the scope and my experience, I am targeting X" is stronger than quoting a single website average.
Frontend developer salary in India in 2026 is not one number. Broad datasets cluster around Rs 6.4-7.5 LPA, but roles at stronger frontend teams can move far above that when the engineer owns product quality, performance, accessibility, TypeScript, and cross-team UI architecture.
Use salary sites to anchor the conversation. Use skill depth and company selection to change the conversation.

The frontend developer career path moves from completing scoped tasks to owning product outcomes. In 2026, progression is less about years and more about scope, judgment, verification, and impact.
Titles vary by company, but the pattern is similar: junior, mid-level, senior, staff or lead, and sometimes manager.
| Level | Typical scope | What the role proves |
|---|---|---|
| Learner | Practice projects and foundations | You can build basic UI and explain your code |
| Junior frontend developer | Scoped tasks inside an existing codebase | You can follow patterns and ask good questions |
| Mid-level frontend developer | Features with moderate ambiguity | You can own delivery with limited guidance |
| Senior frontend developer | Ambiguous product work and technical decisions | You can reduce risk and guide others |
| Staff or lead frontend engineer | Cross-team frontend direction | You can multiply the work of several engineers |
| Engineering manager | People, delivery, and team health | You can build the environment where engineers do good work |
The ladder is not universal. Startups blur levels. Big companies define them more formally. Some engineers stay technical. Some move into management. Some specialize deeply.
The learner stage is about becoming hireable. You need web foundations, a framework, and enough product sense to build useful projects.
A junior frontend developer is expected to:
To move from learner to junior, prove that you can finish small work reliably. Your portfolio should show forms, API usage, responsive layout, loading states, and error states. A pile of cloned landing pages is weaker than one product workflow that behaves carefully.
Use How to Become a Frontend Developer in 2026 if you need the learning sequence.
The mid-level jump is about independence. A mid-level frontend developer can take a feature, clarify requirements, make reasonable technical choices, and deliver without needing constant correction.
At this stage, you should become solid at:
The signal is reliability. Your team should trust you with feature work that has some unknowns.
To reach mid-level, stop waiting for perfect tickets. Learn to ask the questions that make the work clear:
For this stage, practice JavaScript questions, React questions, and focused UI tasks such as Contact Form, Data Table, and Auth Code Input.
The senior jump is about judgment. A senior frontend developer does more than finish bigger tickets. They prevent bad decisions from becoming expensive.
Senior frontend engineers are trusted with:
They also know when not to add complexity. Seniority is not the number of abstractions you create. It is the quality of your tradeoffs.
In the AI era, this level matters more. Generated code can create a lot of output quickly, but senior engineers verify whether the output is correct, accessible, maintainable, and aligned with the product.
At this point, add TypeScript questions, front end system design questions, and larger UI problems such as File Explorer.
Read Senior Frontend Developer Skills: What You Need to Land the Role in 2026 for the deeper skill map.
Promotion is easier when you collect evidence before the conversation starts. Do not rely on "I have been here for two years" as your main case.
| Target level | Evidence that helps |
|---|---|
| Junior to mid-level | Features shipped with less supervision, fewer repeated review comments, cleaner state handling, and better debugging notes |
| Mid-level to senior | Ambiguous work clarified, technical risks named early, components or flows improved for other engineers, and quality issues prevented |
| Senior to staff or lead | Cross-team problems solved, migrations guided, standards adopted, measurable performance or reliability improvements, and other engineers made more effective |
| Engineer to manager | Hiring support, mentoring, planning, feedback, team health, and delivery improvements beyond personal coding output |
Write this evidence down as it happens. Promotion packets are much easier when you have concrete examples instead of trying to reconstruct a year of work from memory.
Staff and lead frontend roles are about broader influence. You are no longer measured only by your own delivery. You are measured by whether teams make better technical decisions because you are involved.
Typical work includes:
Staff engineers do not need to be the loudest people in the room. They need to notice important problems, frame them clearly, and help teams make progress.
Lead titles vary. In some companies, "lead" means technical leadership. In others, it includes people management. Clarify that before accepting the role.
Frontend developers can also move into engineering management. Management is a different job, not a promotion that simply means "more senior engineer."
Engineering managers work on:
Good managers need technical context, but they are not judged mainly by their code. They are judged by whether the team can do good work consistently.
If you love the technical depth of frontend, you do not have to become a manager. A senior, staff, or principal frontend path can remain technical.
Frontend has several specialist paths. These can exist at mid-level, senior, or staff scope depending on the company.
| Track | What you work on |
|---|---|
| Product frontend engineer | Complex product flows, UX behavior, and feature delivery |
| Design systems engineer | Shared components, tokens, accessibility, documentation, and adoption |
| Frontend platform engineer | Build tooling, monorepos, CI, testing infrastructure, and developer experience |
| Web performance engineer | Metrics, rendering, loading, bundle cost, and runtime behavior |
| Accessibility specialist | Usability across assistive technology, keyboard behavior, semantics, and audits |
| Frontend-heavy fullstack engineer | UI plus API and backend changes for feature ownership |
| Web platform specialist | Browser APIs, offline behavior, media, graphics, WebAssembly, or advanced runtime work |
Specializing too early can narrow your options. Build product frontend competence first, then choose depth.
Career paths are cleaner on paper than inside companies.
At a startup, a "frontend developer" may own product UI, backend routes, analytics events, customer bugs, and deployment fixes in the same week. You might gain senior scope before you have a senior title.
At a large company, the title may be slower, but the expectations are clearer. You may need promotion evidence, peer feedback, technical design examples, and cross-team impact.
Neither model is automatically better. The key is knowing what evidence you are building.
The fastest career growth usually comes from work that increases trust.
Useful habits:
For company selection, use How to Evaluate Companies as a Front End Engineer. The company you choose can accelerate or slow your path.
The frontend developer career path in 2026 is not "junior writes components, senior writes more components." It moves from task completion to product and technical ownership.
Your title will vary by company. Your real growth shows in the size of the problems people trust you to handle, the quality of your decisions, and the number of people whose work gets better because of yours.

A senior frontend developer is not measured by component speed alone. In 2026, seniority means better judgment: you can own ambiguous UI work, verify AI-assisted code, prevent regressions, and make product interfaces reliable.
The bar has moved. Code generation is cheaper. Correctness, taste, tradeoffs, and ownership matter more.
A junior developer is usually trusted with well-scoped tasks. A mid-level developer can own features with some guidance. A senior frontend developer can take unclear product goals, break them into technical work, identify risks, coordinate with other functions, and ship something the team can maintain.
That does not mean seniors stop coding. It means their code sits inside a larger responsibility.
Senior frontend engineers are expected to answer questions like:
| Area | Mid-level habit | Senior signal |
|---|---|---|
| Component work | Builds components from designs | Designs component APIs that are hard to misuse |
| State | Makes the current feature work | Chooses state boundaries that survive future changes |
| CSS | Fixes layout bugs | Prevents layout classes, overflow, and responsive bugs through better structure |
| Accessibility | Runs a checklist near the end | Builds keyboard, semantics, labels, focus, and contrast into the design of the UI |
| Performance | Reacts when the app feels slow | Measures cost and removes waste before it becomes user pain |
| Testing | Adds tests when requested | Protects critical flows with the right level of tests |
| APIs | Consumes endpoints | Negotiates contracts, loading states, failure states, and data shape changes |
| AI tools | Accepts useful output | Reviews generated code like a risky pull request |
| Leadership | Helps when asked | Creates clarity for other engineers without taking over everything |
Senior frontend developers understand that the browser is the runtime. React does not replace HTML, CSS, layout, rendering, networking, and accessibility.
You should be comfortable with:
Many frontend incidents are not framework problems. They are browser problems wearing framework clothing.
A senior frontend engineer designs components that other people can use safely.
Good component design includes:
A clever abstraction is not the aim. The aim is to reduce repeated decisions and prevent repeated bugs.
Senior frontend developers use TypeScript to model risk, not to decorate JavaScript.
You should know how to:
Use TypeScript interview questions for senior frontend developers as a calibration point.
Accessibility is not a bonus skill for senior frontend roles. It is part of building usable software.
Senior engineers catch issues such as:
AI-generated markup can look fine visually while failing semantic and keyboard behavior.
Senior frontend developers do not guess about performance for long. They measure.
Useful areas include:
Performance is product quality. Users do not care whether the slowness came from JavaScript, a large image, hydration, or an API. They only experience a slow product.
Senior engineers know that not every bug needs the same test. They choose the test based on risk.
Typical coverage choices:
The senior skill is knowing what must not break and putting protection there.
Frontend engineers do not need to become backend specialists, but senior frontend developers should understand the API boundary well.
You should be able to discuss:
REST knowledge matters because frontend decisions often depend on API contracts. Review REST API interview questions for frontend developers if you want to test that boundary.
In 2026, a senior frontend developer must know how to work with AI tools without lowering the team's quality bar.
AI usage is already common. Stack Overflow's 2025 Developer Survey reported broad AI-tool usage and also found that 66% of respondents were frustrated by AI solutions that were "almost right." DORA's 2025 AI-assisted software development report described AI as an amplifier of an organization's existing strengths and weaknesses.
For senior frontend work, that means AI helps more when the engineer already knows what good UI, safe state, accessible markup, and maintainable component design look like. If the engineer cannot review the output, AI can hide risk instead of removing it.
When reviewing generated frontend code, ask:
The senior skill is not refusing AI. It is making sure the team remains responsible for what ships.
Senior frontend developers create clarity. They do not need a staff title to do that.
Useful senior behaviors include:
Many mid-level developers get stuck here. They can finish their own work, but they do not yet improve the work around them.
Build evidence, not a skill list.
For your next few projects or work items, capture:
Use GreatFrontEnd's TypeScript interview questions, React interview questions, UI coding questions, and front end system design questions to pressure-test senior readiness. Good senior practice problems include Data Table, File Explorer, and Autocomplete.
Those stories are stronger in interviews than saying you know React, TypeScript, and testing.
Senior frontend interviews often test the gaps between coding skill and ownership.
Expect questions such as:
Useful answers are specific. Name the constraints, name the risk, make a tradeoff, and explain how you would verify the result.
Senior frontend developer skills in 2026 are about judgment under product constraints. You still need HTML, CSS, JavaScript, TypeScript, React, accessibility, performance, and testing. But the senior signal is how you use them to reduce risk and increase team output.
Senior work is less about writing more code and more about making the right frontend work happen.

To become a frontend developer in 2026, learn the web fundamentals first, then build apps with JavaScript, TypeScript, and React, then prove you can ship accessible, fast, tested UI. Do not start by collecting libraries.
A realistic path takes months, not a weekend. You are trying to become useful on a product team, not collect every tool.
You can become a frontend developer with different frameworks, but JavaScript, TypeScript, and React are still practical default choices for most learners.
GitHub's 2025 Octoverse report said TypeScript overtook both Python and JavaScript in August 2025 to become the most used language on GitHub, and noted that major frontend frameworks now scaffold TypeScript by default. JetBrains' 2025 developer ecosystem report also called TypeScript the language with the most dramatic rise in usage over the previous five years.
React remains a common hiring signal too. InfoQ's summary of the State of JavaScript 2025 survey reported React as the most used frontend framework among respondents, with Next.js also widely used. That does not mean React is the only good framework. It means React and TypeScript are efficient bets for interview preparation and portfolio work.
Start with HTML, CSS, JavaScript, and the browser. These are not beginner-only topics. Senior frontend engineers still debug problems at this layer.
Learn HTML as structure, not decoration:
Learn CSS as a layout system:
Then learn JavaScript deeply enough to build interactive pages:
Do not skip forms. A lot of frontend work is still forms: validation, disabled states, keyboard behavior, error messages, async submission, and recovery after failure.
Before React, build a few small projects with plain HTML, CSS, and JavaScript:
These projects show you what frameworks are solving. If you skip this stage, React can become a place to hide weak web knowledge.
Use browser DevTools early. Inspect layout, network requests, console errors, performance traces, storage, and accessibility hints.
React remains a common hiring signal, and TypeScript is now expected in many frontend teams with a real interview bar. Learn them together once your JavaScript is steady.
For React, learn:
For TypeScript, learn:
any as a habitYou do not need to memorize every React API. You do need to understand how UI changes over time and how data moves through your components.
A frontend developer builds applications, not isolated components. This stage connects your UI to product behavior.
Spend time with:
Learn REST APIs well enough to work with backend engineers. You should understand status codes, headers, JSON, caching, idempotency, pagination, rate limits, and authentication. The REST API interview questions for frontend developers are useful even before interviews because they expose common gaps.
Many candidates separate themselves here.
Accessibility means the interface works with keyboard navigation, screen readers, visible focus states, labels, semantic HTML, color contrast, and predictable behavior.
Performance means you can measure and reduce the cost of JavaScript, images, fonts, rendering, layout shifts, slow API calls, and unnecessary re-renders.
Testing means you can protect important behavior. Learn unit tests for logic, component tests for UI behavior, and end-to-end tests for critical flows.
Security basics matter too. Frontend engineers should understand cross-site scripting, unsafe HTML, token storage tradeoffs, permissions, CSRF at a high level, dependency risk, and privacy-sensitive data.
You do not need to be an expert in every area. You do need to know enough to avoid shipping careless UI.
A portfolio should prove that you can make product decisions instead of only copying designs.
Build three projects:
| Project | What it should prove |
|---|---|
| A dashboard | Data fetching, tables, filters, charts, loading states, empty states, and responsive layout |
| A form-heavy app | Validation, accessibility, error recovery, async submission, and state management |
| A product workflow | Routing, authentication states, permissions, API integration, and testing |
For each project, write a short case note:
Hiring teams do not need more cloned landing pages. They need evidence that you can think.
Use AI tools to explain unfamiliar code, generate first drafts, write test cases, and compare approaches. Then verify the output yourself.
The mistake is using AI to skip the learning step. If you ask AI to build every component before you understand HTML, CSS, JavaScript, and state, you may finish projects faster but become weaker in interviews and code reviews.
Use AI as a practice partner:
Do not use AI as a replacement for reading docs, using DevTools, or debugging your own mistakes.
For frontend work, check:
AI speeds up drafts. It can also speed up mistakes. Your value is knowing the difference.
AI changes the entry-level bar. Employers do not need a junior developer only to produce a first draft of a component. They need someone who can work with drafts, requirements, feedback, and bugs without lowering the team's quality.
A beginner should build evidence for:
| Skill | What it looks like in 2026 |
|---|---|
| Product behavior | You handle loading, empty, error, disabled, permission, and mobile states |
| Web foundations | You can explain the HTML, CSS, JavaScript, and browser behavior behind your UI |
| AI verification | You can review generated code instead of accepting it blindly |
| Debugging | You can use DevTools, logs, network panels, and TypeScript errors to find problems |
| Accessibility | You think about keyboard behavior, labels, focus, semantics, and contrast early |
| Communication | You can explain tradeoffs in plain language during reviews and interviews |
The roadmap starts with the platform and then adds React, TypeScript, projects, testing, and AI-assisted workflows. The order is intentional.
Frontend interviews usually test a mix of JavaScript, React, CSS, browser behavior, API knowledge, and product debugging.
Practice these areas:
Use GreatFrontEnd's JavaScript interview questions, React interview questions, quiz questions, and user interface coding questions to turn the roadmap into practice. For project-style drills, start with Contact Form, Data Table, and Image Carousel.
If you are targeting senior roles later, start building depth with TypeScript interview questions for senior frontend developers.
You are not job-ready because you watched a React course. You are closer to job-ready when you can take a messy requirement and turn it into working UI without someone else rescuing the details.
Before applying seriously, check whether you can:
If you cannot do these yet, keep building. If you can do most of them, start applying while improving the gaps. Waiting until you feel perfectly ready usually means waiting too long.
After you can build and ship product UI, choose one or two specialist tracks:
Do not begin here. Expert tracks are useful after you have product experience.
Do not read this as "six months guarantees a job." Read it as a readiness window.
If you already have some programming experience and can study consistently, 6 months can be enough to start applying for internships or junior frontend roles. If you are starting from zero, learning part-time, or switching careers while working, 9-12 months is more realistic.
| Phase | Timeframe | What you should be able to do |
|---|---|---|
| Web foundations | Months 1-2 | Build responsive pages, handle forms, write JavaScript interactions, use DevTools, and fetch data from APIs |
| Application basics | Months 3-4 | Build React apps with TypeScript, routing, forms, state, API integration, loading states, and error states |
| Job-ready practice | Months 5-6 | Finish portfolio projects, deploy them, practice GFE questions, explain tradeoffs, and start applying selectively |
| Interview depth | Months 7-12 | Improve weak projects, do more interviews, practice system design, add testing and accessibility depth, and study specialist topics only after the basics are stable |
The main milestone is not the calendar. It is whether you can build a working product flow, explain your decisions, debug mistakes, and pass common frontend interview exercises.
Do not try to become a frontend developer by memorizing a stack. Learn the platform, build interfaces that survive messy states, add React and TypeScript with purpose, and prove your judgment through projects.
The frontend market in 2026 rewards people who can ship usable product UI and explain their decisions. Build toward that.

Yes, frontend development is still a good career in 2026 if you can build product UI that holds up when data, state, accessibility, performance, edge cases, and AI-assisted code all meet in the same feature.
That distinction matters because many beginners are asking the wrong version of the question.
Frontend used to be seen as the easiest software path to enter. Learn HTML, CSS, JavaScript, React, build a portfolio, apply widely. That path still exists, but it is more crowded and less forgiving.
Three things changed:
Frontend is not dying, but shallow frontend work is easier to replace. A person who can only assemble prompted components has a weaker market position than someone who can verify behavior, debug product issues, and make tradeoffs.
For the broader "dying" question, read Is Frontend Development Dying in 2026?.
AI changes frontend development by making first drafts cheaper. It can generate a React component, write CSS, suggest tests, explain an error, convert JavaScript to TypeScript, or produce a quick API integration.
Useful, yes, but it also changes the hiring signal.
The old signal was often: can you build the screen? The 2026 signal is closer to: can you decide whether the generated screen is correct, accessible, maintainable, and aligned with the product?
Stack Overflow's 2025 Developer Survey found that 84% of respondents were using or planning to use AI tools, while more developers actively distrusted AI accuracy than trusted it. DORA's 2025 AI-assisted software development report frames AI as an amplifier of existing strengths and weaknesses, not as a replacement for sound engineering practice.
For frontend developers, that means AI can help you move faster if you already understand the web. If you do not, it can hide mistakes behind code that looks plausible.
AI is often weak at:
Those are exactly the areas where good frontend engineers still matter.
Frontend hiring is shifting from output volume to verification and ownership.
| Earlier expectation | 2026 expectation |
|---|---|
| Build screens from a design | Build product flows that handle real data, errors, loading, permissions, and accessibility |
| Know one framework | Understand the browser, JavaScript, TypeScript, CSS, APIs, and the framework |
| Use component libraries quickly | Know when a component library helps and when custom behavior needs careful implementation |
| Ship the happy path | Test edge cases, keyboard behavior, responsive states, and API failures |
| Ask AI for code | Review AI output for correctness, security, accessibility, and maintainability |
| Show a portfolio | Explain tradeoffs, debugging decisions, and how the project would behave in production |
AI makes frontend harder for people who rely on drafts and better for candidates who can review, debug, and explain their work. If a draft takes seconds, the valuable skill is deciding what should ship.
The software market still has demand, but it is uneven. In the US, the Bureau of Labor Statistics projects 15% growth for software developers from 2024 to 2034, much faster than the average for all occupations.
India's hiring picture is more selective. Naukri's March 2026 Jobspeak report showed white-collar hiring growth, but IT was flat while AI/ML hiring grew much faster.
The broader global picture points the same way. The World Economic Forum's Future of Jobs Report 2025 still lists software and applications developers among roles expected to grow through 2030.
Frontend jobs are not easy to get by default. Software work is not disappearing, but the hiring bar has shifted. A resume that lists React and a few projects is weaker than evidence that you can build and reason through product UI under constraints.
Every serious digital product needs user-facing software. Banking apps, SaaS dashboards, developer tools, marketplaces, internal tools, healthcare portals, education platforms, commerce sites, and AI products all need interfaces that people can understand and trust.
Frontend engineers sit at a difficult boundary. They translate product intent, design systems, backend APIs, user behavior, browser constraints, accessibility needs, and performance budgets into working software.
Most useful frontend work happens at that boundary.
The work that still earns trust includes:
Frontend work here is product engineering in the browser: user flows, constraints, failure states, and the details people notice when software breaks.
Frontend is a weaker career bet if you plan to stop at surface-level skills.
The risky profile looks like this:
This profile is more vulnerable in 2026 because AI tools can produce similar output quickly. Companies do not need to hire a full-time engineer for code that still requires heavy review by someone else.
Frontend candidates therefore need to show judgment. AI can draft a component. It will not reliably know whether the flow is accessible, the form state is recoverable, the API error is handled, or the product behavior is right.
Frontend work with staying power combines web foundations with product judgment.
You do not need to master everything before your first job, but you do need to build toward these areas:
| Area | Why it matters |
|---|---|
| HTML and accessibility | Users need interfaces that work beyond mouse clicks and perfect eyesight |
| CSS layout | Most UI bugs are layout, spacing, overflow, or responsive behavior problems |
| JavaScript and TypeScript | Frontend apps are stateful software, not static pages |
| React or another framework | Teams need maintainable UI architecture |
| APIs and data fetching | Product UI depends on network and backend behavior |
| Testing | Teams need confidence when features change |
| Performance | Slow UI loses trust, revenue, and user patience |
| Product thinking | Good frontend work solves user problems instead of only finishing design tickets |
| AI verification | Generated code still needs a human who knows what correct means |
If you want a staged learning path, use How to Become a Frontend Developer in 2026 as your roadmap.
If you want to test whether your skills are beyond surface-level UI, work through JavaScript interview questions, React interview questions, and user interface coding questions.
A useful first frontend role is not always the highest-paying one. It gives you repeated practice with product UI under review.
Look for:
Be cautious with roles where frontend means only slicing static pages, editing templates, or wiring prebuilt components with no product ownership. That experience can be useful at the beginning, but it can trap you if it never grows.
Frontend is not a single-track career. After the first few years, you can move toward different kinds of depth.
Common growth paths include:
That variety keeps the career interesting. You can start with visible product UI, then choose the kind of complexity you want to become known for.
Frontend is a good career fit if you enjoy the user-facing side of software. You should like details, but not only visual details. The work also needs patience with state, data, edge cases, and browser behavior.
You may enjoy frontend if you:
Frontend is also a good base for product engineering. Many strong product engineers start with frontend and add backend fluency over time.
Do not choose frontend only because it looks easier than backend. The beginner path may feel friendlier, but professional frontend work still demands careful engineering.
You may prefer backend, data, infrastructure, security, or mobile if you dislike:
Choosing the wrong path because it looks easy is expensive. Choose the problems you are willing to keep solving.
Frontend development is a good career in 2026 if you treat user-facing software as engineering work. The market is less generous to shallow skills, and AI has made basic code cheaper.
That should not push you away from frontend. It should push you to learn it properly. Build interfaces that work under pressure, explain your decisions, test the behavior, and understand the product. That version of frontend still has room.

Frontend wins if you want visible product ownership and enjoy browser/UI complexity. Backend wins if you want deeper ownership of systems, data, security, reliability, and business logic. Neither path wins for everyone.
The right question is not "Which career is better?" It is "Which kind of hard work do I want to get good at?"
Frontend developers build the part of a product users touch. That includes pages, flows, forms, dashboards, editors, navigation, state transitions, errors, loading states, accessibility, and performance in the browser.
At junior levels, this may look like building components from designs. At senior levels, it becomes product engineering: deciding how the UI should behave, how data should move through the app, how to prevent regressions, and how to make the experience fast and usable for many kinds of users.
Good frontend work often requires:
Frontend is a good fit if you like visible feedback. When the work is right, users can feel it immediately. When it is wrong, they can feel that too.
Backend developers build the systems behind the product. That includes APIs, databases, queues, permissions, authentication, business rules, integrations, search, payments, logging, reliability, and infrastructure-facing code.
At junior levels, backend work may start with routes, CRUD APIs, and database queries. At senior levels, it becomes systems ownership: data modeling, scaling paths, failure handling, security boundaries, observability, and tradeoffs that affect many teams.
Good backend work often requires:
Backend is a good fit if you like invisible correctness. Good backend work may be noticed only because the product keeps working.
Software remains a growing field, but the entry bar is higher than it was during the hiring boom. The US Bureau of Labor Statistics projects 15% growth for software developers from 2024 to 2034, much faster than the average for all occupations. For web developers and digital designers, it projects 7% growth from 2024 to 2034.
India's market is more uneven. Naukri's March 2026 Jobspeak report showed white-collar hiring growth, IT staying flat, and AI/ML roles growing faster than general IT. foundit's March 2026 tracker also showed a split market: IT software and services declined year over year, while functional IT hiring grew.
The market is not rewarding "I know a framework" as much as it rewards engineers who can handle product constraints. Frontend and backend both work if you build that kind of depth.
Frontend usually gives faster visual feedback. You can open a browser and see your work, which makes it a friendly first step for many beginners.
Backend has fewer visual cues. You work with requests, responses, logs, data models, and failure cases. It can feel abstract earlier, but it teaches durable engineering habits quickly.
Here is the honest version:
| Area | Frontend | Backend |
|---|---|---|
| Beginner feedback | Fast and visual | Slower and more abstract |
| Hidden difficulty | Browser behavior, accessibility, state, performance | Data consistency, security, reliability, scaling |
| First portfolio | Easier to show publicly | Harder to show without product context |
| Debugging style | UI states, network calls, browser tools | Logs, traces, queries, service behavior |
| Senior growth | Product UI, platform, performance, design systems | Systems, architecture, reliability, data, security |
Frontend and backend also differ in how mistakes show up.
Frontend bugs are often visible quickly. A layout breaks, a button does not respond, a form loses input, a modal traps focus, or a page feels slow. The upside is that the feedback loop is direct. The downside is that frontend work can look "almost done" while still failing across devices, assistive technology, slow networks, or unusual data.
Backend bugs can be less visible at first and more expensive later. A bad permission check, data migration, cache invalidation bug, or retry loop may not be obvious to a user immediately, but it can affect data integrity, security, cost, or reliability.
The two paths attract different temperaments. Frontend rewards people who can handle visible product pressure and many user states. Backend rewards people who can think carefully about invisible system behavior and long-term correctness.
| Level | Frontend signal | Backend signal |
|---|---|---|
| Junior | Can build UI from existing patterns and debug with DevTools | Can build small APIs, write simple queries, and understand request flow |
| Mid-level | Can own product features with state, API calls, tests, and accessibility | Can own service changes with validation, data modeling, tests, and logs |
| Senior | Can shape UI architecture, prevent regressions, and guide frontend quality | Can shape service boundaries, reliability, security, and data correctness |
Choosing frontend or backend is not only a technology choice. It is also a trust curve. Each level asks: what kind of risk can the team trust you to handle?
AI tools can generate frontend components and backend routes quickly. That creates pressure on basic implementation work in both careers.
Frontend developers are exposed when they can only produce a visually correct first draft. They become valuable when they can verify behavior across states, devices, accessibility requirements, performance budgets, and product expectations.
Backend developers are exposed when they only copy CRUD patterns. They become valuable when they can reason about data correctness, security, concurrency, migrations, observability, and failure recovery.
AI does not remove the need for either path. It pushes both paths toward better judgment.
Frontend is likely a better fit if you:
It is also a good path if you want to become a frontend-heavy product engineer. Learn APIs well enough that you can work across the product boundary, even if your main depth is frontend.
Backend is likely a better fit if you:
Backend is also a good path if you are comfortable with slower feedback loops and more invisible work.
If you truly have no preference, start with frontend for the first few months. Build pages, forms, stateful interfaces, and API-connected projects. You will get feedback quickly, and you will learn the web platform.
Then learn backend basics: REST APIs, auth, databases, validation, error handling, and deployment. Even if you stay frontend, this knowledge will make you much better at product work. The REST API interview questions for frontend developers are a useful checkpoint.
On GreatFrontEnd, a frontend-leaning path can start with JavaScript questions, React questions, and UI coding questions. A backend-leaning product engineer should still practice system design questions and API-heavy scenarios such as Autocomplete.
After that, follow the problems you enjoy solving. If you keep caring about interaction quality, frontend is a good bet. If you keep caring about data, rules, and system behavior, backend is a good bet.
Beginners often compare the easiest frontend work with the hardest backend work, or the easiest backend work with the hardest frontend work. That creates a distorted view.
Basic frontend can look like styling pages. Professional frontend means handling browser behavior, accessibility, client state, product flows, performance, analytics, design systems, and every user state that appears after launch.
Basic backend can look like CRUD endpoints. Professional backend means data correctness, auth, migrations, observability, scaling, security, integrations, and failure handling.
Do not choose based on the easiest demo. Choose based on the difficult version you are willing to practice.
Frontend and backend are both good careers in 2026, but neither is easy by default. Frontend requires more than visual work. Backend requires more than API work. Both require careful reasoning, testing, and ownership.
Choose frontend for product-facing complexity. Choose backend for systems-facing complexity. Choose the path whose problems you are willing to keep debugging after the easy part is over.

Choose fullstack if you want broad feature ownership across UI, APIs, data, and deployment. Choose frontend if you want depth in product UI, accessibility, browser performance, design systems, and the craft of making software feel clear and reliable.
That is the short answer. The harder part is knowing which path gives you the better signal in the market you are entering.
A frontend developer is not limited to turning mockups into pages. Valuable frontend engineers can reason about UI state, network boundaries, accessibility, rendering cost, design systems, testing, observability, and the product behavior users actually experience.
A fullstack developer is not simply a frontend developer who also knows Node.js. A good fullstack developer can move a feature through the whole product path: UI, API contract, database change, auth, validation, deployment, and basic monitoring.
There is also a middle path: the frontend-heavy fullstack developer. This shows up often in startups and product teams. You do most of your work in the browser and UI layer, but you can edit backend routes, database models, and integration code when the feature needs it.
| Question | Frontend developer | Fullstack developer |
|---|---|---|
| Best fit | People who enjoy visible product work, UI detail, and browser behavior | People who enjoy owning a feature across layers |
| Main risk | Staying at the "build screens" level | Being shallow across too many areas |
| Hiring signal | Strong UI judgment, React/TypeScript, accessibility, performance, testing | Ability to ship complete features with frontend, backend, and data changes |
| Common interview areas | JavaScript, React, CSS, UI architecture, accessibility, product debugging | Frontend plus APIs, databases, auth, system design, deployment basics |
| Best company fit | Design-heavy products, SaaS, marketplaces, consumer apps, design systems teams | Startups, small teams, internal tools, product engineering teams |
| AI-era advantage | Verifying generated UI, edge states, accessibility, and behavior | Turning ambiguous product needs into working end-to-end features |
Frontend is the better path if you care about how users experience the product. That includes layout, interaction, error states, loading states, keyboard behavior, screen reader behavior, latency, and visual consistency.
Good frontend roles sit close to product and design. You may own a checkout flow, onboarding funnel, dashboard, editor, design system, or mobile web experience. The work is visible, which can be satisfying and stressful at the same time. A small bug can be obvious to everyone.
Frontend also rewards people who enjoy details. A button is rarely just a button in a serious product. It may need disabled states, loading behavior, focus styling, analytics, permission handling, optimistic updates, error recovery, responsive layout, and accessibility semantics.
This path is especially good if you want to become a:
If you are worried that frontend is getting automated, read Is Frontend Development Dying in 2026?. The short version is that basic UI output is easier to generate, but frontend judgment is still hard to replace.
Fullstack is the better path if you want to take a feature from idea to shipped behavior with fewer handoffs. You might build the form, write the API, model the database table, add validation, handle permissions, and fix the deployment issue when the first version breaks.
That range is useful in smaller companies because there may not be a clean separation between frontend, backend, infrastructure, analytics, and support tooling. A fullstack developer who can move across those boundaries can unblock a team quickly.
The tradeoff is depth. Many junior developers call themselves fullstack after learning React and one backend framework, but companies do not usually pay for labels. They pay for useful ownership. If your frontend is fragile and your backend is copied from tutorials, the title will not help.
Fullstack is especially useful if you want to:
Frontend often feels easier at the beginning because you can see the result quickly. HTML, CSS, JavaScript, and React give fast feedback. That makes it a friendly entry point for many learners.
But frontend gets hard once you leave toy projects. You need to handle browser differences, accessibility, data fetching, complex state, bundle size, design constraints, flaky networks, and many device sizes.
Fullstack can feel harder at the start because you need more moving parts. You will meet databases, HTTP, authentication, environment variables, server errors, deployment, and security concerns earlier. The learning curve is wider, but the reward is context.
If you are starting from zero, begin with frontend foundations first. Then add backend basics once you can build useful interfaces. A frontend developer who understands APIs is already ahead of many candidates.
For a quick readiness check, practice JavaScript interview questions, React interview questions, and user interface coding questions. If you are testing the fullstack direction, add system design questions after you are comfortable with APIs.
Your portfolio should match the path you want. A frontend portfolio and a fullstack portfolio can overlap, but they should not tell the same story.
For a frontend path, build evidence around:
For a fullstack path, build evidence around:
If a project claims to be fullstack but the backend is only a tutorial API, it will not help much. If a project claims to be frontend but ignores accessibility, responsive behavior, and error states, it will also look shallow.
The title alone does not decide pay. Company type, location, product complexity, interview bar, and ownership matter more.
In many startups, a fullstack developer can command strong compensation because one person can own a complete feature. In larger product companies, a frontend specialist can also earn well if they bring rare depth in performance, accessibility, design systems, or complex UI architecture.
If you are evaluating offers, do not compare only the role label. Compare:
For a deeper company-quality checklist, read How to Evaluate Companies as a Front End Engineer.
AI coding tools make simple code cheaper. They can generate components, API handlers, tests, and boilerplate quickly. That does not remove the need for engineers. It changes what good engineers are judged on.
Frontend developers need to verify whether generated UI handles accessibility, responsive behavior, loading states, browser quirks, and product edge cases.
Fullstack developers need to verify whether generated code respects data rules, auth boundaries, failure modes, and deployment behavior.
In both paths, the value moves from typing code to making correct decisions. The path you choose should match the kind of decisions you want to make every week.
| Your goal | Better first bet |
|---|---|
| Get into software through visible projects | Frontend |
| Build and launch your own app | Fullstack |
| Work closely with designers | Frontend |
| Join early-stage startups | Frontend-heavy fullstack |
| Specialize in performance or accessibility | Frontend |
| Own product features end to end | Fullstack |
| Prepare for broad startup interviews | Fullstack |
| Prepare for UI-heavy product roles | Frontend |
If you are early in your career, start with frontend well enough to build polished, accessible, tested interfaces. Then learn enough backend to understand APIs, auth, data models, and deployment.
After that, choose your depth. Go frontend if the browser and product experience keep pulling your attention. Go fullstack if you keep wanting to own the whole feature path.
Both careers are viable in 2026. The risk is staying generic. The advantage comes from becoming useful enough that a team trusts you with unclear, important work.

Frontend testing interview questions for freshers test whether you can protect user behavior, not whether you can recite tool names. Interviewers ask about unit, integration, and E2E tests through bugs: "Why did checkout break?", "Why is this test flaky?", "What would you test before merging this form?", or "Why did this snapshot fail after a harmless refactor?"
You do not need to be a testing specialist for a fresher frontend role. You should be able to design a small test plan, write one behavior-focused component test, handle async UI, avoid brittle selectors, and explain why critical flows deserve stronger tests than decorative UI.
For extra practice, try GreatFrontEnd's Test Runner coding question, the unit vs integration vs E2E quiz, and the machine coding round guide.
| Interview prompt | What the interviewer is checking | Common fresher mistake |
|---|---|---|
| "Test this login form." | User-centric selectors, validation, async submit, error state | Testing internal React state instead of visible behavior |
| "This test passes locally but fails in CI." | Flake diagnosis | Adding a longer timeout without finding the race |
| "Should this be unit, integration, or E2E?" | Cost vs confidence | Saying every critical flow needs only E2E |
| "Why avoid large snapshots?" | Signal-to-noise judgment | Treating snapshot approval as real verification |
| "How would you mock the API?" | Test boundary design | Mocking the component's internal helper instead of the network boundary |
"A login form has email, password, validation, a loading button, and an API error. What do you test?"
Test the user behavior:
That proves more than checking whether a component's internal isLoading state changed.
test('shows an error when login fails', async () => {server.use(http.post('/api/login', () =>HttpResponse.json({ message: 'Invalid credentials' }, { status: 401 }),),);const user = userEvent.setup();render(<LoginForm />);await user.type(screen.getByLabelText(/email/i), 'ada@example.com');await user.type(screen.getByLabelText(/password/i), 'wrong-password');await user.click(screen.getByRole('button', { name: /sign in/i }));expect(await screen.findByText(/invalid credentials/i)).toBeInTheDocument();expect(screen.getByRole('button', { name: /sign in/i })).toBeEnabled();});
The exact mock library can vary. The test should read like a user flow and control the API at a stable boundary.
"The test types into search and expects results immediately. It fails sometimes."
The likely bug is a timing assumption. Use findBy... or waitFor() for async results, and assert on the final user-visible result instead of sleeping for a fixed time.
"Should every cart edge case be tested in Playwright?"
No. Put pure price calculation and reducer edge cases in fast unit tests. Put cart UI state in component or integration tests. Keep E2E for the critical checkout path: add item, update quantity, enter address, reach payment or order review.
"Why does
getByRole('button', { name: /submit/i })fail?"
The button may not have an accessible name, may not be a real button, may be hidden from the accessibility tree, or may be rendered later. That test failure can reveal a usability problem.
Frontend testing checks whether UI code behaves correctly for users. It covers pure functions, components, browser interactions, network states, accessibility basics, and full user flows.
Good frontend tests catch regressions before users do. They should also be readable enough that future engineers can tell what behavior is protected.
Unit tests check a small isolated piece, such as a formatter or reducer. Integration tests check multiple pieces working together, such as a form component that validates input and calls a submit handler. E2E tests run in a browser and test a user flow across the app.
The tradeoff is speed versus confidence. Unit tests are fast and focused. E2E tests are slower but catch routing, browser, and backend integration issues.
Add this detail: Give an example for the same feature. Price formatter: unit. Cart component plus store: integration. Add-to-cart through checkout: E2E.
Start with critical user behavior: form validation, submit success and failure, rendering loaded data, empty states, error states, and navigation for core flows.
Do not start by snapshotting every component. A few behavior-focused tests protect the product with less churn than many tests that break on harmless markup changes.
Priority rule: Test the behavior that would embarrass the product if it broke: login, checkout, save, delete, upload, search, filtering, permissions, and error recovery.
The testing pyramid suggests many fast unit tests, fewer integration tests, and a smaller number of E2E tests.
Frontend teams adjust this into a more balanced shape because component integration tests can give high confidence at lower cost than full E2E tests. Match test cost to product risk.
React Testing Library is a testing utility for rendering React components and querying the DOM in ways that resemble user interaction.
Its style is to test behavior through accessible output: buttons, labels, text, roles, and form controls. That makes tests less tied to internal component structure.
getByRole()?getByRole() queries elements by their accessible role and name, which is close to how assistive technologies understand the page.
For example, screen.getByRole("button", { name: /submit/i }) checks that there is an actual button users can find by name. If this query is hard to write, the UI may have an accessibility problem.
getBy, queryBy, and findBy?getBy returns an element or throws immediately. Use it when the element should already be present.
queryBy returns null when no element is found. Use it for absence assertions.
findBy returns a promise and retries for async appearance. Use it when UI changes after data loading, timers, or user interaction.
Common failure: Using getByText() immediately after a click that triggers a request. The test races the UI. Use await screen.findByText(...) when the user-visible result appears asynchronously.
user-event, and how is it different from fireEvent?user-event simulates higher-level user interactions such as typing and clicking. It can trigger multiple DOM events and checks that the interaction is possible.
fireEvent dispatches a single event. Use it for lower-level cases, but prefer user-event for most component interaction tests.
Use async queries such as findByRole() or wait helpers such as waitFor() when the DOM updates after a promise, request, or delayed state change.
await user.click(screen.getByRole('button', { name: /load users/i }));expect(await screen.findByText('Ada Lovelace')).toBeInTheDocument();
Avoid fixed sleeps. Waiting for a real condition makes the test faster and less flaky.
Mocks replace a real dependency with a controlled fake. Use them for network calls, timers, browser APIs, analytics, and modules that are expensive or unreliable in tests.
The risk is over-mocking. If every dependency is mocked, the test may only prove that mocks match your assumptions.
A stub provides a controlled response. A mock can also record interactions so you can assert it was called with certain arguments.
For frontend tests, name the boundary you are replacing: network, module, callback prop, or browser API.
For component tests, yes, but prefer mocking at the network boundary instead of mocking internal functions. Tools such as Mock Service Worker let the component still execute its real data-fetching path while the network response is controlled.
For E2E tests, teams may use a test backend, seeded data, or network mocks depending on reliability and cost.
Why this matters: If you mock fetchUsers() directly, a later refactor from fetchUsers() to userClient.list() can break the test even though the user flow is unchanged. Mocking GET /api/users keeps the test tied to the API contract instead of component internals.
Snapshot testing saves a rendered output and compares future output against it. It can catch unexpected structural changes.
For frontend components, large snapshots are noisy. Use snapshots sparingly for stable, small outputs. Behavior tests fit buttons, forms, and user flows.
Snapshot answer: Snapshots work when the output is intentionally stable and reviewable. They fail when the reviewer cannot tell whether the diff is meaningful.
Coverage measures how much code was executed during tests. Common metrics include line, branch, function, and statement coverage.
Coverage is a signal, not a goal by itself. A project can have high coverage and still miss critical behavior if tests assert the wrong thing.
Flaky tests pass sometimes and fail other times without code changes. Common causes include timing assumptions, shared state, uncontrolled network calls, test order dependence, random data, and brittle selectors.
Fix flakes quickly. A flaky test suite trains the team to ignore failures.
Debug checklist: Look for missing await, uncontrolled timers, shared test data, test order dependence, network calls hitting real services, animations, and selectors that match multiple elements.
Test it like a user: find fields by label, type values, submit the form, and assert validation messages, disabled states, loading states, or the success result.
Avoid checking internal state variables. The user cannot see those; the test should verify behavior.
Make the dependency fail, then assert what the user sees. For example, mock the API to return 500, submit the form, and assert that an error message appears and the retry button remains usable.
Error states are worth testing because they are easy to forget during happy-path development.
Use a delayed promise or controlled network mock, trigger the fetch, assert the loading UI appears, then resolve the request and assert the final UI.
The key is to test the transition, not only the final state.
E2E testing runs the app in a real browser and checks a complete user journey, such as signup, login, checkout, or creating a dashboard item.
Playwright and Cypress are common tools. E2E tests catch routing, browser behavior, network, storage, and integration problems that unit tests cannot.
Cover the flows where a regression would hurt users or revenue: login, checkout, onboarding, billing, content creation, destructive actions, and core search or filtering.
Do not put every small component state into E2E. That makes the suite slow and brittle.
Good E2E candidates: signup, login, checkout, project creation, billing update, file upload, permission-protected route, and a destructive action with confirmation.
Visual regression testing compares screenshots across builds to detect unintended visual changes.
It fits design systems, marketing pages, dashboards, and components where layout is part of the contract. It should not replace semantic behavior tests.
Accessibility testing checks whether users with different input methods and assistive technologies can use the UI. Automated tools can catch missing labels, contrast issues, invalid ARIA, and some semantic problems.
Automated accessibility tests catch only part of the problem. Keyboard testing and screen reader checks still matter for core flows.
Prefer accessible selectors: role, label text, placeholder when it is the visible cue, visible text, and alt text. Use data-testid only when there is no stable user-facing selector.
Avoid CSS class selectors for behavior tests because styling classes are not the behavior contract.
Selector priority in practice: Role with accessible name, label text for form fields, visible text for content, alt text for images, and data-testid for UI with no user-facing label such as virtualized rows or canvas-backed controls.
Use renderHook() when the hook has logic worth testing independently, such as debouncing, timers, local storage, or data transformations.
If the hook mostly wires UI behavior, testing the component that uses it may give more confidence.
Both are JavaScript test runners. Jest is mature and common in many existing React projects. Vitest is popular in Vite-based projects because it is fast, ESM-friendly, and integrates well with Vite config.
In interviews, the exact tool matters less than knowing assertions, mocks, async tests, setup files, and test environments.
jsdom is a JavaScript implementation of many browser DOM APIs used in Node-based tests. It lets component tests render and interact with DOM-like elements without launching a real browser.
It is fast, but it is not a full browser. For layout, browser engines, clipboard behavior, downloads, and cross-browser issues, use real browser tests.
Fake timers let tests control time-based functions such as setTimeout, setInterval, and debounce logic. They make timer tests fast and deterministic.
Be careful with user interactions and promises. If the tool needs timer advancement, configure it explicitly rather than adding arbitrary sleeps.
CI testing runs tests automatically on pull requests or commits. It catches failures before code merges.
A typical setup runs linting, type checks, unit tests, and component tests on every PR. E2E and visual tests may run on release branches or deployment previews depending on cost.
Test-driven development means writing a failing test first, then writing the code to pass it, then refactoring.
Freshers do not need to claim they always use TDD. Use TDD for pure functions, reducers, bug fixes, and well-defined behavior; exploratory UI work may start with implementation and add tests once behavior stabilizes.
Do not test library internals, implementation details, trivial getters, generated code, or behavior already guaranteed by a trusted framework.
Test your decisions: validation rules, conditional rendering, state transitions, API handling, permissions, edge cases, and critical user journeys.
Good phrasing in interviews: "I would not test React's ability to call onClick; I would test what our app does after the user clicks."
Avoid tests that inspect component state, private functions, or class names unless that is the public contract. Test what the user can see or do.
Snapshots are easy to create and easy to ignore. Use them only when the output is small and stable.
await new Promise((r) => setTimeout(r, 1000)) makes tests slow and flaky. Wait for the UI condition you expect.
Mocks help at stable boundaries such as network, time, storage, and analytics. Too many mocks disconnect tests from the behavior users depend on.
Write tests for a small signup form:
409 email conflict shows "Email already exists".Use label/role queries, user-event, and an API mock. After writing the tests, explain which one is unit, which one is integration, and what single E2E test you would add for signup.
Frontend testing fresher interviews are less about naming every tool and more about explaining why a test gives confidence. Start with user behavior, then choose the smallest test that protects it.

REST API interview questions for frontend devs test whether you can turn an API contract into a reliable UI. The interview checks which method you call, how you handle status codes, what the loading and error states look like, how auth is sent, why CORS fails in the browser, and how you avoid duplicate or stale requests.
Freshers do not need to design a large backend system. You should know how a browser sends requests, what fetch() actually resolves or rejects on, why GET should not mutate data, and how frontend UI should respond to common API states.
If you want more practice around async JavaScript, pair this guide with GreatFrontEnd's JavaScript interview questions.
Backend interviews may go deep into resource modeling and database consistency. Frontend interviews care about the client contract:
| Interview prompt | What the interviewer is checking | Common fresher mistake |
|---|---|---|
| "Implement save profile." | PATCH, validation errors, disabled submit, stale UI update | Treating all failures as "Something went wrong" |
"Why didn't fetch() go to catch on 404?" | Fetch API behavior | Assuming HTTP errors reject the promise |
| "The request works in Postman but fails in the browser." | CORS and credentials | Debugging React code before checking CORS headers |
| "Should search use GET or POST?" | Method semantics, URL shareability, body size/privacy | Saying "POST is more secure" without explaining URLs |
| "User double-clicks Pay. What protects us?" | Duplicate submission and idempotency | Only disabling the button and ignoring backend guarantees |
"The user edits display name and timezone. Which API call and UI states do you handle?"
Use a partial update if the API supports it:
PATCH /api/meContent-Type: application/json{ "displayName": "Ada", "timezone": "Asia/Kolkata" }
Handle: idle, dirty, submitting, success, field validation error, auth error, conflict, and server error. Include UI behavior: disable duplicate submit, preserve user input on failure, map field errors beside fields, and refetch or update cached user data after success.
fetch() wrapper that does not lieasync function requestJSON<T>(url: string,init?: RequestInit,): Promise<T | null> {const response = await fetch(url, init);const contentType = response.headers.get('content-type') ?? '';const hasBody = response.status !== 204;const body = hasBody? contentType.includes('application/json')? await response.json(): await response.text(): null;if (!response.ok) {throw new APIError(response.status, body);}return body as T | null;}
The point is not that every app needs this exact helper. The point is that HTTP error statuses need explicit handling, and the response body may be JSON, plain text, or empty.
"The API returns data in Postman, but the frontend sees a CORS error."
Postman is not a browser and does not enforce browser CORS rules. The server must allow the frontend origin with the right CORS headers. If cookies or auth credentials are involved, the response must also satisfy credential rules; Access-Control-Allow-Origin: * cannot satisfy credentialed browser requests.
"Build product search with filters and sorting. What should the URL look like?"
For shareable, bookmarkable search, use query params:
GET /api/products?query=shoes&category=men&sort=price_asc&cursor=abc
If the filter object becomes too large or contains sensitive data, discuss a POST /search style endpoint, but name the tradeoff: URL shareability and HTTP caching become less straightforward.
"A user double-clicks Pay. Is disabling the button enough?"
No. Disabling the button improves the UI, but the backend should still protect the operation. Use an idempotency key or server-side duplicate protection for operations such as payments and order creation.
Frontend-only prevention fails when the user refreshes, retries, has two tabs open, or the network repeats a request.
A REST API exposes resources through URLs and uses HTTP semantics to operate on those resources. A frontend might call GET /products to list products, GET /products/123 to read one product, or POST /orders to create an order.
REST is an architectural style, not a single JavaScript library. In interviews, keep the answer tied to HTTP methods, resources, stateless requests, status codes, and representations such as JSON.
Interview-ready add-on: Explain one concrete resource. "Products are resources, so reading one product is GET /products/:id; creating an order is POST /orders; updating my profile can be PATCH /me."
HTTP is the protocol: methods, headers, status codes, request bodies, response bodies, caching, and authentication headers. REST is a style for designing APIs that commonly uses HTTP well.
A REST-like API should make resources and actions understandable through HTTP instead of hiding everything behind one generic endpoint.
GET reads data. POST creates or triggers processing. PUT replaces a resource. PATCH partially updates a resource. DELETE removes a resource. OPTIONS is used by browsers for CORS preflight checks.
Official reference: HTTP request methods on MDN.
GET and POST?GET retrieves a resource and should be safe, meaning it should not intentionally change server state. Query parameters are part of the URL and can be cached, bookmarked, logged, and shared.
POST sends data for the server to process, such as creating a resource or performing an action. Use POST for form submissions, login attempts, and operations with bodies that should not be placed in the URL.
Frontend trap: Do not say "POST is secure and GET is not." Security depends on HTTPS, auth, server handling, logs, and what data is exposed. The key distinction is method semantics and URL visibility.
PUT and PATCH?PUT replaces the full resource at the target URL when the API follows standard REST semantics. If you send a user object with PUT /users/1, the server treats that representation as the new full state.
PATCH applies a partial update. For example, PATCH /users/1 with { "timezone": "Asia/Kolkata" } updates only that field if the API contract says so.
An operation is idempotent if repeating the same request has the same intended effect as sending it once.
GET, PUT, and DELETE are idempotent by HTTP semantics. POST is not idempotent by default because sending it twice may create two orders, two payments, or two records.
A safe method is intended only to retrieve information and not change server state. GET and HEAD are safe.
Safe does not mean "no logs or analytics happen." It means the client did not request a state-changing operation.
Status codes are grouped by first digit:
1xx: informational2xx: success3xx: redirection4xx: client error5xx: server errorOfficial reference: HTTP response status codes on MDN.
200, 201, and 204?200 OK means the request succeeded and returns a response body in most JSON API flows.
201 Created means a resource was created. APIs may include a Location header or response body with the created resource.
204 No Content means the request succeeded but there is no response body, commonly after delete or update operations.
400, 401, and 403?400 Bad Request means the request is invalid, such as malformed JSON or missing required data.
401 Unauthorized means authentication is missing or invalid. Despite the name, it is about authentication.
403 Forbidden means the server understood who you are, but you do not have permission for that resource or action.
UI mapping: 400 maps to form or request correction in many APIs. 401 may trigger login. 403 should show a permission message or hide the action if the user truly cannot perform it.
404?Return 404 Not Found when the requested resource does not exist or should not be revealed to the current user.
For frontend UI, 404 maps to an empty detail state, a not-found page, or a redirect to a safer listing page.
409 Conflict?409 Conflict means the request conflicts with the current state of the resource. Examples include duplicate usernames, editing an outdated version of a document, or trying to reserve an already-booked slot.
The frontend should show a specific message and ask the user to refresh, change input, or retry with updated data.
429 Too Many Requests?429 means the client has hit a rate limit. APIs may include a Retry-After header telling the client when to try again.
The frontend should slow down, disable repeated actions, show a helpful message, or schedule a retry only when appropriate.
fetch() handle HTTP errors?fetch() rejects for network-level failures, not for HTTP error status codes like 404 or 500. If the server responds, the promise resolves with a Response.
Check response.ok or response.status before reading the body as success.
const response = await fetch('/api/products');if (!response.ok) {throw new Error(`Request failed: ${response.status}`);}const data = await response.json();
Official reference: Using the Fetch API.
What to say in the room: "I handle two failure classes: network/request failures through catch, and HTTP failures through response.ok or response.status."
response.ok mean?response.ok is true when the HTTP status is in the 200 to 299 range. It is false for redirects that are not followed, client errors, and server errors.
Official reference: Response.ok.
Headers are metadata sent with requests and responses. Common request headers include Content-Type, Accept, and Authorization. Common response headers include Cache-Control, ETag, Set-Cookie, and CORS headers.
Frontend developers should know which headers are safe to set from browser JavaScript and which are controlled by the browser.
Content-Type?Content-Type tells the receiver the media type of the body. JSON requests use Content-Type: application/json.
Without the right Content-Type, the server may not parse the body correctly.
Accept header?Accept tells the server which response formats the client can handle. For a JSON API, the client may send Accept: application/json.
Many APIs return JSON by default, but naming the expected format makes the contract clearer.
CORS is a browser security mechanism that lets a server decide which origins can read its responses from frontend JavaScript.
If a React app on https://app.example.com calls https://api.example.com, the API must send the right CORS headers. Otherwise the browser blocks JavaScript from reading the response.
Official reference: CORS on MDN.
Debug signal: If the browser console says CORS, changing React state management will not fix it. Check request origin, method, custom headers, credentials mode, and server response headers.
A preflight is an OPTIONS request the browser sends before some cross-origin requests. It asks the server whether the actual method and headers are allowed.
You see preflights for requests with methods like PUT or DELETE, custom headers, or non-simple content types.
Common approaches include cookies with server sessions and tokens sent through the Authorization header. In browser apps, secure cookie-based auth is common because cookies can be HttpOnly, which prevents JavaScript from reading them.
If using bearer tokens, avoid storing long-lived secrets in unsafe places. The frontend can send a token, but it cannot truly hide one from the user if JavaScript can read it.
Official reference: Authorization header on MDN.
Cookies are sent automatically on same-origin browser requests. For cross-origin fetch() calls, the request also needs the right credentials option, cookie attributes, and CORS response headers.
Bearer tokens are sent manually in an Authorization: Bearer ... header. They are flexible for APIs, but frontend storage choices matter because XSS can steal tokens available to JavaScript.
Pagination splits large lists into smaller responses. Page-based pagination uses page and limit. Cursor-based pagination uses a pointer such as nextCursor.
Cursor pagination fits feeds and frequently changing data because it avoids missing or duplicating items when records are inserted between page requests.
Frontend behavior to mention: Keep previous items while loading more, prevent duplicate "load more" clicks, handle the end of the list, and make refresh behavior explicit.
Filtering narrows the result set, and sorting controls order. A frontend might call:
GET /products?category=shoes&sort=price_asc
Keep query parameter names stable and predictable. The UI should encode user choices into the URL when those choices are shareable.
API versioning lets the backend change contracts without breaking existing clients. Common strategies include /v1/products, custom headers, or compatibility windows.
Frontend developers should know which version they call and what migration plan exists before relying on new fields.
HTTP caching stores responses so future requests can reuse them. Important headers include Cache-Control, ETag, and conditional request headers such as If-None-Match.
Caching can happen in the browser, CDN, framework, or application data layer. Stale UI bugs come from checking the wrong cache.
Official reference: HTTP caching on MDN.
ETag and 304 Not Modified?An ETag is a validator for a specific representation of a resource. The browser or client can send it back with If-None-Match.
If the resource has not changed, the server can return 304 Not Modified, allowing the client to reuse its cached copy.
Handle errors by category. Validation errors should show field messages. Auth errors should route to login or show permission messaging. Rate limits should slow retries. Server errors should show a retry option or fallback state.
Do not show raw backend stack traces. Do log enough context for debugging, such as endpoint, status code, and request ID if available.
Useful error map:
| Status | UI response |
|---|---|
400 / 422 | Show field-level validation messages when available |
401 | Ask the user to sign in again |
403 | Explain missing permission or hide the restricted action |
404 | Show not-found or empty detail state |
409 | Ask user to refresh, choose another value, or resolve conflict |
429 | Slow down retries and show rate-limit messaging |
500 | Show retry/fallback and log request context |
Disable the submit button while the request is in flight, show progress, debounce repeated clicks when appropriate, and make the backend operation idempotent when duplicate requests are dangerous.
For payments and order creation, the API should still support idempotency keys or another duplicate-protection strategy.
A frontend-friendly API contract has stable field names, clear status codes, predictable error shapes, pagination metadata, documented nullability, and enough data to render the UI without extra avoidable requests.
For mutations, it should define what the response returns, whether the client should refetch, and how validation errors map to fields.
Ask this in interviews: "Can I assume this endpoint returns the updated resource after mutation, or should the client refetch?" That one question prevents many stale UI bugs.
fetch()fetch() resolves for many HTTP error responses. Always check response.ok or response.status.
URLs can be logged, cached, shared, and stored in browser history. Use request bodies and secure auth mechanisms for sensitive data.
GET for mutationsGET should be safe. Do not create orders, delete items, or trigger payment actions through GET.
Validation, auth, permission, not-found, conflict, rate-limit, and server errors need different UI responses.
Design the frontend contract for a profile settings page:
GET /api/me loads the current profile.PATCH /api/me updates displayName, timezone, and avatarUrl.400 or 422 returns field errors.401 means the session expired.409 means the display name is taken.Then write a small fetch() helper that checks response.ok, parses JSON only when the response is JSON, handles empty responses such as 204, and throws an error object with status and body. That exercise covers more interview value than memorizing every status code.
REST API interviews for frontend freshers are about contracts and UI behavior. Explain the HTTP concept, then say how the browser UI should respond.

In fresher Next.js interviews, the prompt is a small React product problem: choose the right route files, keep secrets on the server, add the minimum client JavaScript, handle missing data, and explain why one page should be static while another must be request-time.
Definitions alone do not prepare you for those prompts. Tie App Router, Server Components, caching, and Route Handlers to product tasks: "Build a product detail page", "Why did this hydration error happen?", "Where should this API key live?", or "Why is this dashboard showing stale data?"
If your React basics are shaky, pair this guide with GreatFrontEnd's React interview questions and the React Interview Playbook.
| Interview prompt | What the interviewer is checking | Common fresher mistake |
|---|---|---|
"Build /products/[id] and show a 404 for missing products." | Dynamic routes, params, server fetching, notFound(), metadata | Rendering a generic empty div instead of a route-level not-found state |
| "Add a cart button to a server-rendered product page." | Server/Client Component boundary | Marking the whole page as "use client" instead of isolating the button |
| "This page uses an API key. Where should the request happen?" | Secret handling and server-only code | Calling the private API directly from a Client Component |
| "Why does this date cause a hydration error?" | Server HTML must match initial client render | Rendering new Date() or Math.random() directly in shared markup |
| "Product prices changed, but the page still shows old data." | Caching, revalidation, dynamic rendering | Saying "clear browser cache" without checking Next.js data/server caches |
Use this structure for most answers:
page.tsx, layout.tsx, route.ts, generateStaticParams(), notFound(), redirect(), "use client", or fetch() options.For 2026 interviews, use App Router vocabulary by default. If the interviewer asks about pages/, getStaticProps, or getServerSideProps, answer it as Pages Router knowledge and then explain the App Router equivalent.
"A product detail page is server-rendered. Marketing wants it fast for SEO, but price and stock change every few minutes. How would you build it?"
Separate stable and volatile data. Product title, description, images, and SEO metadata can be cached or statically generated. Price and stock may need short revalidation, tag-based revalidation after catalog updates, or request-time fetching if showing stale values would be harmful.
The mistake is treating the whole page as one rendering mode. Next.js lets you split the problem: keep the mostly static shell fast, then choose explicit freshness rules for the data that changes.
"The product page is a Server Component, but the Add to Cart button needs click state. What do you do?"
Keep the page as a Server Component and move only the interactive button into a Client Component.
// app/products/[id]/page.tsxexport default async function ProductPage({params,}: {params: Promise<{ id: string }>;}) {const { id } = await params;const product = await getProduct(id);return (<><h1>{product.name}</h1><AddToCartButton productId={id} /></>);}
Do not move the whole route to the client. Put the client boundary at the smallest component that needs browser behavior.
"Should
/search?q=reactbe statically generated?"
Probably not as a fully static page for every possible query. The route can render a reusable shell, but results depend on searchParams, user input, ranking, and freshness. Explain whether the results should be fetched on the server for SEO/shareable URLs, on the client for highly interactive filtering, or through a mix.
Mention loading, empty, and error states. Search pages fail interviews when candidates only talk about routing and forget the UI states users will actually see.
"A logged-out user visits
/dashboard. Where should the redirect happen?"
If the server can know from cookies that the user is logged out, redirect before rendering protected UI. Depending on the app, that can happen in server route logic or Proxy for broad route gates.
Do not rely only on useEffect(() => router.push('/login')). That flashes protected UI and ships dashboard JavaScript to a user who should not see it.
"The server renders light theme, but the client reads
localStorage.themeand switches to dark during hydration. Why is React warning?"
The first client render does not match the server HTML. Fix it by making the initial render deterministic, delaying browser-only reads until after hydration, using a cookie-backed theme available to the server, or rendering a small client theme boundary that handles the mismatch intentionally.
The word "hydration" alone does not answer the question. Name the mismatch and fix the first render.
Next.js is a React framework for building web applications. It adds routing, server rendering, static generation, data fetching patterns, image optimization, metadata handling, API route handlers, and deployment conventions around React.
Plain React mainly gives you the UI layer. Next.js decides how routes are mapped to files, how pages are rendered, how data can be fetched on the server, and how the app is bundled and optimized.
Say why: If asked "Why use it?", give a product reason: server-rendered pages for SEO, route-based code splitting, safer server data access, built-in metadata, and deployment conventions. Avoid saying only "because it improves performance"; name which part improves and why.
Official reference: Next.js App Router docs.
React is a library for building component-based UI. Next.js is a framework that uses React and adds application structure.
In a React app built with Vite, you choose your own router, data fetching setup, rendering model, and deployment pattern. In Next.js, file-system routing, server rendering, code splitting, metadata, and production build behavior come with the framework.
The App Router is the modern Next.js router based on the app/ directory. It uses React Server Components, layouts, nested routing, loading UI, error boundaries, route handlers, and streaming.
For example, app/products/page.tsx maps to /products, and app/products/[id]/page.tsx maps to dynamic product pages.
What to mention in 2026: App Router is the default mental model for new Next.js work. The Pages Router still exists, but App Router answers should use page.tsx, layout.tsx, Server Components, Route Handlers, and cache/revalidation APIs instead of starting with getStaticProps.
File-based routing means the folder and file structure defines the URL structure. In the App Router, a route segment becomes public only when it contains a page.tsx or page.js file.
For example:
app/page.tsxabout/page.tsxblog/[slug]/page.tsx
This creates /, /about, and /blog/:slug.
page.tsx, layout.tsx, and template.tsx?page.tsx renders the UI for a route. layout.tsx wraps a route segment and its children, and it is preserved across navigation when possible. template.tsx also wraps children, but it creates a new instance on navigation, so state inside it is reset.
Use layouts for shared shells like navbars and sidebars. Use templates when each navigation should remount that wrapper.
Dynamic routes handle URLs whose segment values are not known ahead of time. In the App Router, a folder like [slug] captures a value and passes it through params.
export default async function BlogPost({params,}: {params: Promise<{ slug: string }>;}) {const { slug } = await params;return <article>{slug}</article>;}
Catch-all routes use [...slug], and optional catch-all routes use [[...slug]].
generateStaticParams()?generateStaticParams() tells Next.js which dynamic routes to generate at build time. It replaces getStaticPaths from the Pages Router.
For example, a blog can fetch all slugs during the build and return { slug } objects. Next.js then pre-renders those pages instead of waiting for a first request.
Official reference: generateStaticParams.
Server Components render on the server and do not send their component code to the browser. They are the default in the App Router.
Use them for reading files, querying databases, fetching data with secrets, rendering static content, and reducing client-side JavaScript. They cannot use browser-only APIs, event handlers, or hooks like useState.
How it appears in interviews: "Why can't I add onClick here?" or "Why is window undefined?" The answer is that the component is rendering on the server, not in the browser. Add a Client Component only for the interactive part.
Official reference: Server and Client Components.
Client Components are React components that can run in the browser. Add the "use client" directive at the top of the file to mark a client boundary.
Use Client Components for state, event handlers, effects, browser APIs, focus management, animations that require browser state, and interactive forms.
"use client"?Use "use client" when the component needs client-only behavior: useState, useEffect, useReducer, DOM events such as onClick, or APIs such as window and localStorage.
Do not add "use client" to every file. Once a file is marked as a Client Component, its imported child components are part of that client bundle unless separated by server boundaries. Keeping static and data-heavy UI on the server reduces shipped JavaScript.
Yes. A Server Component can render a Client Component and pass serializable props to it. This is the common pattern for mixing server-rendered data with small interactive widgets.
A Client Component cannot directly import a Server Component, because client code runs in the browser. You can pass a Server Component as children to a Client Component from a server parent.
In Server Components, you can use async components and fetch directly on the server:
export default async function Page() {const response = await fetch('https://api.example.com/products');const products = await response.json();return <ProductList products={products} />;}
In Client Components, fetch with a client-side library or a custom hook when the data depends on browser interaction, local state, or live updates.
Trap: Server-side fetch() in Next.js is not always the same as browser fetch(). Next.js can add caching and revalidation behavior on the server. If the page must always be fresh, say so explicitly with the relevant cache option or rendering choice.
Official reference: Fetching Data.
Static rendering prepares HTML ahead of time and can be served quickly. Dynamic rendering creates the response at request time, which is needed when output depends on request-specific data such as cookies, headers, search params, auth state, or uncached data.
A fresher-friendly rule: use static rendering for public content that can be reused, and dynamic rendering for personalized or request-dependent content.
ISR means Incremental Static Regeneration. It lets static output be refreshed after a configured time or after an explicit revalidation event.
In the App Router, do not treat ISR as only the older Pages Router revalidate option. You will see fetch() cache options, revalidatePath(), revalidateTag(), and, in newer Next.js 16 codebases, Cache Components APIs such as "use cache" and cache tags.
Next.js has multiple caches. Route output, cached async work, server fetch() results, and client navigation data can all have different freshness rules.
You may see server fetch() caching controlled with cache: "force-cache", cache: "no-store", or next: { revalidate }. You may also see Cache Components controlled with "use cache", cacheLife(), cacheTag(), and updateTag(). The interview answer should name the cache and the freshness rule instead of saying "Next.js caches it."
Debugging answer: Name which cache you suspect. "Is the stale value from browser HTTP cache, Next.js server data cache, generated route output, client router cache, or our own data library?" That question beats guessing at one cache blindly.
Official reference: Caching.
loading.tsx?loading.tsx defines loading UI for a route segment. Next.js can show it while the route or a nested part of the route is loading.
It supports streaming because the user can see part of the page while slower server work continues.
error.tsx?error.tsx defines an error boundary for a route segment. It catches uncaught runtime errors from that segment and lets you render a fallback UI.
In the App Router, error.tsx must be a Client Component because error boundaries use client-side React behavior.
not-found.tsx?not-found.tsx defines the UI for a route segment's 404 state. You can trigger it by calling notFound() from next/navigation.
Use it when the route exists but the resource does not, such as /blog/missing-slug.
Route Handlers let you create API endpoints inside the App Router using route.ts or route.js. They support HTTP methods such as GET, POST, PUT, and DELETE.
export async function GET() {return Response.json({ ok: true });}
They fit webhooks, BFF endpoints, auth callbacks, and server-only operations that the browser should not implement directly.
Server Functions are async server-side functions marked with the "use server" directive. In form submissions or mutation flows, they are called Server Actions.
For a fresher interview, explain the idea carefully: the browser submits data, but the trusted mutation logic runs on the server.
A Route Handler exposes an HTTP endpoint. Other clients can call it if they know the URL and have permission.
A Server Action is a server function integrated with React and Next.js for form and mutation flows. Use Route Handlers for external HTTP APIs and webhooks. Use Server Actions for app-owned mutations tied to UI flows.
SSR renders HTML on the server for each request. SSG creates HTML at build time. CSR sends a JavaScript app shell and renders mainly in the browser. ISR serves static output but regenerates it after revalidation.
Next.js can mix these patterns route by route and even within a route when streaming and caching are involved.
Hydration is the process where React attaches event handlers and client-side behavior to HTML that was already rendered on the server.
Server-rendered HTML can be visible before it is interactive. Client Components need hydration before clicks, inputs, and stateful behavior work.
Hydration errors happen when the HTML rendered on the server does not match what React expects on the client.
Common causes include rendering Date.now() or Math.random() directly in markup, reading window during server render, changing output based on browser-only state, invalid HTML nesting, or mismatched data between server and client.
How to fix it: Make the initial server and client render match. Move browser-only reads to an effect, pass server-known values through cookies or props, or isolate the unstable UI in a Client Component that renders a stable placeholder first.
next/link used for?next/link enables client-side navigation between routes. It avoids a full page reload and can prefetch route data when links are likely to be visited.
Use <Link href="/dashboard">Dashboard</Link> for internal navigation. Use a normal <a> for external links.
useRouter() used for?useRouter() is used in Client Components for programmatic navigation, such as redirecting after a user clicks a button or completes a client-side interaction.
In Server Components, use server-side helpers such as redirect() from next/navigation instead.
In the App Router, you can export a static metadata object or an async generateMetadata() function from a page or layout.
Use static metadata when the title and description are known. Use generateMetadata() when metadata depends on params or fetched data.
Official reference: generateMetadata.
next/image?next/image is Next.js's image component. It handles image sizing, optimization, lazy loading, and layout stability when used correctly.
Interviewers ask about it because images are among the largest page assets, and a framework image component can reduce layout shift and improve loading behavior.
next/font?next/font loads and optimizes fonts. It can self-host supported fonts and reduce layout shift by generating font-related CSS during the build.
Use it instead of manually adding third-party font <link> tags when possible, because font loading affects performance and visual stability.
Proxy is the modern name for what older Next.js versions called Middleware. A proxy.ts file can run before a request completes and can redirect, rewrite, set headers, read cookies, or respond early.
Use it for request-level logic such as auth redirects, locale routing, and header changes. Do not put heavy application logic there.
Auth nuance: Proxy runs before route rendering, so it works for broad request decisions. It is not a replacement for server-side authorization inside data access or mutations. If the dashboard route is protected, both the route and the backend operation should still enforce permission.
Official reference: proxy.js file convention.
Server-only environment variables are available to server code and should be used for secrets such as API keys. Variables prefixed with NEXT_PUBLIC_ are bundled for the browser and must not contain secrets.
The interview trap is treating every environment variable as secret. Anything sent to client JavaScript can be viewed by users.
Authentication involves a session cookie or token, server-side validation, and route protection. Server Components can read request data such as cookies, Route Handlers can implement auth callbacks, and Proxy can redirect unauthenticated users before rendering.
Avoid trusting only client-side checks. The server must protect sensitive data and mutations.
redirect() and client-side navigation?redirect() from next/navigation is used during server rendering or server logic to stop rendering and send the user to another route.
Client-side navigation with router.push() happens in a Client Component after browser interaction. Use the server redirect when the server already knows the user should not see the current route.
The standard production flow is to run next build, then host the generated server output on a platform that supports the chosen Next.js features. Vercel supports Next.js features directly, but Next.js can also run on Node-based hosts and other platforms with the right adapter or deployment setup.
Freshers should know that a static-only export cannot support every server feature. Server rendering, Route Handlers, Server Actions, and request-time data need a runtime.
Build one small app with public pages, a dynamic detail page, a form, a Route Handler, metadata, a loading state, and a protected dashboard. That is enough to discuss most junior Next.js topics.
Practice with a mini product catalog or blog: list page, detail page, search params, server-fetched data, a client filter, a form mutation, and a small auth gate.
Next.js supports multiple rendering patterns. A page might be statically generated, dynamically rendered, partially streamed, hydrated on the client, or driven by client-side data after the first load.
"use client" everywhereThis removes many benefits of Server Components. Add "use client" at the smallest boundary that needs browser behavior.
getStaticProps, getServerSideProps, and getStaticPaths belong to the Pages Router. In the App Router, learn Server Components, fetch() options, generateStaticParams(), generateMetadata(), Route Handlers, and cache revalidation APIs.
Server Components and Route Handlers can access secrets. Client Components cannot keep secrets because their JavaScript is sent to the browser.
Build a small product catalog:
/products lists products and has a loading state./products/[id] fetches a product on the server and calls notFound() when missing.AddToCartButton is the only Client Component on the detail page.POST /api/cart is implemented as a Route Handler or app-owned mutation path.When explaining the solution, say which parts run on the server, which parts hydrate, which data can be cached, and where secrets would live. That is the real interview signal.
Next.js fresher interviews become clearer when you explain each feature by execution location: server, client, build, or request boundary. That mental model prevents most wrong answers.

Redux interview questions for freshers test whether you can choose and explain a state model, not whether you memorized the phrase "single source of truth." Connect the UI problem to Redux's data flow: state lives in a store, the UI dispatches events, reducers calculate the next state, and components read only the data they need.
In 2026, answer Redux questions with Redux Toolkit as the default. Older tutorials show hand-written action types, createStore(), switch-heavy reducers, and lots of boilerplate. You should still understand those ideas, but modern Redux code normally uses configureStore(), createSlice(), React Redux hooks, and RTK Query or thunks for async work.
If you want hands-on practice, implement Redux Store and Redux Store II on GreatFrontEnd after reading this guide.
| Interview prompt | What the interviewer is checking | Common fresher mistake |
|---|---|---|
| "The navbar cart count updates from many pages. Where should that state live?" | Shared state and component communication | Putting everything in Redux without explaining why |
| "This reducer pushes into an array. Is that okay?" | Immutability and Redux Toolkit/Immer | Saying all mutation is always wrong, even inside createSlice() |
| "Why does this component re-render on every action?" | Selector return values and reference equality | Returning a new object from useSelector() every time |
| "Should a text input use Redux?" | Local vs global state | Dispatching on every keystroke by default |
| "How do you fetch products and cache them?" | Thunks vs RTK Query vs component fetch | Writing loading reducers for every endpoint without considering server-state tooling |
"A user can add items from product pages, search results, and recommendations. The navbar badge should update everywhere."
Redux is a reasonable choice because multiple distant components need the same state and the actions are meaningful product events: cart/itemAdded, cart/itemRemoved, cart/quantityChanged. The reducer owns the cart state; selectors expose selectCartCount and selectCartTotal.
Do not stop at "Redux avoids prop drilling." Name the shared state, the events, and the derived selectors the UI needs.
"A modal has a search box. Should the input value go into Redux?"
Usually no. The typed value is temporary UI state owned by one component. Keep it in React state unless another feature needs it, the value must survive navigation, or it is part of a shareable URL.
This scenario checks judgment. Redux skill includes knowing when not to use Redux.
"Where should product list loading, error, and cached data live?"
If the app already uses Redux Toolkit, RTK Query fits this case because product data is server state: it needs fetching, caching, invalidation, and refetching. A thunk still works for custom flows, but server state and client state are different problems.
"This selector returns
{ count, total }, and the component re-renders after unrelated actions. Why?"
Returning a new object creates a new reference on every store update. Split selectors, use a memoized selector, or use an equality function when appropriate.
const count = useSelector(selectCartCount);const total = useSelector(selectCartTotal);
The interviewer is checking whether you understand React Redux's render behavior, not only Redux vocabulary.
"A user likes a post. The UI updates immediately, but the API fails. What should happen?"
Dispatch an optimistic action, keep enough information to roll back, and handle the failure action by reverting or showing a retry state. For server-state flows, use the mutation lifecycle provided by the data library.
The product detail matters: a like can be retried or reverted; a payment cannot be treated casually.
Provider, useSelector, and useDispatch?Redux is a predictable state management library for JavaScript apps. It stores application state in a single store and updates that state by dispatching actions to reducers.
In React apps, Redux fits state that many components need, state that benefits from debugging history, or server/cache state managed through Redux Toolkit Query.
Add this detail: Do not pitch Redux as "global variables for React." Say it fits state transitions that need structure, traceability, and consistent reads across distant components.
Official reference: Getting Started with Redux.
Redux solves prop chains through many component layers, shared reads across distant components, and state transitions that need a traceable action history.
It is not needed for every piece of state. Input text, modal open state, hover state, and small component-only state belong in React local state.
Example: Cart, auth session metadata, feature flags, editor document state, and cross-page notification queues have a clearer Redux payoff than a single input's current value.
The core concepts are store, actions, and reducers.
The store holds state. Actions describe what happened. Reducers receive the previous state and an action, then return the next state.
The store is the object that holds the entire Redux state tree. It lets you read state with getState(), update state with dispatch(action), and subscribe to changes.
In modern Redux, create the store with Redux Toolkit's configureStore(), not the older createStore() API.
Official reference: Redux Store API.
An action is a plain object that describes something that happened in the app. It must have a type field and may include a payload.
{type: "cart/itemAdded",payload: { id: "p1", quantity: 1 }}
Good action names describe events, not setter operations. cart/itemAdded is clearer than SET_CART.
A reducer is a pure function that calculates the next state from the previous state and an action.
function counterReducer(state = { value: 0 }, action: { type: string }) {if (action.type === 'counter/incremented') {return { value: state.value + 1 };}return state;}
Reducers should not fetch data, mutate external variables, read time randomly, or dispatch actions.
How to test this answer: A reducer should be easy to unit test with plain inputs and outputs. If the reducer needs the current time, random IDs, network data, or localStorage, that work belongs before the action is dispatched or in async/middleware logic.
Pure reducers make Redux predictable. Given the same previous state and action, the reducer should return the same next state.
This predictability enables debugging, replaying actions, testing reducers as simple functions, and understanding state changes from the Redux DevTools history.
dispatch() sends an action to the Redux store. The store runs the root reducer with the current state and the dispatched action, then stores the new state.
In React components, you normally dispatch action creators:
const dispatch = useDispatch();dispatch(cartItemAdded({ id: 'p1' }));
Redux data flow goes in one direction:
This keeps state changes from happening in many unrelated places.
Redux Toolkit is the official recommended way to write Redux logic. It includes helpers like configureStore(), createSlice(), createAsyncThunk(), and RTK Query.
It reduces boilerplate, sets up store defaults, includes development checks for common mistakes, and lets reducers use draft mutation syntax through Immer.
Official reference: Redux Style Guide.
configureStore()?configureStore() creates a Redux store with sensible defaults. It combines reducers, adds middleware, enables Redux DevTools in development, and includes checks that catch accidental mutations and non-serializable values.
import { configureStore } from '@reduxjs/toolkit';export const store = configureStore({reducer: {cart: cartReducer,user: userReducer,},});
createSlice()?createSlice() creates a slice reducer, action creators, and action types from one feature-focused definition.
const counterSlice = createSlice({name: 'counter',initialState: { value: 0 },reducers: {incremented(state) {state.value += 1;},},});
The code looks like mutation, but Redux Toolkit uses Immer to produce immutable updates safely.
Fresher trap: This does not mean "Redux allows mutation now" everywhere. The safe draft mutation applies inside Immer-powered reducers created by Redux Toolkit. Outside that boundary, keep immutable update rules in mind.
A slice is the Redux logic for one feature or domain, such as cart, auth, todos, or products. It contains initial state, reducers, generated actions, and selectors for that feature.
Feature-based slices keep related state, actions, and reducers in the same file instead of scattering one feature across action, reducer, and constant folders.
Redux Toolkit uses Immer. Immer gives your reducer a draft version of state. You write changes to the draft, and Immer produces the next immutable state behind the scenes.
This is why state.value += 1 is okay inside a createSlice() reducer but mutating state directly in a plain hand-written reducer is not okay.
Immutability means you do not change the existing state object. You return a new object or array for changed parts and reuse unchanged parts.
Redux relies on reference changes to know what changed. If you mutate the same object and return it, React Redux may not detect a change correctly.
React Redux is the official binding library that connects React components to a Redux store. It provides <Provider>, useSelector(), and useDispatch().
<Provider store={store}> makes the Redux store available to components. Hooks then read and update the store.
Official reference: React Redux hooks.
<Provider> do?<Provider> passes the Redux store through React context so any nested component can use React Redux hooks.
Without <Provider>, useSelector() and useDispatch() do not know which Redux store to use.
useSelector()?useSelector() reads a value from the Redux store. It accepts a selector function and re-renders the component when the selected value changes.
const cartCount = useSelector((state: RootState) => state.cart.items.length);
Return the smallest value the component needs. Returning a new object every time can cause unnecessary re-renders.
Bad pattern:
const cart = useSelector((state) => ({count: state.cart.items.length,total: state.cart.total,}));
This creates a new object on every store update. Prefer separate selectors or a memoized selector when returning derived objects.
useDispatch()?useDispatch() returns the store's dispatch function so a component can dispatch actions.
const dispatch = useDispatch();function onAddToCart(id: string) {dispatch(itemAdded({ id }));}
In TypeScript apps, many teams create a pre-typed useAppDispatch() hook based on the store's dispatch type.
Selectors are functions that read specific data from Redux state.
const selectCartItems = (state: RootState) => state.cart.items;
Selectors keep components from knowing the exact state shape. If the store shape changes, you update selectors rather than every component.
Memoized selectors cache derived results. Use them for filtered lists, totals, or expensive derived values from state.
Use memoization when the derived calculation is expensive or returns new arrays/objects that would otherwise cause unnecessary re-renders.
Reducers must stay pure, so async work happens outside reducers. Common options are thunks, createAsyncThunk(), RTK Query, or custom middleware.
For freshers, know the common flow: dispatch pending, perform the request, dispatch success or failure, and update state based on those actions.
How to choose: Use RTK Query for normal CRUD/server data. Use thunks for custom workflows that coordinate multiple actions, read current state, or mix API calls with client-owned state changes.
A thunk is a function that can contain async logic and dispatch actions later. Redux Toolkit includes thunk middleware by default.
export const fetchUser = (id: string) => async (dispatch: AppDispatch) => {dispatch(userRequested());const user = await api.getUser(id);dispatch(userReceived(user));};
Use thunks for app logic that needs dispatch, getState, or multiple actions around one async operation.
createAsyncThunk()?createAsyncThunk() is a Redux Toolkit helper for request-style async logic. It automatically creates pending, fulfilled, and rejected action types.
Reducers handle those cases in extraReducers, which keeps loading and error states predictable.
RTK Query is Redux Toolkit's data fetching and caching tool. It can generate hooks for queries and mutations, cache server responses, deduplicate requests, and invalidate data by tags.
Use RTK Query when the main problem is server data. Use slices and reducers when the main problem is client-owned application state.
Official reference: RTK Query overview.
Use React Context for dependency-style values such as theme, locale, or current user metadata. Use Redux when state changes frequently, many components need slices of it, debugging history matters, or update logic is complex.
Context alone does not provide reducers, middleware, DevTools history, normalized patterns, or built-in server-state caching.
Middleware runs between dispatching an action and the action reaching reducers. It can log actions, handle async functions, report analytics, or intercept certain actions.
Reducers should not have side effects. Middleware is one safe place for side-effect logic.
Redux DevTools show dispatched actions, state changes, and the current store state. They help debug why UI changed and what action caused it.
This is one reason Redux still appears in larger codebases: it gives teams a common timeline for state transitions.
Avoid putting non-serializable values such as DOM nodes, class instances, Promises, functions, and raw Date objects in Redux state. Also avoid storing tiny UI state that only one component uses.
Redux state should be serializable because DevTools, persistence, debugging, and predictable updates depend on plain data.
Reducers are easy to test because they are pure functions. Pass an initial state and an action, then assert the returned state.
For React components connected to Redux, render the component with a real test store when possible. For async data, mock the network boundary or use RTK Query testing patterns instead of testing internal implementation details.
Good reducer test shape:
expect(cartReducer({ items: [] }, itemAdded({ id: 'p1' }))).toEqual({items: [{ id: 'p1', quantity: 1 }],});
This tests the state transition rather than the implementation.
Redux is a tool for shared, traceable, complex state. A small form or one-page widget does not need it.
state.items.push(item) is only safe inside Redux Toolkit's Immer-powered reducers. In plain reducers, return a new array or object.
useSelector()If a selector returns a new object on every store update, the component can re-render on unrelated changes. Select the exact values needed or use memoized selectors.
Server state comes from the backend and needs fetching, caching, invalidation, and refetching. Client state is owned by the UI. RTK Query fits server state more directly than hand-writing loading reducers for every endpoint.
Build a mini cart state model:
cartSlice with itemAdded, itemRemoved, and quantityChanged.useSelector().itemAdded() from a product card.This small drill proves more Redux understanding than memorizing ten definitions.
For a fresher interview, a solid Redux answer has three parts: describe the data flow, state why reducers stay pure, and mention Redux Toolkit as the modern default.

Rippling frontend interview questions usually test practical React, JavaScript concurrency, schema-driven forms, API-backed admin UI, and frontend system design. Prepare to build working features quickly, run code, explain state ownership, and reason about payroll, reporting, workflow, and employee-management interfaces.
For the company-guide view, use the Rippling Front End Interview Guide alongside this article.
Use each question as a working session: ship the baseline first, then explain what changes for validation, pagination, permissions, large data, accessibility, retries, and cross-functional ownership.
Rippling is a broad enterprise SaaS product. A frontend engineer might work on onboarding, employee records, payroll, benefits, app provisioning, spend management, reports, or Workflow Studio. That product context explains why the interview loop mixes React implementation, JavaScript utilities, DSA, and frontend system design.
| Area | What to practice | Why it matters at Rippling |
|---|---|---|
| React implementation | Schema forms, paginated lists, data tables, grids, editable records, filters, workflow builders | Admin workflows are form-heavy and data-heavy. |
| JavaScript | Concurrency-limited task runner, event emitter, promise race, bind, flatten, recursive counts | Interviews check whether you can write utilities without hiding behind React. |
| API-backed UI | Cursor pagination, load more, infinite scroll, dedupe, loading/error states, optimistic updates | Rippling interfaces often read and mutate company data through APIs. |
| State modeling | Field schemas, derived validity, dependent fields, row-level edits, undo/redo, commit/rollback | Enterprise UI punishes duplicated or unclear state. |
| DSA | Trees, arrays, subarray sums, grid logic, hash maps, recursion | Some loops include a separate algorithm round. |
| Frontend system design | Employee directory, reports builder, payroll dashboard, Workflow Studio, feed or masonry layout | Senior rounds test product tradeoffs and browser architecture. |
| Behavioral | Ownership, speed, project scope, collaboration, production follow-through | Hiring-manager rounds dig into how you ship with product, design, and backend. |
The right prep is not LeetCode-only and not React-only. Rippling rewards engineers who can turn an ambiguous admin workflow into a working, testable interface.
Rippling's exact process varies by team. The company hires across many products, so a frontend-leaning role can still include backend API or general coding rounds. Treat recruiter instructions as the source of truth.
Expect a process shaped roughly like this:
| Stage | What to expect | Prep note |
|---|---|---|
| Recruiter screen | Background, motivation, role fit, compensation, team context | Ask whether the role is frontend-only or frontend-leaning full stack. |
| Technical phone screen | Live React, JavaScript utility, or API-backed UI coding | Practice on a small starter project and run code as you go. |
| Hiring manager round | Project depth, ownership, product judgment, collaboration, tradeoffs | Prepare one detailed project story with architecture, rollout, and metrics. |
| Onsite React round | Build an incremental UI feature with state, fetching, validation, tests | Expect follow-ups that add pagination, infinite scroll, dependent fields, or dedupe. |
| DSA / JavaScript round | Trees, arrays, recursion, event emitter, concurrency, flatten, bind | Keep practical JavaScript and algorithm basics warm. |
| System design round | Feed, masonry layout, reports builder, employee directory, workflow UI | Ground the answer in enterprise admin UI behavior, permissions, and large data. |
| Behavioral / team round | Cross-functional work, ownership, pace, mistakes, conflict | Prepare concrete STAR stories; avoid generic teamwork answers. |
If the loop includes a web API round, practice a small Node or Express service with CRUD endpoints, validation, error responses, and a short discussion of auth, logging, rate limits, and monitoring.
Use these as practice prompts, not a guaranteed question bank. They cover the recurring Rippling shape: practical React, async JavaScript, data-heavy admin screens, and system design.
| Rippling question or task | What to practice |
|---|---|
| Build a grid of lights in React where clicking a cell toggles its state and derived counters update. | Grid state, immutable updates, derived counts, testable components |
| Build a 2048-style board model with initialization, move logic, merge behavior, and tests. | 2D arrays, pure functions, edge cases, unit tests |
| Build a paginated list that fetches items from an API and appends the next page when the user clicks "Load more." | Fetch state, cursor tokens, dedupe, loading and error states |
Extend the paginated list with infinite scroll using IntersectionObserver and a throttled fallback. | Browser APIs, cleanup, duplicate request prevention |
| Build a schema-driven React form with text, select, checkbox, and dependent fields. | Schema modeling, controlled inputs, validation, field dependencies |
| Add form submission that returns values, validation state, and field-level errors. | Error display, submit gating, API contract, accessibility |
| Implement a task runner that executes async tasks with a concurrency limit and preserves result order. | Promises, queues, worker loops, error policy |
| Implement promise race behavior and explain how cancellation differs from ignoring stale results. | Promise timing, cleanup, AbortController, stale guards |
| Count all comments in a nested replies tree. | Recursion, iterative traversal, tree data |
| Implement an event emitter or pub/sub utility with cleanup. | Map, Set, listeners, unsubscribe, one-time handlers |
| Flatten a nested array where top-level items keep priority over deeper items. | Queue traversal, recursion vs iteration, ordering rules |
Implement Function.prototype.bind behavior for normal calls and partial arguments. | this, closures, function context |
| Solve max tree depth and subarray sum style questions. | DFS, hash maps, prefix sums, complexity |
| Build a small employee directory with search, filters, row actions, and permission-aware columns. | Admin UI, data table state, bulk actions, role-based rendering |
| Design a social feed or activity feed for company events such as hires, approvals, payroll status, and app provisioning. | Feed ranking, pagination, optimistic updates, dedupe, moderation |
| Design a masonry layout for cards with variable heights and large image or report previews. | Layout algorithms, responsive columns, virtualization |
| Design a reports builder where users pick attributes, group rows, filter columns, and drill into aggregate values. | Frontend system design, query lifecycle, preview state, cancellation |
| Design a Workflow Studio-style automation builder with trigger nodes, action nodes, step configuration, undo/redo, and execution log. | Graph UI, schema-aware forms, validation, local drafts, long-running jobs |
Rippling-shaped forms are rarely just two inputs and a submit button. Benefits enrollment, payroll setup, app provisioning, onboarding, and workflow actions all depend on field schemas, validation rules, permissions, and dependent fields.
Start with a small schema:
type FieldSchema =| {id: string;label: string;type: 'text';required?: boolean;}| {id: string;label: string;type: 'select';options: Array<{ label: string; value: string }>;required?: boolean;dependsOn?: string;}| {id: string;label: string;type: 'checkbox';};type FormState = Record<string, string | boolean>;
Then build in layers:
A good answer covers:
isValid separately when it can be derived from values and schema.Practice this with Contact Form for validation habits and Users Database for CRUD-style state.
A Rippling UI often starts as "fetch and display a list" and then grows: cursor pagination, load more, infinite scroll, dedupe, sorting, filters, and retries.
Use a state shape that separates data, cursor, and request status:
type PageItem = { id: string };type PageState<T extends PageItem> = {items: Array<T>;nextCursor: string | null;status: 'idle' | 'loading' | 'success' | 'error';errorMessage?: string;};
The fetch function should prevent duplicate in-flight requests and ignore stale responses:
let activeRequestId = 0;async function loadNextPage() {const cursor = state.nextCursor;if (state.status === 'loading' || cursor == null) {return;}const requestId = ++activeRequestId;setState((current) => ({...current,errorMessage: undefined,status: 'loading',}));try {const response = await fetchItems({ cursor });// A newer request has started, so this response should not update the UI.if (requestId !== activeRequestId) {return;}setState((current) => {const seenIds = new Set(current.items.map((item) => item.id));// Cursor APIs can overlap pages, so append only unseen records.const newItems = response.items.filter((item) => !seenIds.has(item.id));return {...current,items: [...current.items, ...newItems],nextCursor: response.nextCursor,status: 'success',};});} catch {if (requestId !== activeRequestId) {return;}setState((current) => ({...current,errorMessage: 'Could not load more items.',status: 'error',}));}}
In the interview, discuss what you would tighten in production code:
AbortController when the fetch layer supports cancellation.IntersectionObserver for infinite scroll, with a visible fallback button.Practice Job Board and Data Table until this flow feels automatic.
The concurrency-limited task runner is a compact JavaScript problem with product value. Rippling's platform work includes workflows, reports, provisioning steps, and async jobs where unbounded parallelism can overload a service.
Start with the contract:
limit tasks run at the same time.One implementation:
async function runWithConcurrency<T>(tasks: Array<() => Promise<T>>,limit: number,): Promise<Array<T>> {if (!Number.isInteger(limit) || limit < 1) {throw new RangeError('limit must be a positive integer');}const results = new Array<T>(tasks.length);let nextIndex = 0;async function worker() {while (nextIndex < tasks.length) {// Claim the next task synchronously before awaiting its result.const currentIndex = nextIndex;nextIndex += 1;results[currentIndex] = await tasks[currentIndex]();}}const workerCount = Math.min(limit, tasks.length);await Promise.all(Array.from({ length: workerCount }, worker));return results;}
Then explain follow-ups:
limit < 1.Promise.all, or collect { status, value, reason } per task.AbortSignal into tasks when the caller cancels.This is a better answer than using Promise.all(tasks.map(...)), because unbounded concurrency is the bug the prompt is trying to reveal.
Game prompts test state transitions without CSS distraction. For 2048, keep the board logic pure:
type Board = Array<Array<number>>;function compact(row: Array<number>): Array<number> {return row.filter((value) => value !== 0);}function mergeLeft(row: Array<number>): Array<number> {const values = compact(row);const merged: Array<number> = [];for (let index = 0; index < values.length; index += 1) {if (values[index] === values[index + 1]) {merged.push(values[index] * 2);// Skip the next tile because it has already been merged.index += 1;} else {merged.push(values[index]);}}while (merged.length < row.length) {merged.push(0);}return merged;}
After mergeLeft works, derive other directions by reversing or transposing the board. Add tests before UI:
[2, 0, 2, 0] -> [4, 0, 0, 0][2, 2, 2, 2] -> [4, 4, 0, 0][4, 4, 8, 0] -> [8, 8, 0, 0][2, 4, 8, 16] -> [2, 4, 8, 16]For grid-light prompts, use the same habit: pure toggle logic first, React state second. Interviewers can change the prompt quickly, and pure functions make follow-ups easier.
Rippling's engineering blog on real-time reporting is useful background because reports combine frontend attribute selection with query planning, permissions, caching, and large result sets. A frontend system design answer should not try to design the whole analytics backend. Focus on the browser contract.
Clarify scope:
Then structure the design:
| Design area | What to cover |
|---|---|
| Attribute picker | Search, grouping by product area, permissions, selected-field summary, keyboard navigation |
| Query builder | Client-side report config, validation, dependent options, disabled invalid states |
| Preview lifecycle | Debounced preview, cancellation, stale response guard, loading, empty, error, sampled preview |
| Result table | Pagination, virtualization, column resizing, sorting, drilldown, export, copy |
| Caching | Cache by report config and permission context; invalidate when filters, role, or fields change |
| Drafts | Autosave, dirty state, undo/redo, conflict behavior when another edit wins |
| Accessibility | Form labels, keyboard access, grid navigation, focus recovery after preview updates |
| Observability | Track slow previews, canceled requests, empty results, field-search misses, and export errors |
Good follow-up answers:
This is the kind of system design that feels closer to Rippling than a generic social feed.
Rippling prep should keep practical JavaScript and DSA warm at the same time.
| Topic | Practice questions |
|---|---|
| Async JavaScript | Task runner with concurrency, promise race, async memoization, retry, stale response handling |
| Events | Event emitter, pub/sub, unsubscribe cleanup, one-time listeners |
| Arrays and functions | Flatten with custom ordering, bind polyfill, grouping, dedupe by ID, array transforms |
| Trees and recursion | Count nested comments, max tree depth, tree filtering, nested menu rendering |
| Hash maps | Subarray sum, frequency counts, duplicate detection, lookup tables |
| React UI | Schema forms, data table, paginated list, grid of lights, editable rows, controlled inputs |
| System design | Reports builder, employee directory, payroll dashboard, Workflow Studio, feed, masonry layout |
Useful GreatFrontEnd practice:
Use Rippling's own product and engineering material to make system design answers concrete:
Use this as a Rippling-specific checklist. The exact order can change, but the coverage should stay the same: practical React, async JavaScript, admin UI system design, DSA, and project depth.
| Prep area | What to do | Rippling-specific angle |
|---|---|---|
| Practical React | Build schema form, paginated list, infinite scroll, data table, grid of lights, editable rows, and search/filter UI. | These map to employee records, reports, approvals, payroll setup, and onboarding workflows. |
| Async JavaScript | Implement concurrency-limited task runner, event emitter, async memoization, promise race, debounce, and retry with backoff. | Rippling screens often check whether you can control async work without library help. |
| DSA | Practice max tree depth, subarray sum, tree traversal, flatten, hash maps, and grid logic. | Some loops include a separate algorithm round. |
| Frontend system design | Design employee directory, reports builder, payroll run dashboard, Workflow Studio, and an activity feed. | Cover permissions, large data, long-running jobs, drafts, validation, and error states. |
| API-backed UI | Build a small mocked API and wire React to it with loading, empty, error, retry, pagination, and optimistic updates. | Frontend-leaning roles can still ask for web API thinking. |
| Behavioral and project depth | Prepare one deep project walkthrough and 5-7 STAR stories around ownership, speed, collaboration, conflict, and mistakes. | Hiring-manager rounds dig into what you personally owned and how you worked with other disciplines. |
| Mock loop | Run one React mock, one JavaScript utility mock, one DSA mock, one system design mock, and one project deep dive. | Fix the weakest answer after each mock. |
If you have only 72 hours, prioritize schema form, paginated list with infinite scroll, task runner with concurrency, event emitter, max tree depth, subarray sum, reports-builder system design, and your strongest project story.
Rippling frontend prep should end with one clear story: "I can take an ambiguous admin workflow and turn it into a working, testable, API-backed UI." That means the best practice set is not a random pile of React trivia. It is schema forms, paginated lists, data tables, async concurrency, event emitters, tree traversal, and product-shaped system design.

Snowflake frontend interview questions usually test JavaScript fluency, React state, grid-style UI coding, and frontend system design for data tools. Prepare for interactive components, async state, graph or dynamic-programming follow-ups, and Snowsight-shaped interfaces such as worksheets, dashboards, result grids, and AI-assisted SQL.
For the company-guide view, use the Snowflake Front End Interview Guide alongside this article.
Use each question as a working session: build the baseline, then explain what changes for keyboard input, async loading, large results, permissions, accessibility, and slow queries.
Snowflake is a data cloud company, but frontend prep should not become database-internals prep. The frontend work is closer to building a browser-based workspace for analysts, engineers, and data teams: editors, query results, dashboards, catalog search, notebooks, Streamlit apps, and AI side panels.
| Area | What to practice | Why it matters at Snowflake |
|---|---|---|
| React implementation | Checkerboards, grid games, editable tables, typeahead, tabbed workspaces, dashboard tiles | Small UI prompts reveal state ownership, event handling, and follow-up resilience. |
| JavaScript | Event emitter, debounce, throttle, promises, this, closures, arrays, maps, undo/redo | Frontend rounds often check language fluency without much library help. |
| Data rendering | Result tables, pagination, virtualization, sorting, filtering, column sizing, empty states | Snowsight users inspect query output and dashboards inside the browser. |
| Async UI | Query execution, cancellation, stale response handling, loading/error states, retries | Data tools spend a lot of time waiting on remote work. |
| Algorithms | Grid traversal, shortest path, dynamic programming, Sudoku or Tic Tac Toe validation, graph search | UI-game prompts can turn into algorithmic follow-ups quickly. |
| Frontend system design | SQL worksheet, query-result viewer, dashboard builder, autocomplete, AI code assistant | Senior loops test whether you can design the client side of a data product. |
| Behavioral | Project depth, tradeoffs, ownership, customer focus, collaboration, mistakes | Snowflake teams care about how you make decisions in ambiguous product work. |
The right prep balance is JavaScript plus practical UI. Do enough Snowflake product reading to make your system design answers concrete, but spend most of your interview practice writing and explaining code.
Snowflake's hiring process page says engineering hiring consists of four stages and can take up to two to four weeks, with variation by team and role. It also says most open jobs include phone screens and onsite or video interviews.
Expect a process shaped roughly like this:
| Stage | What to expect | Prep note |
|---|---|---|
| Recruiter screen | Background, role fit, location, timeline, and team discussion | Ask whether the first technical round is React, JavaScript, DSA, or system design. |
| Technical screen | Live coding in JavaScript, TypeScript, React, or a shared editor | Practice from a blank file without relying on autocomplete. |
| Second technical screen | UI coding, graph/grid logic, or JavaScript utility implementation | Be ready for an interactive grid prompt followed by algorithmic follow-ups. |
| Onsite coding | Practical UI build, JavaScript fundamentals, or LeetCode-style problem | Build a working baseline before optimizing. |
| Frontend system design | Data-tool UI such as a worksheet, result grid, dashboard, or autocomplete | Spend more time on browser behavior, state, networking, and rendering than cloud boxes. |
| Project / behavioral | Deep project walkthrough, collaboration, tradeoffs, mistakes | Choose one frontend project where you can explain design, rollout, metrics, and failures. |
Treat your recruiter instructions as the source of truth. Some loops lean React-heavy; others include a general software engineering coding round.
Use these as practice prompts, not a guaranteed question bank. They cover the recurring shape: interactive grids, JavaScript utilities, React rendering, data-heavy UI, and frontend architecture.
| Snowflake question or task | What to practice |
|---|---|
| Build a checkerboard where the board size is configurable and each square renders the correct alternating color. | 2D rendering, derived cells, props, CSS grid, React state |
| Extend the checkerboard with selectable cells, keyboard navigation, and reset behavior. | Focus state, arrow keys, stable coordinates, accessibility |
| Build a robot-grid game where a robot moves within boundaries and cannot leave the board. | Event handling, grid state, boundary checks, testable move logic |
| Add randomized target cells, a score counter, and collision detection to the robot grid. | Derived state, random placement, win conditions, reset logic |
| Validate a Tic Tac Toe or Sudoku board and explain invalid states. | Game-state invariants, row/column scans, sets, edge cases |
| Find the minimum-cost path from one node or cell to another in a graph or grid. | BFS, Dijkstra-style thinking, dynamic programming, complexity |
Explain useMemo and useCallback, then decide where memoization helps in a table or editor UI. | React render behavior, reference identity, profiling judgment |
| Implement an event emitter with subscribe, unsubscribe, emit, and one-time listeners. | Map, Set, cleanup, listener mutation during emit |
| Implement debounce or throttle and explain where it belongs in a SQL editor or object search. | Timers, closures, cancellation, search pacing |
| Build a data table that supports sorting, pagination, loading, empty, and error states. | Large result rendering, state derivation, URL state, accessibility |
| Design a query-result viewer that can handle thousands of rows without freezing the page. | Virtualization, pagination, column sizing, copy behavior, query cancellation |
| Build an autocomplete for SQL keywords, table names, and column names. | Request ordering, ranking, permissions, keyboard combobox behavior |
| Implement undo/redo for a small calculator or editor command history. | Command pattern, reversible state, stacks, redo invalidation |
Recreate a subset of JSON.stringify or deep clone for common JavaScript values. | Recursion, type checks, arrays vs objects, unsupported values |
| Design a Snowsight-style worksheet with code editor, query execution, result panel, saved tabs, and query history. | Frontend system design, state ownership, async query lifecycle |
| Design a dashboard builder where each tile runs its own query and shares filters with other tiles. | Multi-request state, cache, chart rendering, partial failure |
| Explain one frontend project you owned, including constraints, tradeoffs, and what you would change now. | Project depth, communication, ownership |
The checkerboard prompt looks simple, but it tests whether you separate data from rendering. Start by naming the state:
One clean helper:
type Cell = {id: string;row: number;column: number;tone: 'light' | 'dark';};function createBoard(size: number): Array<Cell> {return Array.from({ length: size * size }, (_, index) => {// Convert the flat array index back into board coordinates.const row = Math.floor(index / size);const column = index % size;return {id: `${row}-${column}`,row,column,tone: (row + column) % 2 === 0 ? 'light' : 'dark',};});}
Then explain follow-ups:
createBoard(size) only when board creation or rendering becomes measurable; do not add memoization as decoration.For a robot grid, use the same coordinate model. The move function should be independent of React so it can be tested quickly:
type Position = {row: number;column: number;};function move(position: Position,direction: 'up' | 'down' | 'left' | 'right',size: number,): Position {const next = {up: { row: position.row - 1, column: position.column },down: { row: position.row + 1, column: position.column },left: { row: position.row, column: position.column - 1 },right: { row: position.row, column: position.column + 1 },}[direction];return {// Clamp movement so the robot stays inside the board.row: Math.max(0, Math.min(size - 1, next.row)),column: Math.max(0, Math.min(size - 1, next.column)),};}
This is the fastest path to a working answer: pure logic first, UI rendering second, follow-ups third.
React hook questions at Snowflake usually matter because data-tool UIs can rerender large trees: editors, result panes, side panels, schema browsers, and tables. Avoid memorized definitions. Explain what problem each hook solves.
| Hook | Useful answer | Bad interview habit |
|---|---|---|
useMemo | Cache an expensive derived value between renders when dependencies are stable. | Wrapping every computed value without measuring cost. |
useCallback | Keep a function reference stable when a memoized child or subscription depends on identity. | Using it for every event handler even when no child benefits. |
useRef | Store mutable values that should not trigger rerenders, such as latest request ID, DOM node, or timer ID. | Using refs to bypass React state for visible UI. |
useEffect | Synchronize with systems outside render: network subscriptions, timers, editor instances, browser events. | Fetching without cleanup or ignoring stale responses. |
For a result table, say this:
useMemo for derived rows only if filtering, sorting, or grouping is expensive enough to matter.useCallback when row actions are passed into memoized row components.Practice this answer with Data Table and Users Database. The useful skill is deciding where state lives and what is derived, not reciting hook definitions.
An event emitter maps well to Snowflake-style frontends because editor events, query status updates, workspace tabs, and panels all need subscription cleanup.
Start with the API:
type Listener<T = unknown> = (payload: T) => void;class EventEmitter {private listeners = new Map<string, Set<Listener>>();on(eventName: string, listener: Listener) {if (!this.listeners.has(eventName)) {this.listeners.set(eventName, new Set());}this.listeners.get(eventName)!.add(listener);return () => this.off(eventName, listener);}off(eventName: string, listener: Listener) {const listeners = this.listeners.get(eventName);if (listeners == null) {return;}listeners.delete(listener);if (listeners.size === 0) {this.listeners.delete(eventName);}}emit(eventName: string, payload?: unknown) {const listeners = this.listeners.get(eventName);if (listeners == null) {return;}// Snapshot listeners so unsubscribe calls during emit do not skip callbacks.for (const listener of Array.from(listeners)) {listener(payload);}}once(eventName: string, listener: Listener) {const unsubscribe = this.on(eventName, (payload) => {unsubscribe();listener(payload);});return unsubscribe;}}
Explain the edge cases after the baseline works:
emitonFor a frontend round, Map<string, Set<Listener>> is a good default. It gives constant-time deletion, avoids accidental duplicate listeners, and keeps cleanup easy to explain.
Snowflake product context makes table questions more important than they look. Snowsight dashboards and worksheets display query results, charts, and tables. A naive table is fine for 50 rows; it starts to break when the user sorts, filters, resizes columns, copies cells, runs a new query, or changes role permissions.
Start with a clear model:
type QueryResultState = {queryId: string;status: 'idle' | 'running' | 'success' | 'error' | 'canceled';columns: Array<{ id: string; label: string; type: string }>;rows: Array<Record<string, string | number | boolean | null>>;sort: { columnId: string; direction: 'asc' | 'desc' } | null;page: number;errorMessage?: string;};
Then cover:
Practice Data Table and How to handle large datasets in front-end applications, then adapt the answer to query results instead of generic users.
A Snowflake frontend system design round should spend most of its time on the browser contract. A good worksheet design includes a code editor, run controls, context selector, query history, result panel, saved tabs, schema browser, and an optional AI assistant panel.
Clarify scope first:
Then organize the answer:
| Design area | What to cover |
|---|---|
| Editor | Monaco-style editor, syntax highlighting, keyboard shortcuts, autocomplete providers |
| Query lifecycle | Run, cancel, rerun, timeout, syntax error, permission error, and result pagination |
| State ownership | Local draft, saved worksheet, active tab, warehouse/role context, query result cache |
| Result rendering | Pagination, row virtualization, column resizing, sticky headers, copy cell, chart handoff |
| Autocomplete | SQL keywords, functions, table names, column names, permission-aware catalog search |
| AI panel | Streaming suggestions, accepting/rejecting edits, cancellation, prompt context, audit trail |
| Performance | Lazy-load editor and chart bundles, avoid rendering all results, isolate heavy panels |
| Reliability | Ignore stale responses, recover drafts, retry transient failures, keep failed panels contained |
| Accessibility | Keyboard navigation, focus recovery, command labels, screen-reader-friendly status updates |
The strongest answers connect product behavior to implementation. For example, if the user changes role or warehouse, cached schema suggestions may no longer be valid. If a query is canceled, the result panel should show the canceled state for that query, not a generic error. If a large result set comes back, the app should render the first page quickly and avoid locking the main thread.
Useful GreatFrontEnd practice:
Snowflake prep should cover both frontend utilities and algorithmic grid problems.
| Topic | Practice questions |
|---|---|
| Arrays and strings | Flatten, deep clone, JSON.stringify, find duplicates, group records, merge sorted arrays |
| Timers and async | Debounce, throttle, promise utilities, stale response guards, retry with backoff |
| Events | Event emitter, pub/sub cleanup, keyboard handlers, listener mutation during emit |
| UI games | Checkerboard, robot grid, Tic Tac Toe, Sudoku validation, memory game |
| Graphs and grids | BFS, DFS, shortest path, minimum-cost path, visited-state tracking |
| React | Hooks, memoization, controlled inputs, derived state, focus management, rendering large lists |
| System design | Worksheet, autocomplete, result grid, dashboard, AI assistant panel |
Do not treat DSA as a separate universe. Many Snowflake-friendly coding prompts are grid UI prompts that become graph problems through follow-ups.
Use official Snowflake material to make system design answers concrete:
Use this as a Snowflake-specific checklist. The order matters less than the coverage: JavaScript, grid UI, data rendering, system design, and project depth.
| Prep area | What to do | Snowflake-specific angle |
|---|---|---|
| JavaScript fundamentals | Implement event emitter, debounce, throttle, deep clone, JSON.stringify, promise utilities, and undo/redo. | These show language control without framework help. |
| React UI coding | Build checkerboard, robot grid, Tic Tac Toe, typeahead, tabs, and editable table in timed 30-45 minute sessions. | Grid prompts map well to Snowflake's UI-coding style. |
| Data-heavy UI | Build data table, virtualized result viewer, dashboard tile layout, and schema browser. | Snowsight work depends on rendering, searching, and navigating large data views. |
| Algorithms | Practice BFS/DFS, shortest path, DP on grids, Sudoku validation, and graph traversal. | Some frontend prompts deepen into graph or path problems. |
| Frontend system design | Design worksheet, autocomplete, query-result grid, dashboard builder, and AI side panel. | Explain editor state, query lifecycle, cancellation, permissions, virtualization, and progressive results. |
| Behavioral and project depth | Prepare one project deep dive and 6-8 stories across ownership, collaboration, mistakes, mentoring, and tradeoffs. | Choose stories involving complex UI, performance, data visualization, platform tooling, or editor work. |
| Mock loop | Run one React mock, one JavaScript utility mock, one grid/DSA mock, one system design mock, and one project deep dive. | Fix weak answers after each mock instead of adding random questions. |
If you have only 72 hours, prioritize checkerboard or robot grid, event emitter, debounce, data table, one graph shortest-path problem, worksheet system design, and your best project story.
Snowflake frontend prep should end with one clear story: "I can build the UI, explain the JavaScript, and reason about data-tool behavior in the browser." That means the best practice set is not 100 random React questions. It is a smaller set of Snowflake-shaped drills: interactive grids, React hooks, event emitter, debounce, result tables, autocomplete, graph traversal, and a Snowsight worksheet design.

Discord frontend interview questions usually test whether you can build and explain realtime product UI: chat, message lists, editable components, async events, large-list rendering, and JavaScript primitives that keep a client responsive. Prepare for React, TypeScript, browser fundamentals, frontend system design, and behavioral rounds about collaboration and product judgment.
For the company-guide view, use the Discord Front End Interview Guide alongside this article.
Use each question as a working session: solve the baseline prompt, then explain what would change for larger data, unreliable connections, accessibility requirements, slow devices, and product follow-ups.
Discord is a realtime communication product, so frontend interviews do not stop at "can you render a component?" A useful Discord client has to handle fast-changing events, long chat histories, thousands of members and channels, typing indicators, presence, reactions, attachments, markdown, voice/video state, unreliable connections, and heavy user-generated content.
| Area | What to practice | Why it matters at Discord |
|---|---|---|
| React implementation | Chat UI, editable cells, spreadsheet grids, message actions, mention pickers, virtualized lists | These map to message views, channel lists, member lists, modals, and internal tooling. |
| JavaScript | Event emitter, debounce, throttle, DOM traversal, async events, timers, stale updates | Realtime clients depend on event routing, cleanup, and input pacing. |
| Realtime UI | Optimistic messages, retries, typing indicators, reconnect behavior, duplicate event handling | A chat client must feel fast while still correcting itself when the network disagrees. |
| Data modeling | Messages, channels, users, cell formulas, derived values, normalized state | The interview often deepens through follow-ups that punish copied or duplicated state. |
| Frontend system design | Discord-style chat, messaging systems, presence, autocomplete, server recommendations | The strongest answers connect UI behavior to API contracts and transport choices. |
| Behavioral | Ownership, ambiguity, conflict, feedback, product usage, cross-functional work | Discord's loop includes values and cross-functional signal, not only coding. |
The best preparation is to build Discord-shaped interfaces, then explain how they behave when data grows, events arrive out of order, or the user loses connection.
The exact process changes by team and level, but prepare for this shape:
| Stage | What to expect | Prep note |
|---|---|---|
| Recruiter screen | Role fit, timeline, compensation, location, and interview logistics | Ask whether the technical round is React, JavaScript, DSA, or fullstack. |
| Hiring manager screen | Background, project depth, Discord interest, and team alignment | Prepare a concise story about why Discord and what product areas you use. |
| Technical screen | One practical coding exercise, often in your own editor or a live UI | Build a working baseline before optimizing or abstracting. |
| Final interview loop | Coding, architecture, project discussion, values, and cross-function | Practice narrating tradeoffs, not only writing code. |
| Senior/project deep dive | A project retrospective with architecture, alternatives, and impact | Pick one project where you owned meaningful frontend decisions. |
Discord's own interview preparation guide says technical interviews are based on real work and can include coding, architecture, values, attitude, and specialty sessions. That is the right way to calibrate your prep: practice building and explaining product features, not memorizing trivia.
Treat your recruiter's instructions as the source of truth. Your loop may be frontend-only, backend-aware, or broader architecture-heavy depending on the role.
Use these as practice prompts, not a guaranteed question bank. They cover the recurring Discord-style patterns: realtime UI, chat state, editable components, JavaScript primitives, and frontend system design.
| Discord question or task | What to practice |
|---|---|
| Build a Discord-like chat interface with a message composer, message list, and basic message actions. | React state, list rendering, controlled input, optimistic updates |
| Extend a chat UI so users can edit, delete, retry, and display message-send status. | Message identity, derived status, rollback behavior, error states |
| Add typing indicators and reconnect behavior to a chat view. | Timers, event expiry, WebSocket lifecycle, stale event cleanup |
| Build an editable React cell like a spreadsheet cell. | Display/edit mode, focus management, keyboard behavior, controlled inputs |
| Build a spreadsheet-style grid where users can edit cells and enter simple formulas. | Cell data model, dependency tracking, recalculation, circular references |
| Implement a debounce utility and explain where it would be useful in Discord. | Closures, timers, cancellation, search and typing-indicator pacing |
| Implement an event emitter with subscribe, unsubscribe, emit, and once behavior. | Listener storage, cleanup, re-entrancy, event payloads |
| Write a function that finds elements by class name without using the browser's native helper. | DOM traversal, recursion or iteration, matching logic |
| Build a simple socket-based chat service or messaging layer. | Message delivery, connection lifecycle, fan-out, ordering |
| Design a user messaging system that supports sending, receiving, editing, and displaying messages reliably. | Client-server contracts, WebSocket events, idempotency, retry behavior |
| Solve a word-search, substring, or trie-style lookup problem. | Strings, prefix search, tries, complexity analysis |
| Design a Discord-like chat client from the frontend perspective. | Data model, transport events, normalized state, rendering, offline behavior |
| Design realtime recommendations for users joining new servers or communities. | Product systems, ranking signals, frontend data needs, empty states |
| Walk through a project you led and explain the technical tradeoffs you made. | Project depth, ownership, alternatives, results |
| Explain why you want to work at Discord and what part of the product interests you. | Product usage, motivation, user empathy |
| Describe a time a project failed, requirements changed, or feedback forced you to adjust your technical direction. | Reflection, collaboration, judgment |
Notice the repeated theme: practical UI plus event-driven state. Even a simple utility question can lead back to Discord's product. Debounce connects to search and typing indicators. Event emitters connect to gateway events. Editable grids connect to internal tools and data-heavy UI. Chat prompts connect to the core product.
Start with a small data model before writing components:
type MessageStatus = 'sending' | 'sent' | 'failed';type Message = {id: string;channelId: string;authorId: string;content: string;createdAt: number;editedAt?: number;status: MessageStatus;};
Use stable message IDs, not array indexes. Keep the input draft separate from the message list. Derive visible state from messages rather than copying the same information into several local states.
A good baseline includes:
Then discuss follow-ups:
Practice the base implementation with Data Table for stable row identity and Autocomplete for async input behavior, then adapt those habits to chat.
The editable-cell prompt looks small, but it tests whether you can separate UI mode, input state, saved state, focus, and keyboard behavior.
Start with one cell:
One possible component contract:
type EditableCellProps = {id: string;value: string;onCommit: (id: string, nextValue: string) => void;onNavigate?: (id: string,direction: 'up' | 'down' | 'left' | 'right',) => void;};
Keep transient draft text inside the cell while editing. Keep committed cell values in the parent grid. That gives the parent one source of truth and still lets each cell manage local focus and selection behavior.
For a spreadsheet grid, explain the deeper model:
type Cell = {id: string; // "A1", "B2", etc.rawValue: string; // typed text, including formulascomputedValue: string | number | null;error?: 'invalid-formula' | 'circular-reference';};
Important follow-ups:
A1 and B2 into dependenciesDo not overbuild the parser in the first 20 minutes. Ship editable cells first, then add formulas in layers.
An event emitter is a small problem with many useful edge cases. It maps well to Discord because realtime clients receive events from one transport and distribute updates to many pieces of UI.
Start with the API:
type Listener<T = unknown> = (payload: T) => void;class EventEmitter {private listeners = new Map<string, Set<Listener>>();on(eventName: string, listener: Listener) {if (!this.listeners.has(eventName)) {this.listeners.set(eventName, new Set());}this.listeners.get(eventName)!.add(listener);return () => this.off(eventName, listener);}off(eventName: string, listener: Listener) {this.listeners.get(eventName)?.delete(listener);}emit(eventName: string, payload?: unknown) {const listeners = this.listeners.get(eventName);if (listeners == null) {return;}for (const listener of Array.from(listeners)) {listener(payload);}}once(eventName: string, listener: Listener) {const unsubscribe = this.on(eventName, (payload) => {unsubscribe();listener(payload);});return unsubscribe;}}
Then discuss edge cases:
emit is running?off clean up empty event sets?on return an unsubscribe function?For interview code, Map<string, Set<Listener>> is a clean default. It makes listener removal easier than an array and prevents accidental duplicates.
Debounce delays a function until calls stop for a given amount of time. In Discord-shaped UI, it appears in search, mention pickers, typing indicators, resize handlers, and scroll-linked work.
Implement the baseline:
function debounce(fn, delay) {let timerId;return function debounced(...args) {clearTimeout(timerId);timerId = setTimeout(() => {fn.apply(this, args);}, delay);};}
Then explain product usage:
Mention the limits too. Do not debounce the actual input state update, because typing should feel immediate. Debounce the side effect: network request, analytics call, or expensive calculation.
If asked for a fuller implementation, add cancel, flush, leading-edge execution, and return-value behavior. Name those as follow-ups rather than trying to cram every feature into the first version.
For a frontend system design round, do not start by drawing components. Start by clarifying scope:
Then structure the answer around the browser contract. Discord's engineering post on reducing WebSocket traffic by 40% is useful context here because it explains the gateway, payloads such as MESSAGE_CREATE and TYPING_START, passive sessions, and why client bandwidth matters for realtime UI.
| Design area | What to cover |
|---|---|
| Data model | Messages, users, channels, servers, members, reactions, typing state, presence, drafts |
| Transport | WebSocket events for realtime updates, HTTP for history, reconnect and resume behavior |
| State management | Normalized entities, per-channel message IDs, local drafts, optimistic sends, subscription cleanup |
| Rendering | Virtualized message list, scroll anchoring, dynamic row heights, lazy embeds, markdown rendering |
| Reliability | Retry failed sends, dedupe duplicate events, ignore stale events, handle reconnect gaps |
| Performance | Avoid global rerenders, memoize expensive message rows, split heavy features, clean up listeners |
| Accessibility | Keyboard navigation, focus recovery, readable controls, announcement of new messages when needed |
| Testing | Message send, retry, edit, delete, duplicate event, reconnect, scroll preservation |
One useful state shape:
type ChatState = {messagesById: Record<string, Message>;messageIdsByChannelId: Record<string, Array<string>>;draftsByChannelId: Record<string, string>;typingUserIdsByChannelId: Record<string, Array<string>>;};
From there, describe event handling:
MESSAGE_CREATE: insert message if it is not already presentMESSAGE_UPDATE: patch the existing message by IDMESSAGE_DELETE: remove or tombstone the messageTYPING_START: show typing state with an expiry timerPRESENCE_UPDATE: update visible presence without rerendering every unrelated messageRECONNECT: resume from the last known event sequence when possible; otherwise refetch the active channelThe key tradeoff: the UI should feel instant, but the server remains the source of truth for message identity, ordering, permissions, moderation, and delivery state. For performance follow-ups, Discord's post on maintaining performance while adding features is a good model for discussing code splitting, lazy loading, retrying failed chunks, and keeping a large client fast as features accumulate.
Read these after building the baseline chat UI. They give you concrete language for system design, performance, accessibility, and product-specific follow-ups.
| Official Discord resource | What to use it for |
|---|---|
| How to prepare for your Discord interview | Round structure, technical interview expectations, architecture interviews, values, and cross-functional conversations. |
| How Discord Reduced WebSocket Traffic by 40% | Gateway events, compressed payloads, passive sessions, bandwidth tradeoffs, and realtime client design. |
| How Discord Maintains Performance While Adding Features | Code splitting, lazy loading, retry behavior, bundle size, route-level chunks, and frontend performance tradeoffs. |
| How Discord Implemented App-Wide Keyboard Navigation | Accessibility, focus management, keyboard navigation, component-system constraints, and custom focus rings. |
| How Discord achieves native iOS performance with React Native | React Native performance, store dispatch costs, message parsing, virtualization, and mobile client tradeoffs. |
| How Discord Handles Two and Half Million Concurrent Voice Users using WebRTC | Voice/video architecture, WebRTC constraints, browser vs native client behavior, and media performance. |
Review React through Discord-style examples, not isolated definitions.
| Topic | Discord-shaped way to practice |
|---|---|
useState and useRef | Manage a draft message, input focus, pending scroll action, and latest request ID. |
useEffect | Subscribe to WebSocket events and clean up listeners when the channel changes. |
useMemo | Derive visible message IDs from normalized state when the transform is expensive enough. |
useCallback | Stabilize callbacks only when child memoization or subscription identity actually needs it. |
| Controlled inputs | Build message composer, search box, and editable spreadsheet cell. |
| Component composition | Split message row, composer, channel header, reaction picker, and typing indicator sensibly. |
| Context | Use it for stable app-level dependencies, not every message update. |
| Error boundaries | Keep one broken embed or markdown block from breaking the whole chat view. |
| Performance profiling | Find unnecessary rerenders in message rows, member lists, or virtualized lists. |
| Accessibility | Make modals, menus, keyboard navigation, and message actions reachable without a mouse. |
Avoid generic answers like "use memoization for performance." Explain what rerenders, why it matters, and which optimization matches the bottleneck.
Discord frontend prep should include JavaScript fundamentals because many realtime UI bugs come from closures, timers, async work, and event cleanup.
Practice:
setTimeout, setInterval, and cleanupTie each topic to a product bug. For example, a stale closure can send a message to the previously selected channel. A missing cleanup can leave an old channel subscription active. A naive DOM traversal can miss nested elements. A search request that returns late can overwrite newer results.
Useful GreatFrontEnd practice:
Do not treat the behavioral round as a formality. Discord interviews include signals around values, attitude, product usage, collaboration, and how you respond to feedback.
Prepare stories for:
For each story, prepare the user problem, your role, the constraints, alternatives considered, the decision, the result, and what you would change now. Specific stories beat polished generalities.
Also use the product before the interview. Create or manage a server, try roles and permissions, use channels, threads, voice, screen share, reactions, search, notifications, and moderation settings. Product familiarity makes system design and behavioral answers much more concrete.
Use this as a Discord-specific checklist. The exact order can change based on your timeline, but the coverage should stay the same: chat UI, spreadsheet-style components, realtime primitives, frontend system design, and behavioral depth.
| Prep area | What to do | Discord-specific angle |
|---|---|---|
| Chat UI implementation | Build a React and TypeScript chat app with composer, message list, edit, delete, retry, typing state, and older-message loading. | Practice optimistic sends, duplicate event reconciliation, scroll preservation, and channel switching. |
| Spreadsheet-style coding | Build editable cells, keyboard navigation, formula references, dependency recalculation, and circular-reference handling. | This tests state modeling and follow-up resilience, not only UI rendering. |
| JavaScript primitives | Implement debounce, throttle, event emitter, DOM traversal, and a small rate limiter from scratch. | Connect each primitive to search, typing indicators, gateway events, and message-send pacing. |
| Realtime system design | Design Discord chat, typing indicators, presence, message history, and reconnect behavior from the frontend perspective. | Cover WebSocket events, HTTP history, normalized state, optimistic UI, offline behavior, and cleanup. |
| Performance practice | Add virtualization to message/member/channel lists and profile unnecessary rerenders. | Discord-like UIs are list-heavy and can become slow without stable identity and careful subscriptions. |
| Behavioral and product prep | Prepare project stories and use Discord deeply enough to discuss specific flows. | Values and cross-functional rounds reward evidence, not generic interest in communication products. |
| Mock loop | Run one React mock, one JavaScript utility mock, one system design mock, and one project deep dive. | Fix weak answers after each mock instead of collecting more random questions. |
Prepare for Discord by building the interfaces Discord actually depends on: chat, editable grids, realtime event handlers, long lists, search, and presence. Memorizing React definitions is not enough if your code falls apart when messages arrive out of order or the user changes channels mid-request.
In coding rounds, ship a working baseline first. In system design, explain the client-server contract and the failure modes. In behavioral rounds, bring concrete product usage and project stories with real tradeoffs.
If you can build a chat UI, reason through editable grid state, implement core JavaScript utilities, and explain realtime frontend architecture clearly, you are preparing for the right interview shape.

PayPal frontend interview questions usually test JavaScript, React, DSA, and payment-system reasoning in the same loop. Prepare for practical UI coding, React internals, browser architecture, and checkout-specific design topics such as security, retries, idempotency, iframes, currency handling, and accessible payment flows.
For the company-guide view, use the PayPal Front End Interview Guide alongside this article.
Use each question as a working session: solve the baseline prompt, then explain what changes for payment reliability, accessibility, security, multi-currency handling, slow networks, and backend ownership.
PayPal is a payments company, so frontend interviews do not stop at rendering a button. A checkout UI has to handle money, risk, privacy, failed networks, user trust, third-party merchant pages, and strict accessibility expectations. That product context explains the mix of React, JavaScript utilities, DSA, system design, and backend-aware architecture.
| Area | What to practice | Why it matters at PayPal |
|---|---|---|
| React implementation | Cart UI, file explorer, typeahead, form validation, currency converter, checkout widgets | These map to checkout, merchant dashboards, wallet flows, and SDK examples. |
| JavaScript | Async classes, reduce, flatten, deep copy, closures, promises, event loop, debounce, throttle | These show whether you can handle utility code and async data flow clearly. |
| DSA | Strings, arrays, stacks, DP, graphs, binary search, LRU cache | PayPal coding screens can still include LeetCode-medium style problems. |
| Browser fundamentals | DOM vs BOM, cookies, security, CDN, loading, accessibility, performance | Checkout and dashboard work requires browser-to-backend reasoning. |
| Frontend system design | Checkout SDK, payment gateway, transaction dashboard, real-time fraud queue | Payments UI sits across frontend, API, ledger, risk, and operations systems. |
| Behavioral | Ownership, conflict, mentoring, decision-making, project depth | Senior and staff loops include manager or bar-raiser discussions. |
PayPal's official careers guidance says recruiters usually reach out by phone, interviews are virtual on Microsoft Teams, and applicants should study the job description, company values, and Leadership Principles. It also points applicants to a prep video series.
Expect a process shaped roughly like this:
| Stage | What to expect | Prep note |
|---|---|---|
| Recruiter screen | Background, role fit, location, timeline, interview format | Ask whether the first technical round is HackerRank, Karat, DSA, React, or role-specialization. |
| Online assessment | HackerRank-style React, JavaScript, and DSA tasks | Be ready for 60-150 minutes depending on the assessment. |
| Technical coding | DSA, JavaScript utilities, React machine coding, code review | Use JavaScript or TypeScript unless recruiter says any language is acceptable. |
| Role specialization | React internals, hooks, state, fetch, tests, frontend architecture | Expect deeper follow-ups for SDE-2, senior, and staff roles. |
| System design | Payment gateway, checkout flow, web architecture from browser to backend | Explain both client behavior and backend contracts. |
| Behavioral / bar raiser | Project depth, conflict, mentoring, tradeoffs, ownership | Prepare varied STAR stories, especially around customer impact and reliability. |
Use these as practice prompts, not a guaranteed question bank. For each one, timebox a working baseline first, then talk through tradeoffs, edge cases, accessibility, performance, and tests.
| PayPal question or task to practice | What to practice |
|---|---|
Build a small async data wrapper class with get and create-style methods. It should return stored values, reject missing reads, and reject duplicate writes. | Async JavaScript, classes, promise sequencing, error handling |
| Build a React cart where users can change quantities, remove items, and see totals update immediately. | Cart state, derived totals, immutable updates, testable UI |
| Solve a dynamic-programming triangle path problem, implement a stack with constant-time minimum lookup, and explain topological sorting. | Dynamic programming, stack design, graph ordering |
| Design a payment gateway and explain the transaction flow, consistency model, ledger behavior, retries, and failure handling. | PayPal orders, payment sessions, idempotency, ledger, webhooks, retries |
| Explain React reconciliation and sketch a simplified algorithm for comparing an old UI tree with a new one. | React rendering internals, tree comparison, keyed lists, rerender causes |
| Build a React currency converter with controlled inputs and recalculated output. | Controlled inputs, derived state, Intl.NumberFormat, precision, async rates |
| Given a string, remove the smallest possible contiguous section so the remaining characters satisfy a uniqueness constraint. | Sliding window, character counts, edge cases |
| Write a deep-copy function, then adapt it to apply one extra transformation to arrays during copying. | Recursion, arrays vs objects, reference safety, cycles if asked |
| Complete missing React component logic, then implement an LRU Cache. | Component state, DSA data structures, Map plus linked-list reasoning |
| Walk through a web application from DNS and CDN to browser cookies, security, backend storage, cache, and real-time communication choices. | End-to-end web architecture, network basics, API design, storage choices |
| Build a React form with validation, visible error states, and disabled submission while invalid. | Forms, validation state, error messages, disabled submission, accessibility |
| Build a simple page in a frontend framework and pair it with a small object-oriented backend model. | Full-stack UI basics, component composition, simple backend model |
| Solve a paint-fill style grid traversal problem. | Grid traversal, DFS/BFS, visited checks |
| Explain ES6 features through examples from your own project work. | Project-based JavaScript, modules, destructuring, promises, classes, iterators |
Explain why a team might choose React, describe page performance improvements, then implement Array.prototype.reduce behavior in vanilla JavaScript. | React tradeoffs, rendering performance, array methods, polyfills |
| Build a typeahead that filters a list as the user types and includes a clear-input control. | Typeahead logic, clear control, accessible combobox basics, browser terms |
| Find all anagrams of a pattern inside a string, then implement array flattening with configurable depth. | Sliding window, recursive or iterative flatten, edge cases |
| Build a nested file explorer in React with expand/collapse and basic create, rename, and delete actions. | Recursive data, local edit state, immutable tree updates |
| Build a small shopping app with routes, navigation, cart add/remove behavior, duplicate prevention, total calculation, and test-friendly markup. | Machine coding, routing, shared state without Redux, test-driven details |
| For a staff-level frontend role, answer JavaScript, TypeScript, frontend architecture, React, component library, system design, and behavioral questions. | Staff-level frontend architecture, LLD, HLD, component APIs, leadership |
| Build a React task using hooks, state, data fetching, error handling, and a small test strategy; then discuss REST APIs, pagination, caching, auth, and indexing. | Full-stack frontend specialization, API-backed React, backend awareness |
| Solve short array-manipulation tasks and simple calculation problems under time pressure. | Arrays, loops, basic transformation, correctness under time |
The cart prompt is common because it tests a payment-adjacent workflow without requiring a real payment API. Start with the smallest data model that can survive follow-ups:
type CartItem = {id: string;name: string;priceCents: number;quantity: number;};
Use item IDs as identity. Derive subtotal, itemCount, and disabled states from the cart array instead of duplicating them in separate state variables. That makes quantity changes, removal, and duplicate prevention easier to reason about.
A complete answer covers:
Intl.NumberFormat. PayPal's currency code reference is useful here because some supported currencies do not allow decimal amounts.Practice this with Data Table for tabular rendering and Contact Form for validation habits, then adapt the state model to a cart.
Fetcher classThe Fetcher prompt checks whether you can wrap an async dependency without losing error semantics. Do not jump straight to try/catch; first name the contract:
get(id) resolves with the stored object when it existsget(id) rejects or throws when the ID does not existpost(id, value) creates a value for an unused IDpost(id, value) rejects or throws when the ID already existsDB.read and DB.create are asyncOne possible shape:
class Fetcher {constructor(db) {this.db = db;}async get(id) {const value = await this.db.read(id);if (value == null) {throw new Error('ID not found');}return value;}async post(id, value) {const existing = await this.db.read(id);if (existing != null) {throw new Error('ID already exists');}return this.db.create(id, value);}}
Then discuss race conditions. If two post(7, value) calls run at the same time, both can read "empty" before either creates. In an interview, say that the real fix belongs in the database or API layer through a uniqueness constraint, transaction, or atomic create. The frontend wrapper can expose errors clearly, but it cannot guarantee cross-client uniqueness by itself.
PayPal uses country pickers, currency selectors, merchant search, transaction filters, and support flows where typeahead behavior matters. A baseline implementation is not enough if it overwrites newer results with older responses.
Mention these requirements before coding:
AbortControllerFor system design depth, practice Autocomplete. For UI implementation speed, practice Users Database.
React internals questions are easier when you connect the concept to a bug or design decision. Keep the answer practical:
If asked to pseudocode diffing, use a simplified tree compare:
function diff(previousNode, nextNode) {if (previousNode == null) return { type: 'insert', node: nextNode };if (nextNode == null) return { type: 'remove', node: previousNode };if (previousNode.type !== nextNode.type) {return { type: 'replace', node: nextNode };}return {type: 'update',props: diffProps(previousNode.props, nextNode.props),children: diffChildren(previousNode.children, nextNode.children),};}
Then add the caveat: real React reconciliation is much more complex, but the interview wants to see identity, tree comparison, and keys.
This is the round where generic frontend prep runs out. A payment gateway design asks you to connect browser behavior, SDK boundaries, server APIs, risk checks, and ledger systems.
Start from the user action:
Payments need extra care:
PayPal's own SDK and integration materials are useful background here. Review the JavaScript SDK, React SDK v6, hosted Card Fields model, Orders API, and sandboxed iframe integration pattern before a system design round. Keep the concepts separate: PayPal wallet flows use a PayPal payment session, Card Fields keep raw card data inside PayPal-hosted iframes, and the sandboxed iframe guide shows an advanced wrapper pattern where the merchant page and isolated payment frame communicate with postMessage. For duplicate-submit and retry discussions, mention PayPal's PayPal-Request-Id idempotency header.
PayPal interview prep clusters around a few repeatable patterns.
| Topic | Practice questions |
|---|---|
| Arrays and strings | Find all anagrams, longest substring without repeating characters, remove minimum substring for unique characters |
| Stacks and caches | Min Stack, LRU Cache |
| DP and graphs | Triangle DP, Paint Bucket Fill, topological sort, grid BFS |
| JavaScript utilities | reduce, flatten with depth, deep copy, debounce, throttle |
| Async JavaScript | Fetcher class, fetch with loading/error states, retries, stale responses |
| React machine coding | Cart, shopping app, file explorer, form validation, typeahead, currency converter |
| Browser and web | DOM vs BOM, cookies, CDN, DNS, WebSockets, long polling, accessibility |
Useful GreatFrontEnd practice:
Use PayPal's own docs to make payment-system answers concrete instead of generic:
PayPal-Request-Id, retries, duplicate prevention, and unclear response handling.postMessage.Use this as a PayPal-specific checklist. The exact order can change based on your timeline, but the coverage should stay the same: JavaScript, React machine coding, payments architecture, and behavioral depth.
| Prep area | What to do | PayPal-specific angle |
|---|---|---|
| JavaScript and DSA | Solve Min Stack, LRU Cache, flatten, reduce, deep clone, anagrams, one sliding-window problem, and one DP problem. | Keep utility implementation and LeetCode-medium style coding both warm. |
| React machine coding | Build cart, file explorer, form validation, typeahead, shopping app, and currency converter in timed 45-60 minute sessions. | Narrate state ownership, derived totals, validation, accessibility, loading states, and test selectors. |
| Payments architecture | Design checkout SDK, payment gateway, merchant transaction table, and fraud-review queue. | Include PayPal orders, server-side create/capture, idempotency, Card Fields, iframe isolation, webhooks, and reconciliation. |
| Browser and web architecture | Review DNS, CDN, cookies, cache, WebSockets, long polling, security, and storage choices. | Explain how a checkout or merchant dashboard moves from browser interaction to backend services. |
| Behavioral and project depth | Prepare one deep frontend project story and 6-8 STAR examples across conflict, ownership, reliability, mentoring, and tradeoffs. | Choose examples involving risk, customer trust, accessibility, performance, or security-sensitive UI. |
| Mock loop | Run one DSA mock, one React mock, one system design mock, and one behavioral project deep dive. | After each mock, fix the weakest answer rather than adding more random questions. |
If you have only 72 hours, prioritize cart UI, typeahead, reduce, flatten, LRU Cache, one payment gateway design, and your best project story.
PayPal's careers guidance asks applicants to prepare detailed examples and vary stories across roles. For frontend engineers, choose stories that involve product risk, reliability, accessibility, security-sensitive UI, or cross-team execution.
Prepare stories for:
For each story, include the user problem, the technical constraint, the options considered, the decision, the result, and what you would do differently now.
PayPal frontend prep should end with one clear story: "I can build the UI, explain the JavaScript, and reason about what happens when money moves." That means the best practice set is not 100 random React questions. It is a smaller set of payment-shaped drills: cart, form validation, typeahead, currency converter, file explorer, Fetcher, flatten, deep clone, LRU Cache, and a checkout or payment-gateway system design.

Databricks frontend interview questions usually test data-heavy UI engineering: async search, API-backed components, request race conditions, large result tables, dashboards, collaboration, and system design. Your prep should also include algorithm and machine-coding rounds, so do not prepare only for React trivia.
The fastest way to prepare is to combine three sources of truth:
For the company-guide view, use the Databricks Front End Interview Guide alongside this article.
Databricks is not a generic consumer app company. The frontend work sits around notebooks, SQL editors, schema browsers, dashboards, workflows, model tools, governance UI, and AI-assisted data products. That changes the interview signal.
| Area | What to practice | Why it matters for Databricks |
|---|---|---|
| Async UI | Typeahead, cancellation, retries, stale response handling | Search and autocomplete appear in editors, catalogs, dashboards, and notebooks. |
| Data rendering | Tables, filters, pagination, virtualization, charts | Query results and dashboards can grow past what a simple component can handle. |
| JavaScript fundamentals | Closures, event loop, Set, Map, iterators, timers | JavaScript and data-structure follow-ups can appear in frontend loops. |
| System design | Collaborative editing, dashboards, autocomplete, real-time updates | Official frontend systems prep calls out client-server interfaces, API design, state, and sync. |
| Product implementation | Design-to-code constraints, empty states, loading states, error states | Databricks' official frontend code round expects a browser app that fetches and displays remote data. |
| Cross-functional judgment | Project depth, design collaboration, tradeoffs, customer impact | Official prep says cross-functional rounds dig into projects, decisions, and collaboration. |
The shape is closer to "build and explain a data product UI" than "recite React hooks." React can be useful, but Databricks' official prep says the frontend code round supports vanilla JS/DOM, React, Angular, Vue, and Svelte, so the core bar is web engineering, not one framework.
Databricks' public interview prep page describes seven hiring stages: identifying opportunities, applying online, connecting with Talent Acquisition, skill assessments, interviewing, reference checks, and decision/offer. It also says interviews usually run over Google Meet.
For front-end/full-stack software engineers, the April 2025 Databricks engineering prep PDF lists these interview types:
| Round | What Databricks says it evaluates | How to prepare |
|---|---|---|
| Front-End Code | Building a browser app that fetches and displays remote data, with async operations, efficient fetching, and UI state management | Practice typeahead, tables, search filters, loading/error/empty states, and request ordering bugs. |
| Front-End Systems | Client-server interfaces, API design, state management, data synchronization, and hands-on coding inside an open-ended design prompt | Design autocomplete, collaborative editing, dashboards, and large result panes, then implement one part. |
| Product Design | Turning Figma-like requirements into working implementation while discussing constraints with a designer | Explain layout, accessibility, state, edge cases, responsive behavior, and tradeoffs. |
| Front-End Infrastructure | Large refactors, migrations, testing infrastructure, build systems, performance, and code quality | Prepare examples from design systems, monorepos, migrations, CI, test reliability, and performance work. |
| Cross-Functional | Career path, motivation, major projects, decisions, collaboration, and leadership | Prepare project deep dives with technical detail and outcomes. |
Some loops also include algorithmic coding or backend/system-design rounds, especially for full-stack, senior, and general software engineering roles. Ask your recruiter which rounds are in your loop and whether the coding round is frontend, algorithmic, or both.
Treat these as practice prompts, not a guaranteed question bank. They are useful because they cover recurring Databricks-style patterns: async UI, system design depth, data structures, graph traversal, and data-platform products.
| Question | Round / role signal | What to practice |
|---|---|---|
| Build or design an autocomplete widget that fetches suggestions, debounces input, handles slow requests, recovers from errors, and prevents older responses from overwriting newer results. | Frontend phone screen | Request IDs, AbortController, cache, retries, loading/error states, API shape |
| Implement typeahead logic in CoderPad from a starter state object and input handler. Add caching, cache expiry, retry behavior, and race-condition handling across success, loading, and error states. | Senior full-stack / frontend systems | Business logic first, race handling, cache semantics, retry limits |
| Design a shared playlist where multiple users can add, remove, and reorder items at the same time. | Frontend systems | Collaboration model, WebSocket events, conflict handling, optimistic updates |
| Explain JavaScript closures, then render a nested comment list with CSS and vanilla JavaScript using an iterative approach. | Frontend coding | Closures, iterative tree traversal, DOM rendering, CSS structure |
| Build a dashboard view that reads live API data and explain the component model, data refresh strategy, routing, event handling, and HTTP API choices. | Pair programming / full-stack | API-backed dashboards, polling/SSE/WebSocket, state model, route boundaries |
| Solve a grid path-counting problem and compare a brute-force search with the optimized approach. | Problem solving | Backtracking, visited-state management, complexity analysis |
| Design an ad-click aggregation system with ingestion, aggregation windows, indexing, storage choice, scale, fault tolerance, and monitoring. | System design | Event ingestion, aggregation windows, storage, monitoring |
| Implement a configurable TicTacToe class for an MxN board with move handling, turn state, board updates, and win detection. | Coding | OOP state design, row/column counters, extensibility |
| Convert a route-planning problem into a graph problem, find a valid path, then extend the solution to account for different travel costs. | Algorithm | BFS, weighted search, path-cost tracking |
| Design an AutoML-style platform that accepts training requests, schedules jobs, stores experiments, tracks model versions, and isolates users. | System design | Job orchestration, model metadata, tenant isolation, resource control |
| Build a custom set that supports add, remove, contains, and iteration where an iterator sees a snapshot while the live set can still change. | L4 phone screen | Iterator snapshots, mutation semantics, Set/linked-list choices |
| Given a city grid with blocked cells and several transportation modes, choose the fastest route from source to destination and use cost as the tie-breaker. | Technical phone screen | BFS per mode, tie-breaking, grid traversal |
Implement an in-memory key-value map with put, get, and rolling five-minute call-rate metrics for reads and writes. | Machine coding | Sliding windows, timestamp buckets, concurrency discussion |
| Implement firewall-style allow/deny rules for individual IP addresses and CIDR ranges. | L5 technical phone screen | IP parsing, CIDR masks, rule precedence |
| Randomly connect several already-connected graphs into one graph so each possible ordering is equally likely. | Technical phone screen | Graph representation, random permutations, correctness of probabilities |
| Prepare for new-grad style loops that combine architecture discussion, DSA rounds, and behavioral interviews. | New grad SWE | DSA, system design communication, behavioral project stories |
| Prepare for senior data-platform rounds that can move from frontend/product discussion into distributed logs, analytics pipelines, and Spark-adjacent systems. | Senior SWE / data systems team | Data-platform system design, distributed systems, project depth |
The strongest frontend pattern is autocomplete/typeahead. It matches Databricks' official Front-End Code and Front-End Systems expectations: fetch remote data, manage async state, design client-server behavior, and implement business logic in CoderPad.
Start with the behavior before code:
For a CoderPad business-logic version, a small state machine is often clearer than a large component:
const cache = new Map();let activeRequestId = 0;let debounceTimer = null;const state = {query: '',status: 'idle', // idle | loading | success | empty | errorresults: [],error: null,};function setState(nextState) {Object.assign(state, nextState);}function isFreshCacheEntry(entry, now = Date.now()) {return entry != null && entry.expiresAt > now;}function handleInputChange(query) {setState({ query, error: null });clearTimeout(debounceTimer);if (query.trim() === '') {setState({ status: 'idle', results: [] });return;}debounceTimer = setTimeout(() => {fetchSuggestions(query);}, 250);}async function fetchSuggestions(query) {const cached = cache.get(query);if (isFreshCacheEntry(cached)) {setState({status: cached.results.length === 0 ? 'empty' : 'success',results: cached.results,});return;}const requestId = ++activeRequestId;setState({ status: 'loading' });try {const results = await fetchResults(query);if (requestId !== activeRequestId) {return;}cache.set(query, {results,expiresAt: Date.now() + 60_000,});setState({status: results.length === 0 ? 'empty' : 'success',results,});} catch (error) {if (requestId === activeRequestId) {setState({ status: 'error', error });}}}
Name the race condition explicitly. If the user types d, then da, then dat, the d request can finish last and overwrite newer results unless the app ignores stale responses or aborts old requests.
Good follow-up answers:
AbortController when the request layer supports it; still keep a request ID check because not every data source supports cancellation.Practice the implementation with Users Database, then practice the architecture with Autocomplete.
Use two tracks: frontend coding and general coding.
For frontend coding, practice:
For JavaScript and machine coding, practice:
For each coding problem, explain:
This is especially important for Databricks because many prompts start simple and then deepen through follow-ups. A working baseline is not enough if the next requirement forces a rewrite.
Databricks' official frontend systems description is unusually specific: client-server interfaces, API design, state management, data synchronization, and hands-on coding. That means a good answer should move between architecture and implementation.
Practice these Databricks-shaped designs:
| Design prompt | What to cover |
|---|---|
| Autocomplete across notebooks, tables, dashboards, and models | Query normalization, ranking, permissions, caching, cancellation, keyboard behavior, no-result states |
| Collaborative notebook editor | Cell model, output streaming, presence, conflicts, undo/redo, permissions, revision history |
| SQL editor result pane | Query lifecycle, cancellation, result limits, streaming/partial results, table virtualization, chart handoff |
| AI/BI dashboard | Tile lifecycle, cross-filtering, browser cache, parameterized queries, first paint, cache hit rate, concurrency |
| Workflow DAG UI | Graph layout, viewport virtualization, polling vs push, run history, status transitions, error recovery |
| Genie-style natural-language analytics | Prompt context, generated SQL review, result rendering, feedback loop, permission boundaries |
Use Databricks docs to make the answer concrete:
In a frontend system design round, spend less time drawing cloud boxes and more time on the browser contract:
Use the Front End System Design Playbook to structure the answer, then adapt the system to a data-product UI instead of a social feed or ecommerce page.
Databricks' interview prep page says behavioral interviews ask about real examples from past experience, problem solving, decision-making, learning, collaboration, and handling challenges. Prepare for project deep dives, design choices, collaboration, impact, conflict, and motivation for Databricks.
Prepare stories for:
For each story, write down:
If you have worked on editors, dashboards, data grids, query tools, charts, workflow builders, AI assistants, or internal platforms, use those examples first. They map cleanly to Databricks product work.
This plan is ordered by payoff, not by calendar week.
Before the interview, you should be able to:
Databricks frontend preparation should feel like practicing for a data product team: browser fundamentals, async state, data rendering, system design, and project judgment all matter. If you can build the typeahead cleanly, explain stale-response handling, design a large-result dashboard, and still solve graph-style coding prompts, you are covering the highest-value prep areas.

Netflix React interview questions are usually less about memorizing framework trivia and more about building practical UI, explaining state and data flow, handling performance constraints, and showing sound product judgment. Expect the exact loop to vary by team, but prepare for React, JavaScript, browser fundamentals, frontend system design, and culture/project discussion.
For deeper company-specific prep, use the Netflix Front End Interview Guide alongside this article.
Use each question as a working session: solve the baseline prompt, then explain what would change for larger data, weaker devices, unreliable APIs, accessibility requirements, and product experimentation.
Netflix frontend work spans streaming UI, discovery rows, playback controls, studio tooling, data dashboards, ads, games, experimentation, and internal workflows. The interview is not only checking whether you know React; it is also testing whether you can build a usable interface under constraints and explain the decisions behind it.
| Area | What to practice | What a complete answer shows |
|---|---|---|
| React implementation | Tables, lists, forms, carousels, charts, search, dashboards | You can ship a working baseline, keep state simple, and extend the component without rewriting it. |
| Data handling | Fetching, grouping, filtering, derived data, retries, stale responses | You understand API-backed UI and avoid state that drifts from the source of truth. |
| Rendering performance | Memoization, virtualization, throttling, debouncing, image loading | You measure before optimizing and know which bottleneck each technique addresses. |
| Browser fundamentals | Event loop, DOM APIs, storage, layout, accessibility, security | You can reason beyond React abstractions when the browser behavior matters. |
| System design | Playback, discovery, search, dashboards, recommendations | You can move from user flow to API contracts, cache behavior, metrics, and rollout. |
| Culture/project discussion | Feedback, disagreement, ownership, judgment, ambiguity | You can explain the decision-making behind your work, not just the final result. |
A table with 20 rows is simple. A table with expandable JSON, async filters, keyboard navigation, partial failures, and slow rows gives a better signal of how you build.
Use these as practice prompts, not a guaranteed question bank. For each one, timebox a working baseline first, then talk through tradeoffs, edge cases, accessibility, performance, and testing.
[2, 4, 5, 2, 3, 4], produce a frequency map and render it as a histogramUse this sequence to turn each prompt into a serious interview drill:
Aim for working code first, then explain data growth, network failure, changing requirements, accessibility, and tests.
Start with a simple data model:
id for stable row identityconfig or metadata object for expandable JSONKeep the first version simple. Fetch data, render rows, and show loading, error, and empty states. Use semantic table markup if the data is truly tabular. Keep row expansion keyed by stable IDs, not row index, because sorting and filtering can reorder the rows.
Then talk through follow-ups:
Set of expanded row IDs. Do not copy the full row data into expansion state.aria-expanded, and keyboard-reachable actions.Name these choices while coding. For example: "I am storing expanded row IDs instead of expanded row objects so sorting, filtering, and refetching do not corrupt expansion state."
Practice this with Data Table, then add your own expandable JSON follow-up after the base solution works.
The histogram-style prompt checks whether you can move from raw data to a useful UI. It also reveals whether you can write JavaScript cleanly under time pressure.
Start with the transformation:
function countByValue(values) {return values.reduce((counts, value) => {counts[value] = (counts[value] ?? 0) + 1;return counts;}, {});}
Then convert the object into sorted display data:
const bars = Object.entries(counts).map(([value, count]) => ({ value: Number(value), count })).sort((a, b) => a.value - b.value);
The habits matter more than the exact rendering code:
If the interviewer lets you use React, split the work into a data transform and a presentational chart component. If they ask for vanilla JavaScript, explain the data shape before writing DOM code.
React interviews still lean on JavaScript because many React bugs come from JavaScript, browser behavior, or the network.
Practice these questions:
this work in JavaScript, and how does Function.prototype.bind change it?bind polyfill. What edge cases matter?localStorage, sessionStorage, cookies, and in-memory cache?For practice, work through useThrottle, bind, and the React quiz questions. Tie each concept to a UI bug.
For closures, use a timer, event listener, or async request example. Show how a callback can read an older value and how to fix it with a functional state update, a ref, or a corrected dependency list. For prototypes, explain lookup and mutation without making it sound like a pattern you would reach for in modern React code. For the event loop, explain how promise callbacks and timers affect loading spinners, input handlers, and batched updates.
For storage, do not just compare lifetimes. Explain what belongs where:
localStorage for small, non-sensitive preferences.That difference is important in Netflix-style UI because the wrong state location can break profile switching, stale filters, analytics attribution, or experiment behavior.
Practice these prompts:
Before coding, say what you are optimizing for. For example: "I will keep selected IDs in state instead of copying whole item objects, because the source list can change after a refetch."
For broader practice, use the React coding interview questions and spend extra time on table, list, transfer, histogram, autocomplete, and carousel-style components.
A credible React answer has a few visible habits:
Avoid early abstraction. In a 45-minute interview, build one clear component, extract a helper when duplication appears, and explain where production code would separate concerns.
Before coding, decide where each state value belongs:
| State | Good default | Watch out for |
|---|---|---|
| Raw API data | Server/query state or a top-level component state | Copying rows into multiple local states. |
| Filter text | Local state, sometimes URL state | Applying filters directly inside event handlers and losing source data. |
| Selected rows | Set of stable IDs | Storing whole objects and breaking selection after refetch. |
| Expanded rows | Set of stable IDs | Using array indices as identity. |
| Sort | Local or URL state | Sorting raw data in place. |
| Loading/error | Data-fetching layer or component state | Hiding partial failures. |
State these choices during the interview; the interviewer cannot give credit for reasoning they never hear.
For Netflix-style UI, account for loading speed, input responsiveness, memory, and device constraints.
Practice these questions:
For system design, start from the user flow, then cover component boundaries, data contracts, cache behavior, loading states, failure modes, accessibility, metrics, and rollout.
Structure the answer like this:
Avoid treating "use memoization" as a complete answer. Memoization helps only when referential stability or expensive computation is the bottleneck. For image-heavy UI, network, decoding, layout stability, and focus behavior may matter more.
Cover these decisions:
Practice the base structure with Autocomplete, then adapt the answer to profiles, maturity restrictions, localization, personalization, and multiple device types.
Prepare stories where the technical decision and the human context are both clear.
Practice these questions:
Use the Behavioral Interview Playbook to keep each story concrete: context, constraint, decision, result, and what changed afterward.
"I built the dashboard and improved performance" is not enough. Interviewers need the decision trail.
For each project, prepare:
For a performance story, name the metric: interaction latency, JavaScript bundle size, image bytes, table render time, or error rate. Each points to a different bottleneck and fix.
For disagreement stories, explain what information each side had, how shared context was created, what decision was made, and how you supported it afterward.
Start by mapping your interview to the team. A Studio tooling role, TV UI role, ads role, and discovery role can all ask React questions, but the examples and tradeoffs will differ.
Use this preparation order:
Connect React and behavioral preparation: what you built, why it mattered, what constraints shaped it, and how you handled the product and team tradeoffs around it.
| Round type | How to prepare |
|---|---|
| Recruiter screen | Know why this role, why Netflix, what team domain interests you, and what culture points you can discuss honestly. |
| React coding | Practice one working component per session. Add a follow-up after the baseline works. Speak through state choices. |
| JavaScript/browser | Explain concepts through UI bugs: stale closures, event loop timing, storage misuse, DOM performance, XSS. |
| Frontend system design | Practice verbally: user flow, API shape, state model, cache, loading, failure, accessibility, metrics, rollout. |
| Hiring manager/project | Prepare two or three stories with decision, tradeoff, result, and lesson. |
If your interview is soon, prioritize prompts that transfer across teams:
bind, storage, XSS, and async request cancellation.After each coding drill, write a short self-review:
This is more useful than collecting hundreds of React trivia questions.
Yes. React can appear in frontend, UI engineer, data visualization, and full-stack loops. The questions are often practical: build a component, fetch data, manage state, explain hooks, improve rendering, or discuss product constraints.
Some loops include algorithms, but frontend candidates should not prepare only with LeetCode. Practice JavaScript, React components, browser behavior, async UI, performance, and frontend system design.
Hooks, state ownership, async data fetching, memoization, derived state, context tradeoffs, error boundaries, list rendering, accessibility, and performance measurement.
Yes. Read it before the recruiter or hiring-manager conversation, then prepare specific stories about feedback, disagreement, judgment, autonomy, and ownership. Generic culture answers are easy to identify.

Frontend LLD questions are machine coding interview questions where you design and build a small UI feature from scratch. In frontend interviews, "LLD" usually means proving that you can break a component into requirements, state, interactions, edge cases, and readable code before the timer runs out.
This guide covers five practical frontend LLD problems:
Each one includes the requirement, a two-minute planning outline, a React solution, and the mistakes interviewers notice quickly.
Use this article as a practice guide, then solve more user interface coding interview questions on GreatFrontEnd in a real coding environment.
LLD stands for low-level design. In backend interviews, it often means designing classes, APIs, relationships, and object behavior. In frontend interviews, the same term is usually applied to UI implementation.
For a frontend developer, frontend LLD usually tests whether you can:
That is why frontend LLD questions overlap heavily with React machine coding round questions and solutions. The interviewer is not only checking whether the component works. They are checking whether the implementation has a design.
If you are preparing for LLD for frontend developer roles, treat each prompt as both a design problem and a coding problem. The same prompts often appear under names like frontend machine coding round questions in React, React live coding interview questions, or React UI coding questions.
Before writing React code, spend two minutes turning the prompt into a plan.
Say what you are building in one sentence.
For example: "I will build an autocomplete input that fetches suggestions after the user types, shows loading and empty states, and lets the user select a suggestion."
This catches misunderstandings early.
Most frontend machine coding round questions fail in state design. List the important states before coding:
Avoid storing the same idea twice. If visibleItems can be derived from items and filter, derive it.
Keep it practical:
AutocompleteSearchInputSuggestionsListSuggestionItem
You do not need a production-grade architecture. You need boundaries that make the code easier to review.
Do not start with polish. Start with the smallest demo that proves the requirement:
This rhythm is what separates strong React live coding interview questions from half-finished code.
Build a list that loads the next page of items when the user scrolls near the bottom. Show loading, error, and "no more results" states.
This is a common frontend LLD problem because it tests async state, browser APIs, cleanup, duplicate request prevention, and edge cases around slow responses.
items, page, status, and hasMore in state.page changes.IntersectionObserver on a sentinel element at the bottom of the list.hasMore becomes false.import { useEffect, useRef, useState } from 'react';export default function InfiniteScrollList({ fetchPage }) {const [items, setItems] = useState([]);const [page, setPage] = useState(1);const [status, setStatus] = useState('idle'); // idle | loading | success | errorconst [hasMore, setHasMore] = useState(true);const sentinelRef = useRef(null);useEffect(() => {let cancelled = false;async function loadPage() {setStatus('loading');try {const result = await fetchPage(page);if (cancelled) {return;}setItems((currentItems) => [...currentItems, ...result.items]);setHasMore(result.hasMore);setStatus('success');} catch {if (!cancelled) {setStatus('error');}}}loadPage();return () => {cancelled = true;};}, [fetchPage, page]);useEffect(() => {const sentinel = sentinelRef.current;if (sentinel == null || status !== 'success' || !hasMore) {return;}const observer = new IntersectionObserver(([entry]) => {if (entry.isIntersecting) {// Stop observing until the next page finishes loading.observer.unobserve(sentinel);setPage((currentPage) => currentPage + 1);}},// Start loading before the user reaches the absolute bottom.{ rootMargin: '200px' },);observer.observe(sentinel);return () => {observer.disconnect();};}, [hasMore, status]);return (<section><ul>{items.map((item) => (<li key={item.id}>{item.title}</li>))}</ul>{status === 'loading' && <p>Loading...</p>}{status === 'error' && <p>Unable to load more items.</p>}{status === 'success' && items.length === 0 && <p>No results found.</p>}{items.length > 0 && !hasMore && <p>No more results.</p>}<div ref={sentinelRef} aria-hidden="true" /></section>);}
IntersectionObserver.items value instead of a functional state update.In a real interview, call out pagination details explicitly: "I am assuming the API returns { items, hasMore }. If it returns total count or next cursor instead, I will adapt the state model."
Build a star rating component that allows users to select a rating from one to five. It should show hover preview, selected value, and support a controlled value prop.
Practice this problem directly on GreatFrontEnd: Star Rating in React.
max, value, and onChange.import { useState } from 'react';export default function StarRating({ max = 5, value, onChange }) {const [internalValue, setInternalValue] = useState(0);const [hoveredValue, setHoveredValue] = useState(0);const selectedValue = value ?? internalValue;const displayValue = hoveredValue || selectedValue;function selectRating(nextValue) {if (value == null) {setInternalValue(nextValue);}onChange?.(nextValue);}return (<fieldset><legend>Rating</legend><divonMouseLeave={() => {setHoveredValue(0);}}>{Array.from({ length: max }, (_, index) => {const rating = index + 1;const isFilled = rating <= displayValue;return (<buttonaria-label={`${rating} star${rating === 1 ? '' : 's'}`}aria-pressed={rating === selectedValue}key={rating}onClick={() => {selectRating(rating);}}onMouseEnter={() => {setHoveredValue(rating);}}type="button">{isFilled ? '★' : '☆'}</button>);})}</div></fieldset>);}
value prop is provided.type="button", which can accidentally submit a parent form.A strong follow-up is half-star ratings. The design change is clear: replace each full-star button with either two hit areas or pointer-position logic, then store values in 0.5 increments.
Build an autocomplete search input that fetches suggestions as the user types. It should debounce requests, cancel stale requests, show loading and empty states, and allow keyboard selection.
Autocomplete is one of the best frontend LLD questions because it touches forms, async behavior, race conditions, keyboard interactions, positioning, and accessibility. For a deeper architecture discussion, read GreatFrontEnd's Autocomplete front end system design question.
query, suggestions, status, and activeIndex in state.AbortController so older requests cannot overwrite newer results.import { useEffect, useState } from 'react';export default function Autocomplete({ search }) {const [query, setQuery] = useState('');const [suggestions, setSuggestions] = useState([]);const [status, setStatus] = useState('idle'); // idle | loading | success | errorconst [activeIndex, setActiveIndex] = useState(-1);useEffect(() => {const trimmedQuery = query.trim();if (trimmedQuery.length < 2) {setSuggestions([]);setStatus('idle');setActiveIndex(-1);return;}// AbortController lets us cancel this request if the user types a newer query.const controller = new AbortController();// Debounce the request so every keystroke does not call the API.const timeoutId = window.setTimeout(async () => {setStatus('loading');try {const results = await search(trimmedQuery, {signal: controller.signal,});setSuggestions(results);setStatus('success');setActiveIndex(results.length > 0 ? 0 : -1);} catch (error) {if (error?.name !== 'AbortError') {setStatus('error');}}}, 250);return () => {// Cancel both the scheduled search and any in-flight request for the old query.window.clearTimeout(timeoutId);controller.abort();};}, [query, search]);function selectSuggestion(suggestion) {setQuery(suggestion.label);setSuggestions([]);setStatus('idle');setActiveIndex(-1);}function handleKeyDown(event) {if (suggestions.length === 0) {return;}if (event.key === 'ArrowDown') {event.preventDefault();setActiveIndex((currentIndex) =>Math.min(currentIndex + 1, suggestions.length - 1),);}if (event.key === 'ArrowUp') {event.preventDefault();setActiveIndex((currentIndex) => Math.max(currentIndex - 1, 0));}if (event.key === 'Enter' && activeIndex >= 0) {event.preventDefault();selectSuggestion(suggestions[activeIndex]);}if (event.key === 'Escape') {setSuggestions([]);setActiveIndex(-1);}}return (<div><label htmlFor="search">Search</label><inputaria-activedescendant={activeIndex >= 0? `search-suggestion-${suggestions[activeIndex].id}`: undefined}aria-autocomplete="list"aria-controls="search-suggestions"aria-expanded={suggestions.length > 0}autoComplete="off"id="search"onChange={(event) => {setQuery(event.target.value);}}onKeyDown={handleKeyDown}role="combobox"value={query}/>{status === 'loading' && <p>Loading suggestions...</p>}{status === 'error' && <p>Unable to load suggestions.</p>}{status === 'success' && suggestions.length === 0 && (<p>No suggestions found.</p>)}{suggestions.length > 0 && (<ul id="search-suggestions" role="listbox">{suggestions.map((suggestion, index) => (<liaria-selected={index === activeIndex}id={`search-suggestion-${suggestion.id}`}key={suggestion.id}onMouseDown={(event) => {// Keep focus on the input while selecting with the mouse.event.preventDefault();selectSuggestion(suggestion);}}role="option">{suggestion.label}</li>))}</ul>)}</div>);}
onMouseDown avoids that common issue.If the interviewer asks for production-level accessibility, discuss the full combobox pattern and keyboard expectations. In a timed React live coding interview, a clear partial implementation plus the right explanation is usually better than a broken full implementation.
Build a list where users can reorder items by dragging one item and dropping it before another item.
This is a useful LLD problem because it tests list state, stable keys, event handling, and how well you can avoid overcomplicating the first version.
import { useState } from 'react';const initialItems = [{ id: 'todo', label: 'Todo' },{ id: 'doing', label: 'Doing' },{ id: 'review', label: 'Review' },{ id: 'done', label: 'Done' },];export default function DragAndDropList() {const [items, setItems] = useState(initialItems);const [draggedId, setDraggedId] = useState(null);function moveItemBefore(targetId) {if (draggedId == null || draggedId === targetId) {return;}setItems((currentItems) => {const draggedItem = currentItems.find((item) => item.id === draggedId);if (draggedItem == null) {return currentItems;}const remainingItems = currentItems.filter((item) => item.id !== draggedId,);// Find the target after removing the dragged item so the insertion index is correct.const targetIndex = remainingItems.findIndex((item) => item.id === targetId,);if (targetIndex === -1) {return currentItems;}return [...remainingItems.slice(0, targetIndex),draggedItem,...remainingItems.slice(targetIndex),];});}return (<ul>{items.map((item) => (<lidraggablekey={item.id}onDragEnd={() => {setDraggedId(null);}}onDragOver={(event) => {event.preventDefault();}}onDragStart={() => {setDraggedId(item.id);}}onDrop={() => {moveItemBefore(item.id);}}>{item.label}</li>))}</ul>);}
items array with splice instead of returning a new array.If the prompt expects a polished implementation, add a drop zone after the final item so the user can move an item to the end. If the prompt expects mobile support, explain that you would likely use pointer events or a well-tested drag-and-drop library in production.
For related practice, solve Transfer List in React, which tests similar item movement and state updates.
Build a toast notification system that can show multiple messages, auto-dismiss each toast after a delay, and allow manual dismissal.
This frontend LLD question tests queues, timers, cleanup, rendering a stack, and accessibility for status updates.
showToast function.setTimeout.import { useCallback, useEffect, useRef, useState } from 'react';export default function ToastDemo() {const [toasts, setToasts] = useState([]);const nextIdRef = useRef(1);// Keep timer IDs outside render state so they can be cleared reliably.const timersRef = useRef(new Map());const dismissToast = useCallback((id) => {const timerId = timersRef.current.get(id);if (timerId != null) {window.clearTimeout(timerId);timersRef.current.delete(id);}setToasts((currentToasts) =>currentToasts.filter((toast) => toast.id !== id),);}, []);const showToast = useCallback((message, variant = 'info') => {const id = nextIdRef.current;nextIdRef.current += 1;setToasts((currentToasts) => [...currentToasts,{ id, message, variant },]);const timerId = window.setTimeout(() => {dismissToast(id);}, 4000);timersRef.current.set(id, timerId);},[dismissToast],);useEffect(() => {return () => {timersRef.current.forEach((timerId) => {window.clearTimeout(timerId);});timersRef.current.clear();};}, []);return (<section><buttononClick={() => {showToast('Profile saved', 'success');}}type="button">Show toast</button><div aria-live="polite" aria-relevant="additions" role="status">{toasts.map((toast) => (<div data-variant={toast.variant} key={toast.id}><span>{toast.message}</span><buttonaria-label="Dismiss notification"onClick={() => {dismissToast(toast.id);}}type="button">×</button></div>))}</div></section>);}
isToastVisible, which cannot represent multiple toasts.For a stronger answer, mention how you would expose this through context in a real app:
ToastProvideruseToast()showToast()dismissToast()ToastViewport
That shows you understand the difference between an interview implementation and a reusable application service.
When practicing react machine coding round questions and solutions, do not only ask "does it work?" Ask whether the solution will survive follow-up questions.
| Component | First follow-up | What it tests |
|---|---|---|
| Infinite scroll | Add retry and cursor pagination | Async state and API assumptions |
| Star rating | Support half-star ratings | Component API and event handling |
| Autocomplete search | Add caching and stale response handling | Async correctness and performance |
| Drag-and-drop list | Move items across two lists | Data modeling and immutable updates |
| Toast notification | Add toast placement and max queue size | State queues and reusable APIs |
Good frontend LLD preparation means practicing the base problem and then asking: "What would break if the interviewer adds one more requirement?"
If you have one week before a frontend LLD or React machine coding round, use this sequence:
Day 1: Basic state and events Build counter, accordion, tabs, todo list, and star rating.
Day 2: Lists and derived state Build transfer list, selectable cells, data table, and drag-and-drop reorder.
Day 3: Async UI Build autocomplete, job board, infinite scroll, and a search results page.
Day 4: Timers and queues Build progress bars, stopwatch, traffic light, and toast notifications.
Day 5: Accessibility pass Revisit tabs, accordion, modal dialog, autocomplete, and star rating.
Day 6: Timed mocks Pick two problems and solve each in 45 to 60 minutes.
Day 7: Review and redo Redo the weakest problem without looking at your previous code.
GreatFrontEnd has many of these as browser-based practice questions. Start with React coding interview questions, then add the broader UI coding question set.
In frontend LLD interviews, the strongest candidates make their thinking easy to follow. They do not simply type React code quickly.
Interviewers usually look for:
The best habit is simple: say the design before you code it. A practical sentence like "I will keep hover preview separate from committed rating because they represent different states" gives the interviewer confidence that your code is intentional.
Frontend LLD questions are not a different category from React machine coding questions. They are the design layer inside the coding round.
If you can clarify requirements, model state, build the main interaction, handle edge cases, and explain trade-offs while coding, you are preparing for the round that many frontend interviews now use to separate React familiarity from real UI engineering skill.

This guide is a practical set of TypeScript interview questions for senior frontend developers. In 2026, senior TypeScript interviews are less about defining Partial<T> and more about using the type system to model product states, prevent invalid component APIs, and make refactors safer.
If you are preparing for TypeScript interview questions for experienced roles, focus on code problems. Interviewers want to see how you design constraints, how you narrow unsafe data, and when you choose a simple type over a clever one.
If you want more hands-on practice after this guide, use GreatFrontEnd's TypeScript interview questions.
At junior and mid-level, TypeScript questions often check syntax: interface vs type, unknown vs any, optional properties, or basic generics.
At senior and staff level, the prompt usually sounds more like production code:
That is the pattern behind the TypeScript coding interview questions below.
How it appears in an interview: "Our dashboard uses isLoading, error, and data, but invalid states keep slipping through. Refactor the type."
type RemoteData<T> =| { status: 'idle' }| { status: 'loading' }| { status: 'success'; data: T }| { status: 'error'; error: Error };function assertNever(value: never): never {throw new Error(`Unhandled state: ${JSON.stringify(value)}`);}function renderUsers(state: RemoteData<Array<{ id: string; name: string }>>) {switch (state.status) {case 'idle':return 'Choose a team';case 'loading':return 'Loading users';case 'success':return state.data.map((user) => user.name).join(', ');case 'error':return state.error.message;default:return assertNever(state);}}
What interviewers look for: You should remove impossible states instead of documenting them. success must always have data; error must always have error; loading should not accidentally carry stale data unless that is an explicit product decision.
The senior signal is exhaustiveness. If a future engineer adds { status: 'refreshing' }, the assertNever line makes incomplete rendering logic fail at compile time.
pick helper without losing key safetyHow it appears in an interview: "Implement pick(obj, keys) so keys must exist on the object and the return type only contains those keys."
function pick<T extends object, K extends keyof T>(obj: T,keys: readonly K[],): Pick<T, K> {const result = {} as Pick<T, K>;for (const key of keys) {result[key] = obj[key];}return result;}const user = {id: 'u_1',name: 'Ada',role: 'admin',active: true,};const summary = pick(user, ['id', 'name'] as const);// summary is { id: string; name: string }
What interviewers look for: This tests generic constraints and keyof. A weaker answer uses string[] for keys and returns Partial<T>, which loses the reason TypeScript is useful here. A strong answer preserves the exact key union.
The follow-up is usually about mutation. The cast inside the function is acceptable because JavaScript object construction is dynamic, but the public API remains precise. Senior candidates can explain that boundary instead of pretending no assertion is ever allowed.
How it appears in an interview: "We need an endpoint that updates a user profile. It should allow name, email, and timezone, but never id, createdAt, or role."
type User = {id: string;name: string;email: string;timezone: string;role: 'member' | 'admin';createdAt: string;};type EditableUserFields = Pick<User, 'name' | 'email' | 'timezone'>;type UpdateUserPayload = Partial<EditableUserFields>;async function updateUser(id: User['id'], payload: UpdateUserPayload) {return fetch(`/api/users/${id}`, {method: 'PATCH',body: JSON.stringify(payload),});}updateUser('u_1', { timezone: 'Asia/Kolkata' });
What interviewers look for: Utility types should express intent. Partial<User> is too broad because it allows protected fields. Pick plus Partial says, "Only these fields are editable, and each is optional in a patch."
This is one of the most common TypeScript coding questions because it mirrors real frontend work: form models, DTOs, optimistic updates, and API client boundaries.
infer to unwrap async resultsHow it appears in an interview: "Given a service function, derive the resolved response type without copying it manually."
async function getCurrentUser() {return {id: 'u_1',name: 'Grace',permissions: ['billing:read', 'team:write'] as const,};}type AsyncReturn<T> = T extends (...args: infer _Args) => Promise<infer R>? R: never;type CurrentUser = AsyncReturn<typeof getCurrentUser>;function canEditTeam(user: CurrentUser) {return user.permissions.includes('team:write');}
What interviewers look for: This tests whether you understand conditional types as relationships between inputs and outputs. infer R captures the resolved value from a promise-returning function.
A practical follow-up: TypeScript already has ReturnType<T> and Awaited<T>, so this can also be written as:
type CurrentUser = Awaited<ReturnType<typeof getCurrentUser>>;
The senior answer names the built-in utility and explains when a custom helper is still useful.
How it appears in an interview: "Create a typed event map for a settings object. Each key should produce an on<Key>Changed handler."
type ChangeHandlers<T extends object> = {[K in keyof T as `on${Capitalize<string & K>}Changed`]: (nextValue: T[K],) => void;};type Settings = {theme: 'light' | 'dark';pageSize: 25 | 50 | 100;showArchived: boolean;};type SettingsHandlers = ChangeHandlers<Settings>;const handlers: SettingsHandlers = {onThemeChanged: (theme) => console.log(theme),onPageSizeChanged: (pageSize) => console.log(pageSize),onShowArchivedChanged: (showArchived) => console.log(showArchived),};
What interviewers look for: This combines mapped types, key remapping, template literal types, and indexed access types. It is a strong senior prompt because the runtime idea is simple, but the type-level transformation requires precision.
The judgment part matters too. This pattern is useful for design-system APIs, event maps, analytics names, and generated clients. It is probably overkill for one local component.
How it appears in React TypeScript interview questions: "Create a button that can behave like a link or a real button, but never both."
type BaseProps = {children: React.ReactNode;variant?: 'primary' | 'secondary';};type LinkButtonProps = BaseProps & {href: string;onClick?: never;};type ActionButtonProps = BaseProps & {href?: never;onClick: () => void;};type ButtonProps = LinkButtonProps | ActionButtonProps;function Button(props: ButtonProps) {if (props.href !== undefined) {return <a href={props.href}>{props.children}</a>;}return <button onClick={props.onClick}>{props.children}</button>;}
What interviewers look for: Prop types can define product constraints instead of merely documenting them. never prevents callers from passing both href and onClick, and the href check narrows the union inside the component.
This is a better answer than a single prop bag with href?: string and onClick?: () => void, because the weak version makes every invalid combination somebody else's runtime problem.
How it appears in an interview: "We have variants in a design system. Keep the object literal narrow, but verify that every variant exists."
type ButtonVariant = 'primary' | 'secondary' | 'danger';const buttonClasses = {primary: 'bg-blue-600 text-white',secondary: 'bg-slate-100 text-slate-900',danger: 'bg-red-600 text-white',} satisfies Record<ButtonVariant, string>;function getButtonClass(variant: ButtonVariant) {return buttonClasses[variant];}
What interviewers look for: This problem checks whether you know the difference between annotation and validation. satisfies verifies the object conforms to Record<ButtonVariant, string> while preserving useful inference from the original object.
In a senior interview, expect a follow-up: "What happens when someone adds ghost to ButtonVariant?" The object must now include ghost, so the compiler catches the incomplete design-system map.
unknown is better than anyHow it appears in an interview: "We parse JSON from local storage. Type it safely."
function parseJson(value: string): unknown {return JSON.parse(value);}type Preferences = {theme: 'light' | 'dark';};function isPreferences(value: unknown): value is Preferences {return (typeof value === 'object' &&value !== null &&'theme' in value &&(value.theme === 'light' || value.theme === 'dark'));}const parsed = parseJson(localStorage.getItem('preferences') ?? '{}');if (isPreferences(parsed)) {parsed.theme;}
What interviewers look for: any says "trust me" and disables checking. unknown says "prove it first." Senior TypeScript work often happens at boundaries: network responses, storage, third-party scripts, feature flags, analytics payloads, and CMS content.
The strong answer pairs unknown with a narrowing function or schema validator. TypeScript cannot make untrusted runtime data safe by itself.
How it appears in an interview: "Our app has several status constants. Should we use an enum, a union type, or an as const object?"
const ORDER_STATUS = {Draft: 'draft',Paid: 'paid',Shipped: 'shipped',Cancelled: 'cancelled',} as const;type OrderStatus = (typeof ORDER_STATUS)[keyof typeof ORDER_STATUS];function getStatusLabel(status: OrderStatus) {switch (status) {case ORDER_STATUS.Draft:return 'Draft';case ORDER_STATUS.Paid:return 'Paid';case ORDER_STATUS.Shipped:return 'Shipped';case ORDER_STATUS.Cancelled:return 'Cancelled';}}
What interviewers look for: In frontend code, string literal unions and as const objects often fit better than regular enums because they keep API values as plain strings and compose cleanly with other type utilities.
A senior answer should not turn this into a rule. Enums can still be useful in shared TypeScript codebases, but for UI state, analytics event names, API statuses, and component variants, literal unions are usually easier to compose with mapped types, discriminated unions, and template literal types.
Use this checklist for TypeScript interview questions for 5 years experience and above:
keyof, indexed access types, and constrained type parameters.Partial, Required, Pick, Omit, Record, ReturnType, Parameters, and Awaited.infer, even if you do not write them every day.unknown at unsafe boundaries and narrow before use.as const and literal unions are cleaner than runtime enums.The best senior TypeScript answers are practical. They prevent invalid states, preserve inference, and make the next refactor less risky.
When practicing TypeScript coding questions, do not memorize every utility type in isolation. Start with a real constraint: which states are impossible, which keys are allowed, which component props conflict, which runtime values are untrusted. Then use TypeScript to encode that constraint clearly.
For more practice, head to GreatFrontEnd's TypeScript interview questions and work through problems with the same mindset: code first, types as guardrails, and explanations that connect back to production frontend work.

No. Frontend development is not dying in 2026. Simple UI assembly is getting cheaper. The harder parts remain: browser behavior, accessibility, state, performance, and product trade-offs.
That distinction matters. Most "will AI replace frontend developers" arguments confuse two different jobs:
AI can generate React components, Tailwind layouts, landing page sections, form markup, and first-pass prototypes. That helps. It also solves the easiest part of many frontend tasks.
The harder part is still engineering: clarifying requirements, designing component boundaries, reasoning about state, debugging CSS, handling edge cases, reviewing generated code, and shipping interfaces that hold up outside a demo.
The better question is: which version of frontend development is dying?
The version that is most exposed looks like this:
That work is easier to automate because the requirements are narrow and the review is mostly visual. If the screen looks close enough, the task feels done.
Frontend is an attractive target for AI. There is a huge amount of HTML, CSS, JavaScript, React, and Tailwind code online. Many UI patterns are well documented. A lot of frontend work also sits close to the edge of the system, so teams are more comfortable generating a first pass there than in payments, infrastructure, or security-critical paths.
But not all frontend work has the same risk. A static marketing page, an admin CRUD screen, and a support dashboard are different from browser-native products like Figma, Linear, Google Docs, Spotify, or a complex AI workspace. The first group is often a thin layer over data. The second group has deep client-side state, real-time collaboration, offline behavior, streaming updates, performance constraints, and many interaction edge cases.
The harder work looks different:
That second group still needs frontend engineers. It may need fewer people typing boilerplate, but it needs people who can own the outcome.
There is no single clean metric called "frontend developer demand." Job titles vary by company, country, seniority, and whether the role is called frontend, full stack, web developer, UI engineer, product engineer, or software engineer.
Still, the available data does not support the claim that frontend development is disappearing.
| Signal | What it says | How to read it |
|---|---|---|
| BLS web developers and digital designers | Overall employment is projected to grow 7% from 2024 to 2034, with about 14,500 openings per year. | This is a US source, but it is a useful labor-market signal for web and interface work. |
| BLS software developers, QA analysts, and testers | Employment is projected to grow 15% from 2024 to 2034, with about 129,200 openings per year. | Frontend sits inside broader software work, and software demand is not collapsing. |
| World Economic Forum Future of Jobs 2025 | Software and applications developers are listed among the fastest-growing roles by 2030. | Employers expect technology roles to keep growing even as AI changes the work. |
| LinkedIn U.S. Software Engineer Talent Landscape 2026 | SWE hiring momentum has slowed, entry pathways are tightening, and AI-adjacent roles are growing. | The market is not dead, but juniors and narrow specialists face a harder bar. |
| Indeed Global Labor Market and Workforce Trends 2026 | US tech job postings moved from boom to bust, and a larger share of tech postings now ask for at least five years of experience. | Current hiring data supports the experience-bar argument better than broad replacement claims. |
| Stack Overflow Developer Survey 2025 | 84% of respondents use or plan to use AI tools, but more developers distrust AI accuracy than trust it. | AI is now part of development, but human verification remains central. |
| DORA State of AI-assisted Software Development 2025 | AI works as an amplifier of an organization's existing engineering strengths and weaknesses. | AI helps teams with good systems, but it does not remove the need for review, testing, and delivery discipline. |
| GitHub Octoverse 2025 | TypeScript became GitHub's most used language by contributor count in August 2025; JavaScript and TypeScript remain the largest combined ecosystem. | The web stack is not fading. It is becoming more typed and production-oriented. |
| WebAIM Million 2026 | 95.9% of the top one million home pages had detected WCAG failures, with 56.1 errors per page on average. | The web still has a massive quality gap, especially around accessibility. |
Taken together, the data does not show frontend disappearing. It shows a tighter market with higher expectations. AI is now part of development, entry-level pathways are harder, and teams still need people who can review, test, and improve the code before users touch it.
Frontend has been declared dead many times.
WYSIWYG editors like Dreamweaver promised that visual editing would remove the need to write HTML. WordPress made publishing dramatically easier. Offshore outsourcing changed how companies staffed web projects. Bootstrap made decent-looking layouts available to everyone. Webflow and no-code tools let non-engineers create polished sites. Now AI can generate a full screen from a prompt.
Each wave removed repetitive work. None removed the need for people who understand how the web behaves.
What changed was the baseline. In the early web, knowing HTML and CSS was enough to be useful. Later, frontend developers needed responsive design, JavaScript, browser APIs, build tools, design systems, accessibility, performance, analytics, API integration, security awareness, and framework fluency.
AI is another abstraction wave. It removes some manual work. It also creates more code that someone has to review, integrate, test, and maintain.
AI will replace some frontend tasks. It will not replace every frontend developer.
These tasks are already easier to automate:
Use it. Just do not confuse a generated first pass with finished work.
Faster code generation does not remove the rest of the job. In production, the expensive part is often not typing the component. It is deciding what the component should do, how it should fail, whether the state model is correct, whether keyboard users can operate it, whether it performs under load, and whether the implementation will still make sense after three more requirements are added.
Stack Overflow's 2025 survey reflects that split. AI usage is widespread, but only a small fraction of developers highly trust AI output. Most serious teams still review generated code before shipping it.
AI-generated frontend code can be impressive in a screenshot and still fail in the browser.
A generated layout can look fine at 1440px with short English labels and then break with long content, browser zoom, a narrow container, a sticky header, a nested scroll area, or an unexpected image ratio.
The problem is that AI often predicts a plausible layout pattern without knowing the actual page, the container it will live inside, the device constraints, or the user's path through the interface. CSS generated by AI can look right in isolation and still be wrong inside the product.
The hard parts of CSS are constraint problems:
width, max-width, or min-width: 0?AI can suggest CSS. A frontend engineer has to know whether it is stable. If this is a weak spot, start with common CSS mistakes front end engineers make.
Accessibility is a clear example. The 2026 WebAIM Million report found detected WCAG failures on 95.9% of top home pages. It also found that pages using ARIA had significantly more detected errors on average than pages without ARIA.
That does not mean ARIA is bad. It means accessibility is easy to get wrong when people add attributes without understanding the interaction model.
A modal is not accessible because it has role="dialog". A combobox is not accessible because the markup contains aria-expanded. A menu is not accessible because the items look clickable.
You still need to test labels, focus order, keyboard behavior, escape behavior, screen reader names, error messages, contrast, reduced motion, and disabled states. Research on AI-assisted accessible coding has found similar issues: developers often fail to prompt for accessibility, skip manual validation steps, or cannot verify whether the generated UI is compliant.
AI can generate a form. It cannot reliably decide whether the form is too long, whether the error message is useful, whether the destructive action needs confirmation, whether a dropdown should be a combobox, or whether the user should see an empty state before a loading state.
Those choices are product judgment. They come from understanding users, constraints, and trade-offs.
Frontend bugs often live in transitions:
These bugs do not show up in a static screenshot. They show up when a user interacts with the UI in the wrong order, on a slow network, with real data.
The more product state a frontend owns, the more these combinations matter. Auth state, cached server data, optimistic updates, form drafts, route transitions, animations, feature flags, and WebSocket events can all interact at once. AI can generate each individual pattern. The failure usually appears in the combination.
Frontend development is becoming less about writing every line by hand and more about owning the quality of the interface.
Expect frontend roles to move in a few directions:
This does not mean every frontend developer has to become a backend engineer. It means frontend developers need enough surrounding context to make good trade-offs.
Some simple products may move closer to chat, automation, or API-first workflows. The products that still need rich interfaces will usually need better frontend engineering, not less. If the UI is where users make decisions, coordinate work, edit content, review AI output, or recover from mistakes, the interface becomes part of the product's safety and quality layer.
The core skills still matter:
The more generated code a team accepts, the more it needs engineers who can tell correct code from code that merely looks correct.
If you are choosing between roles, ask whether the company actually values frontend. A product where the browser experience is the business will give you more room to build frontend skill than a company where the UI is mostly a thin wrapper over internal data. Use the questions in How to Evaluate Companies as a Front End Engineer to separate those environments.
Yes, if you are willing to learn the browser instead of only learning a framework.
Do not start with the assumption that React alone makes you a frontend developer. React is important, but it sits on top of HTML, CSS, JavaScript, browser events, accessibility rules, network behavior, and product constraints.
A practical learning path looks like this:
Do not let tools hide gaps. If you cannot explain why generated code works, you have not learned the skill yet.
Frontend can still be a good career in 2026, but it is not an easy shortcut into software engineering.
The entry-level bar is higher than it used to be. LinkedIn's 2026 software engineering report says entry pathways are tightening, and Indeed's 2026 labor-market report shows more tech postings asking for at least five years of experience. Companies have more tooling, more AI assistance, and less patience for developers who can only assemble a screen when every requirement is spelled out.
At the same time, teams still need engineers who can make product UI reliable, accessible, fast, and maintainable.
The strongest signal you can show is not a certificate or a cloned landing page. It is working software you can explain.
Build projects that prove you can handle:
Then practice explaining your decisions. In interviews and on the job, communication is part of the work.
The risk is not using AI. The risk is having no judgment after AI gives you code.
| Replaceable signal | Strong signal |
|---|---|
| Copies generated code without reading it. | Reviews generated code and can explain every trade-off. |
| Builds only the happy path. | Handles empty, loading, error, disabled, and interrupted states. |
| Treats CSS as trial and error. | Understands layout, constraints, overflow, and responsive behavior. |
| Adds ARIA by pattern matching. | Tests keyboard behavior, focus, labels, and screen reader names. |
| Waits for exact requirements. | Clarifies scope and proposes a sensible MVP. |
| Ships one-off components. | Builds maintainable components with clear boundaries. |
| Avoids debugging. | Uses DevTools, logs, tests, and reasoning to isolate the bug. |
| Treats AI as a replacement for learning. | Treats AI as a tool for speed, exploration, and review. |
So no, frontend development is not dying in 2026. The low end is being compressed, expectations are rising, and the work that survives depends more on engineering judgment.
If you want to prepare for that version of the role, practice building real UI. Start with GreatFrontEnd's user interface coding questions, review your mistakes, and push past the version that only works in the first screenshot.

A machine coding round is a live coding interview where you build a working application or UI component from scratch in a fixed time, typically 60 to 90 minutes. In frontend interviews, the prompt is often a todo list, autocomplete, data table, image carousel, file explorer, modal dialog, or small dashboard built with HTML, CSS, JavaScript, and often React.
Companies use this round because it is hard to fake. You have to turn a rough product requirement into working code while making trade-offs in real time.
React syntax gets you through the first few minutes. The rest of the round tests whether you can break down the UI, design state, handle events, use accessible markup, keep styling readable, and still ship something that runs.
Use this as an operating guide for the round, then practice the patterns on User interface coding interview questions on GreatFrontEnd.
A machine coding round asks you to build a small but complete feature in a live coding environment. The interviewer gives you a prompt, watches how you plan, and reviews the code you write.
For a frontend role, the prompt often sounds like one of these:
The final UI does not need to look production-grade. It needs to work, and the code needs to be readable enough to discuss.
The machine coding round sits between algorithm interviews and frontend system design interviews. It is implementation-heavy.
| Round | What it tests | Typical output |
|---|---|---|
| DSA coding | Algorithms, data structures, complexity | A function that passes test cases |
| Machine coding | Feature implementation, code organization, state, UI behavior | A working component or small app |
| Frontend system design | Architecture, APIs, scalability, performance | A design discussion with diagrams and trade-offs |
| Take-home assignment | Deeper implementation without live pressure | A larger project submitted later |
That is why developers who are comfortable with DSA can still struggle here. This round is about shipping a small product, not finding one clever trick.
Your solution has to be executable, demonstrable, and easy to review. Code that looks clever but cannot be run, clicked through, or explained will not score well.
For a frontend machine coding round, aim for these deliverables:
App, the solution becomes harder to review.status value over multiple booleans when the UI has distinct modes.Machine coding rounds often end with a walkthrough. If your code works but is hard to explain, you have made the review harder than it needed to be.
A timed round is not a portfolio project. Spend the time on behavior first.
Skip these unless the prompt asks for them:
Performance and extensibility still matter, but only at the right level. In a data table question, sorting and pagination matter more than virtualizing rows before pagination works. In autocomplete, debouncing and stale-response handling matter more than a dropdown animation.
The order is simple: make the feature correct, make it readable, make it resilient, then polish.
Many machine coding resources focus on object-oriented backend-style problems: parking lots, cab booking, split expenses, inventory systems, or command-line menus. Those can teach modularity, but frontend interviews test a different surface area.
Frontend machine coding round questions care about:
For example, a generic machine coding problem might ask whether your service classes are extensible. A frontend machine coding problem might ask whether your autocomplete handles a slow response from an older request after the user has already typed a newer query. Both test design thinking, but the failure modes are different.
If your target role is frontend-heavy, practice UI problems in the browser. Reading about machine coding helps, but this round favors engineers who have built these components enough times that the implementation path feels familiar.
The usual failure mode is not lack of knowledge. It is starting to code before the solution has a shape.
If the prompt says "build autocomplete", do not immediately create an input and start fetching data. Ask what matters:
Clarification prevents building the wrong thing.
Weak solutions often put all JSX, state, event handlers, and styling in one growing component. That works for ten minutes, then becomes hard to reason about.
A better approach is to sketch the component tree before coding:
AutocompleteSearchInputSuggestionsListSuggestionItemEmptyStateErrorState
Do not over-engineer it. Separate responsibilities so the interviewer can follow the code.
State is where machine coding rounds quietly become messy. Candidates add useState calls whenever they need one more value, then discover that several states can contradict each other.
For example, this state model is fragile:
const [isLoading, setIsLoading] = useState(false);const [hasError, setHasError] = useState(false);const [results, setResults] = useState([]);const [showDropdown, setShowDropdown] = useState(false);
It is possible to end up with loading, error, and visible dropdown all at once.
For more complex UI flows, make states explicit:
const initialState = {query: '',status: 'idle', // idle | loading | success | errorresults: [],activeIndex: -1,error: null,};
You can implement that with useState, useReducer, or a small custom hook. What matters is that the states describe the UI clearly.
Interviewers notice when the happy path works but everything else breaks. Common frontend edge cases include:
You will not cover every edge case. Pick the ones that matter for the prompt and call out the rest.
In a live round, silence can make a reasonable solution look weaker than it is. Explain decisions as you go:
This makes the implementation easier to review.
In a timed round, it is tempting to keep coding until the last second. That often creates a worse outcome: the final code has a syntax error, broken import, missing prop, or state update bug that prevents the interviewer from running it.
Keep the app executable after every major step:
This rhythm feels slower, but it prevents the worst outcome: a good idea trapped inside code that does not run.
Do not wait until the final five minutes to clean up the code. If a component is becoming large, extract a small part while the mental model is still fresh. If a state variable name is confusing, rename it before more logic depends on it.
Refactoring during the round should be modest. Extract components, rename variables, move derived values into constants, and group related handlers. Avoid a full architectural rewrite unless the current code is blocking progress.
Interviewers tend to look at these areas:
A small UI component can reveal a lot about day-to-day frontend engineering ability.
Use this rubric during practice:
| Area | Weak signal | Strong signal |
|---|---|---|
| Requirements | Starts coding immediately | Clarifies MVP and follow-ups |
| Structure | One large component | Small components with clear responsibilities |
| State | Many unrelated booleans | Minimal state and derived values |
| Edge cases | Happy path only | Empty, loading, error, reset, and rapid interactions |
| Accessibility | Clickable divs everywhere | Semantic controls and keyboard-aware behavior |
| Communication | Silent implementation | Explains trade-offs while coding |
| Finish | Broken or incomplete demo | Working core feature with clear next steps |
Have a clock plan before the interview starts. Time pressure is easier when the next step is not a mystery.
Before writing code, restate the problem and choose the MVP.
For example:
"I will first build a working autocomplete with query input, loading state, fetched suggestions, and click selection. If time permits, I will add debouncing, keyboard navigation, and caching."
That sentence confirms the core behavior, limits overbuilding, and gives you a path for follow-ups.
Write a small plan in comments or on a scratchpad:
Components:- App- SearchInput- SuggestionsList- SuggestionItemState:- query- status- results- activeIndexEvents:- onChange- onSelect- onKeyDown
This gives you enough structure to start coding without drifting.
Prioritize working behavior before polish. For most React machine coding round questions, the build order should be:
For a todo list, that means add item, render list, toggle item, delete item, then filters. For autocomplete, that means input, fetch/filter, render suggestions, select item, then loading/error/keyboard support.
Do not start with animations, theme systems, or clever abstractions. Add them after the feature works.
Keep a visible "done list" in your head. After every requirement, ask whether the interviewer can verify it now. If yes, move on. If not, finish that slice before starting another feature.
Once the happy path works, add the states candidates often miss:
For accessibility-heavy components, check keyboard behavior and semantic HTML. For example, tabs should use buttons rather than clickable divs.
Use the UI like a user:
Then mention what you would improve with more time:
That turns an unfinished stretch goal into a clear trade-off.
Autocomplete is one of the best machine coding round questions because it tests state, async behavior, forms, keyboard support, accessibility, caching, and race conditions.
Before coding, split it like this:
AutocompleteSearchInputSuggestionsListEmptyStateErrorState
Then define the state transitions:
| User action | State change |
|---|---|
| Types query | Update query, set status to loading |
| API succeeds | Store results, set status to success |
| API fails | Store error, set status to error |
| Selects result | Set query, close suggestions |
| Clears input | Reset to idle |
You might start with this shape:
function Autocomplete() {const [query, setQuery] = useState('');const [status, setStatus] = useState('idle');const [results, setResults] = useState([]);const hasResults = status === 'success' && results.length > 0;const showEmptyState = status === 'success' && results.length === 0;return (<div><label htmlFor="search">Search</label><inputid="search"value={query}onChange={(event) => setQuery(event.target.value)}/>{status === 'loading' && <p>Loading...</p>}{showEmptyState && <p>No results found.</p>}{hasResults && (<ul>{results.map((item) => (<li key={item.id}>{item.label}</li>))}</ul>)}</div>);}
This is the skeleton, not the final solution. Once it works, add debouncing, request cancellation, keyboard navigation, caching, highlighted matches, and accessibility improvements.
Sequence matters. Build the smallest correct version, then layer complexity.
Once the skeleton works, upgrade it in this order:
The stale response bug shows up often. Suppose the user types rea, then quickly types react. If the rea request returns after the react request, a naive implementation can show the wrong results.
One simple defense is to track the latest query inside the effect:
useEffect(() => {if (query.trim() === '') {setStatus('idle');setResults([]);return;}let ignore = false;setStatus('loading');fetchResults(query).then((items) => {if (!ignore) {setResults(items);setStatus('success');}}).catch((error) => {if (!ignore) {setError(error);setStatus('error');}});return () => {ignore = true;};}, [query]);
This is not the only fix. It does show that you understand the browser reality: users type quickly, networks are unpredictable, and UI state must not be overwritten by stale async work.
If time is short, avoid random practice. Focus on patterns that repeat across many rounds.
Start with questions that force you to manage local state cleanly:
Start here because the requirements are small and the component-design lessons repeat everywhere.
Then move to questions involving fetched data, sorting, filtering, pagination, and loading states:
These questions feel closer to everyday product work.
Finally, practice questions with more moving parts:
These expose gaps in event handling, timers, focus management, and state transitions.
For a broader list, use 50 React coding interview questions with solutions and prioritize the ones that match your target roles.
Practice only works when you review the attempt honestly. After solving a question on GreatFrontEnd, compare your solution with the official solution and ask:
Keep a small mistake log. When three attempts show the same issue, that is your next focus area. If your components repeatedly become too large, practice extracting presentational subcomponents. If async bugs keep appearing, practice debouncing, cleanup functions, request cancellation, and loading/error states.
Machine coding round preparation should look like deliberate repetition, not passive reading.
Pick simple components and implement them without looking at solutions. Focus on:
Good questions for this stage: todo list, tabs, accordion, contact form, progress bar, and modal.
Move into data tables, job boards, autocomplete-style flows, and file explorers. Focus on:
This is where frontend machine coding round questions start to feel realistic.
Use a timer. Give yourself 60 minutes for medium questions and 90 minutes for harder ones.
After each attempt, review:
As you practice, explain your approach out loud. Interviewers are evaluating code and judgment.
Follow-ups are where interviewers test whether your implementation is extensible. After completing a question, add one extra requirement:
You are not trying to memorize every feature. You are checking whether your original structure makes follow-ups easy. If adding one small requirement forces a rewrite, the initial design was probably too rigid.
Before your next machine coding round, remember this:
When you feel stuck, return to the MVP. A small working solution beats an ambitious broken one.
Frontend LLD focuses on component APIs, state ownership, extensibility, and interaction contracts. A machine coding round asks you to implement the component or app live. The two overlap, but machine coding is more implementation-heavy.
No. Frontend machine coding round questions can be solved with vanilla JavaScript, React, Vue, Angular, or another framework. For React roles, practice in React because the interviewer will expect component-driven thinking and hook-based state management.
Practice fewer questions more deeply. Ten well-reviewed questions are better than fifty shallow attempts. A strong base includes todo list, tabs, accordion, modal, data table, file explorer, job board, progress bars, image carousel, and one autocomplete-style problem.
Use a platform where you can write code, run it, compare with solutions, and practice realistic UI prompts. Start with React interview questions on GreatFrontEnd, then move into specific user interface coding questions.
The machine coding round is an execution test. You do not need a perfect production app in 90 minutes. You need to understand the requirements, break down the UI, design state, implement the core behavior, handle the right edge cases, and communicate trade-offs.
That is trainable. Pick one question, set a timer, build the MVP, review your mistakes, and repeat. The round gets easier once your process is stronger than the pressure of the clock.

As Front End Engineers, we aim to deliver the best user experience and one of the ways to achieve that is by optimizing the applications' performance.
Users expect fast, responsive experiences, and will quickly abandon sites that are slow to load. Google's research found that 53% of mobile users abandon a page that takes more than 3 seconds to load. With the prevalent usage of mobile devices which can be on slower network speeds, optimizing performance is critical.
Code splitting and lazy loading are effective strategies to achieve great performance on the web. In this post, we’ll explore these techniques, their benefits, and how they can be implemented in React.
Code splitting breaks down your application into smaller chunks, loading only the necessary parts to reduce the bundle size. Lazy loading defers loading non-essential resources until they’re needed, further enhancing performance.
For example, consider a React app with a Login, Dashboard, and Listing page. Traditionally, the code for all these pages are bundled in a single JS file. This is suboptimal because when the user visits the Login page, it is unnecessary to load pages such as the Dashboard and Listing page. But with implementing code splitting and lazy loading, we can dynamically load specific components/pages only when needed, significantly improving performance.
In React, code splitting can be introduced via dynamic import(). Dynamic import is a built-in way to do this in JavaScript. The syntax looks like this:
import('./math').then((math) => {console.log(math.add(1, 2));});
For React apps, code splitting via dynamic imports works out of the box with modern toolchains like Vite, Next.js, and React Router. The React.lazy() function (introduced in React 16.6) lets you render a dynamic import as a regular component, splitting a large JS bundle into smaller chunks that load on demand.
If you're on a custom Webpack setup, refer to the Webpack guide for configuring code splitting.
To implement lazy loading in React, we can leverage React.lazy function and the Suspense component to handle loading states. Here's an example demonstrating lazy loading in React:
const LazyComponent = React.lazy(() => import('./LazyComponent'));function App() {return (<React.Suspense fallback={<div>Loading...</div>}><LazyComponent /></React.Suspense>);}
By wrapping a lazy-loaded component with Suspense, we can provide a fallback/placeholder UI while the component is being loaded asynchronously, such as a spinner.
However, there can be a case where LazyComponent fails to load due to some reason like network failure. In that case, it needs to handle the error smoothly for a better user experience with Error Boundaries.
import MyErrorBoundary from './MyErrorBoundary';const LazyComponent = React.lazy(() => import('./LazyComponent'));function App() {return (<MyErrorBoundary><React.Suspense fallback={<div>Loading...</div>}><LazyComponent /></React.Suspense></MyErrorBoundary>);}
So, when the LazyComponent is lazily loaded, it signifies that the code for LazyComponent is segmented into a distinct JS chunk, separate from the main JS bundle. This JS chunk is exclusively loaded when the LazyComponent is required to be displayed on the user interface, optimizing the loading process and enhancing the application's performance.
Note: Since React 18, React.lazy and Suspense work on the server too via streaming SSR APIs (renderToPipeableStream for Node.js, renderToReadableStream for Edge). Frameworks like Next.js handle this for you. The third-party @loadable/component library was the pre-React-18 workaround and is rarely needed today.
From the above, we have seen how we use React.lazy to code split and lazy load components. But the question is where to lazy load and code split. There are approaches like Route-based code splitting and Component-based code splitting.

Route-based code splitting is almost always the best place to start. It typically gives the largest reduction in initial JS, since each route ends up as its own chunk and only the active one is loaded. Modern bundlers (Webpack 5, Vite/Rollup, Turbopack) automatically extract shared dependencies into common chunks, so you don't usually have to worry about duplication across routes. Just check your build output occasionally to confirm shared code lives in vendor chunks rather than being inlined into every route bundle.
Here is an example of route-based code splitting:
import { Suspense, lazy } from 'react';import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';const Login = lazy(() => import('./Login'));const Dashboard = lazy(() => import('./Dashboard'));const App = () => (<Router><Suspense fallback={<div>Loading...</div>}><Routes><Route path="/" element={<Login />} /><Route path="/dashboard" element={<Dashboard />} /></Routes></Suspense></Router>);

Component-based code splitting provides granular control over loading specific components, allowing for more precise optimization. The real power of code splitting comes into the picture in component-based code splitting where we have more control over granular components. When deciding which components to lazy load, consider the importance and impact of each component on the initial rendering and user experience. Ideal candidates for lazy loading are large components with significant code or resources, conditional components that are not always needed, and secondary or non-essential features. These can be segmented into separate chunks and loaded on demand, optimizing performance. However, critical components like headers, main content, and dependencies should be loaded upfront to ensure a seamless user experience. We need to be careful in selecting which components to lazy load to strike a balance between initial load times and providing essential functionality. Here is an example of component-based code splitting:
import { useState, lazy, Suspense } from 'react';const Modal = lazy(() => import('./Modal'));function App() {const [showModal, setShowModal] = useState(false);const openModal = () => {setShowModal(true);};const closeModal = () => {setShowModal(false);};return (<div><button onClick={openModal}>Open Modal</button>{showModal && (<Suspense fallback={<div>Loading Modal...</div>}><Modal onClose={closeModal} /></Suspense>)}</div>);}export default App;
In this example, the Modal component is lazily loaded using React.lazy() and dynamically imported. The modal is conditionally rendered based on the showModal state, which is toggled by the openModal and closeModal functions. The Suspense component displays a loading indicator while the modal component is being loaded asynchronously. This implementation optimizes performance by loading the modal component only when the user interacts with the Open Modal button, preventing unnecessary loading of heavy components like a text editor until they are actually needed.
If you’re using Webpack to bundle your application, then you can use Webpack's magic comments to further improve the user experience with lazy loading.
We can use webpackPrefetch and webpackPreload for dynamic imports. In the above example of the lazy loading Modal, the Modal is loaded only when the user clicks the Open Modal button and the user has to wait for a fraction of a second to load the Modal.
We can improve the user experience by not making users wait for the Modal to load. So, in that scenario, we can prefetch or preload the Modal component. In the above example of the Lazy loading modal, the only difference will be in how we import the Modal component.
Before:
const Modal = lazy(() => import('./Modal'));
After:
const Modal = lazy(() => import(/* webpackPrefetch: true */ './Modal'));
What webpackPrefetch: true does is that it tells the browser to automatically load this component into the browser cache so it's ready ahead of time and the user won’t have to wait for the Modal component to load when the user clicks on the Open Modal button.
We can use webpackPrefetch and webpackPreload for a particular component when we think that there is a high possibility for the user to use that component when a user visits the app.
Note that magic comments are Webpack-specific. Vite (Rollup) and Turbopack don't honor them. If you're on Next.js, route chunks are prefetched automatically by <Link>, but next/dynamic itself has no prefetch option, so for high-probability interactions you typically trigger the dynamic import() yourself on hover or focus. On Vite, you can use <link rel="prefetch"> directly or a plugin like vite-plugin-preload.
React.lazyFor a long time, Suspense was mostly known as the fallback for React.lazy. Two newer features extend what it can do: streaming SSR (added in React 18) and the use() hook (added in React 19). Both work with the same Suspense boundaries you already place for lazy loading, so a single boundary can show a fallback while a chunk downloads, while data resolves, and while the server streams the rest of the tree.
use() hook with SuspenseThe use() hook lets a component unwrap a promise (or read context). React suspends the component until the promise resolves, so you don't need a useEffect or a manual loading state. It works in both server and client components, but the most common pattern is unwrapping a promise in a client component.
'use client';import { use, Suspense } from 'react';import ErrorBoundary from './ErrorBoundary';function Profile({ userPromise }) {// React suspends here until userPromise resolves.const user = use(userPromise);return <h1>Hello, {user.name}</h1>;}export default function ProfilePage({ userPromise }) {return (<ErrorBoundary fallback={<p>Failed to load profile</p>}><Suspense fallback={<p>Loading...</p>}><Profile userPromise={userPromise} /></Suspense></ErrorBoundary>);}
The typical pattern is to start the fetch in a server component (or route loader), pass the unresolved promise down to a client component, and let use() handle the suspending. The request is in-flight before the client component renders, so the user sees the resolved UI sooner than if the client had to fetch on mount.
Streaming SSR with Suspense has been available since React 18, via renderToPipeableStream (Node.js) or renderToReadableStream (Edge). When a Suspense boundary suspends on the server, the server doesn't block. It sends the fallback HTML immediately and streams the resolved content as soon as it's ready. This is how Next.js App Router's loading.tsx works.
For code splitting, a route-level Suspense boundary lets the server flush the shell, navigation, and skeleton early, then stream in the lazy-loaded sections as their chunks resolve. First Contentful Paint (FCP) usually improves as a result.
React.lazyReact Server Components (RSC) change which components even need code splitting. Server Components run only on the server and are never sent as JavaScript to the browser, so they don't add to the client bundle. You can't (and shouldn't) wrap them in React.lazy.
A short reference for what to split:
| Component type | Ships JS to browser? | Use React.lazy? |
|---|---|---|
| Server Component (default in App Router) | No | No, it's already not in the bundle |
Client Component ('use client') used everywhere | Yes | Optional, split when it's heavy or below-the-fold |
| Client Component used conditionally (modal, chart, editor) | Yes | Yes, high-impact split |
| Route component | Yes (the route's bundle) | The router handles this for you in App Router |
The Next.js App Router does route-based splitting automatically. Every page.tsx is its own bundle, so you only need explicit React.lazy (or next/dynamic) for client components that are heavy and not always rendered: rich-text editors, charting libraries, video players, or feature-flag-gated UI.
The rule of thumb for modern React apps: default to Server Components to keep work off the client, and use React.lazy or next/dynamic for heavy client components that render conditionally.
It depends on the initial bundle size and how much of it is unused on first paint. A few useful reference points:
Three metrics to measure before and after a split:
web-vitals library or the Chrome User Experience Report.If none of them move, the split isn't worth the complexity.
When asked "how would you optimize bundle size in a React app?", these are the mistakes that come up most often:
React.lazy for Server Components. In the App Router, Server Components aren't in the client bundle. Wrapping one in React.lazy or next/dynamic is a no-op at best and an error at worst. Use splitting only for client components with significant code.import() as instant. A lazy import is a network request. If the user clicks "Open editor" and waits 800 ms on a spinner, lazy loading has made things worse. Combine it with prefetching (webpackPrefetch on Webpack, or triggering the dynamic import() on hover/focus on other bundlers) for high-probability interactions.Be sure to assess your application's requirements, tech stack and challenges when deciding the code splitting and lazy loading approach. By strategically dividing code and loading resources on demand, you can create fast, efficient, and engaging web applications.
To strengthen your React fundamentals further, check out our Top ReactJS Interview Questions GitHub repo - a curated collection of 50 frequently asked questions from real interview scenarios.

Headless UI libraries handle the logic, accessibility, and interaction behavior of UI components but leave the styling to you. You bring your own styles or design system, and the library handles keyboard navigation, focus management, ARIA semantics, RTL support, and controlled vs uncontrolled state.
A couple of things have shifted since 2024:
The libraries below are ordered by npm weekly downloads, with shadcn/ui covered last.
These libraries don't all sit at the same layer of your stack:
Picking shadcn/ui doesn't mean skipping the primitive layer. Radix UI (or Base UI, since 2025) still lives in your node_modules underneath your components/ui/ folder. Picking React Aria means writing more code per component, but it gives you the deepest accessibility primitives available.

Headless UI is built by the Tailwind CSS team. It's a small set of unstyled, accessible components designed to compose with Tailwind utilities. The component count is intentionally small (~10 components) and the API is the most beginner-friendly in this list.
By the numbers (as of May 2026):
@headlessui/react): ~5.49MBest for: Tailwind-first teams that want a familiar, opinionated API and don't need the composability of Radix-style primitives.
Skip if: You need components beyond what's covered (no Combobox-with-virtualization, no advanced data table primitives), or you want more composability. Radix and Base UI both expose more primitives.

React Aria by Adobe is the most accessibility-rigorous option in this list. It's a library of React Hooks (rather than components) that handle behavior, ARIA semantics, internationalization, and adaptive interactions across 40+ component patterns.
You compose hooks (useButton, useDialog, useTabs) into your own components, which means more code per component but more control over the rendered output. Worth the investment when accessibility is a hard requirement.
By the numbers (as of May 2026):
react-aria): ~4.47MBest for: Government, enterprise, or any product where WAI-ARIA conformance is contractually required. Also when you need internationalization (RTL, locale-aware date/number components) out of the box.
Skip if: You're shipping a v0 product and want pre-built components. React Aria's hooks-first model is more verbose than Radix or Base UI.

Radix UI is the original primitive library that popularized headless components in React. It provides 30+ unstyled, accessible components (Dialog, Dropdown, Tabs, Popover, etc.) with full keyboard navigation, focus management, ARIA, and RTL support built in.
Radix UI was acquired by WorkOS, and update velocity has slowed for some complex components (Combobox and multi-select being the ones commonly cited). The primitives still see heavy usage through shadcn/ui. @radix-ui/react-slot alone pulls ~131M weekly npm downloads as of mid-2026.
By the numbers (as of May 2026):
Best for: When you want pre-built accessible primitives without the copy-paste of shadcn/ui, and you already have your own styling solution (CSS Modules, Emotion, vanilla CSS, etc.).
Skip if: You need brand-new primitives that haven't shipped yet. Base UI is iterating faster on those.

Base UI is the actively maintained alternative to Radix for the primitive layer. Maintained by MUI with full-time engineers, it provides similar headless primitives with a slightly different API.
shadcn/ui added Base UI as a supported primitive layer in 2025, so you can now choose between Radix-backed and Base UI-backed shadcn components.
By the numbers (as of May 2026):
@base-ui/react): ~3.7MBest for: Greenfield projects where you want the most actively maintained primitive layer, or anyone who hit Radix limitations on a complex component (Combobox, multi-select).
Skip if: You're already deep in a Radix codebase and migration cost is high. Radix still works, it just isn't moving as fast.

Aria Kit is an open-source library of unstyled, primitive components and hooks for accessible web apps. It ships smaller bundles than most for what you get, with an API that sits between Radix's component composition and React Aria's hooks-first style.
By the numbers (as of May 2026):
@ariakit/react): ~697.9kBest for: When bundle size matters more than ecosystem familiarity. Often picked by component library authors building their own libraries on top of a primitive layer.
Skip if: You want the largest community and Stack Overflow surface area. Aria Kit is smaller than Radix and React Aria.

Ark UI is unique in this list: same headless primitive philosophy, but the components work across React, Vue, and Solid. The internal logic is built with state machines (XState) for predictable behavior on complex components.
By the numbers (as of May 2026):
@ark-ui/react): ~634.7kBest for: Multi-framework design systems where you need the same Combobox behavior in a React app and a Vue app and want to maintain one logic implementation.
Skip if: You're React-only. The React-specific options above usually have more idiomatic React APIs.

shadcn/ui isn't strictly a headless library since its components ship with Tailwind styles applied. We're including it because it's how most teams now consume Radix or Base UI primitives in production.
It's a CLI that copies pre-built component source code into your project. The components are built on Radix UI primitives by default (with Base UI as an opt-in alternative since 2025) and styled with Tailwind CSS.
The mental model is different from a typical npm package:
npm install shadcn-ui and import components.npx shadcn add button and the source code for the Button component is added to your repo at components/ui/button.tsx.You trade dependency management for full control over the code, including the option to strip the Tailwind classes and treat the underlying Radix or Base UI layer as truly headless. That trade-off is why it has taken over so much of the React UI ecosystem.
By the numbers (as of May 2026):
Best for: New product apps where you want full design control on top of headless primitives without writing accessibility logic from scratch, especially if you're already using Tailwind.
Skip if: You can't use Tailwind, want a strictly headless library, or want a published library you can npm update (you don't get that with shadcn/ui).

Any of the libraries above can be used to ship an accessible, maintainable app. Pick the one that matches your styling preferences and how much code you're willing to write per component.
Want to solidify your React foundation while exploring UI libraries like these? Check out our Top ReactJS Interview Questions GitHub repo with 50 frequently asked questions to help you prepare for projects and interviews.

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

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

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

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

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

Preparing for a React interview can be daunting, but having access to the right questions can make all the difference. We've created a prioritized list of 100+ questions and solutions, covering essential topics like React fundamentals, React Hooks, React Router, internationalization in React, testing of React apps, and the latest React 19 features.
This comprehensive guide is designed to help you prepare effectively, boost your confidence, and ensure you make a strong impression during your interview.
What's new in the May 2026 update
- 10 new questions covering React 19: Actions,
useActionState,useOptimistic, theusehook, Server Components, the React Compiler, and form actions, in the "React 19 and modern React" section below.- Existing answers updated so function components and hooks are the default, and class-based patterns are flagged where they're now legacy.
- All code samples checked against React 19.
If you're looking for more in-depth React interview preparation materials, also check out these resources:
Mastering React fundamentals is crucial in front end interviews because most companies rely on React for building modern web applications, and interview questions often test your ability to reason about components, state management, and data flow. A solid grasp of these core concepts is an important step to achieving interview success.
React is a JavaScript library developed by Facebook for creating user interfaces, particularly in single-page applications. It enables the use of reusable components that manage their own state. Key advantages include a component-driven architecture, optimized updates through the virtual DOM, a declarative approach for better readability, and robust community backing.

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

Find in-depth explanations and track study progress here ->
The virtual DOM is a simplified version of the actual DOM used by React. It allows for efficient UI updates by comparing the virtual DOM to the real DOM and making only the necessary changes through a process known as reconciliation.
Find in-depth explanations and track study progress here ->
The virtual DOM in React is an in-memory representation of the real DOM. When state or props change, React creates a new virtual DOM tree, compares it to the previous one using a diffing algorithm, and efficiently updates only the parts of the real DOM that changed.
Find in-depth explanations and track study progress here ->
A React Node refers to any unit that can be rendered in React, such as an element, string, number, or null. A React Element is an immutable object that defines what should be rendered, typically created using JSX or React.createElement. A React Component is either a function or class that returns React Elements, enabling the creation of reusable UI components.
Find in-depth explanations and track study progress here ->
React Fragments allow you to group multiple elements without adding extra nodes to the DOM. They are particularly useful when you need to return multiple elements from a component but don't want to wrap them in a container element. You can utilize shorthand syntax <>...</> or React.Fragment.
return (<><ChildComponent1 /><ChildComponent2 /></>);
Find in-depth explanations and track study progress here ->
key prop in React?In React, the key prop is used to uniquely identify elements in a list, allowing React to optimize rendering by updating and reordering items more efficiently. Without unique keys, React might re-render elements unnecessarily, causing performance problems and potential bugs.
{items.map((item) => <ListItem key={item.id} value={item.value} />);}
Find in-depth explanations and track study progress here ->
Using array indices as keys can lead to performance issues and unexpected behavior, especially when reordering or deleting items. React relies on keys to identify elements uniquely, and using indices can cause components to be re-rendered unnecessarily or display incorrect data.
Find in-depth explanations and track study progress here ->
Props (short for properties) are inputs to React components that allow you to pass data from a parent component to a child component. They are immutable and are used to configure a component. In contrast, state is internal to a component and can change over time, typically due to user interactions or other events.
Find in-depth explanations and track study progress here ->
Class components are ES6 classes that extend React.Component and rely on lifecycle methods (componentDidMount, componentDidUpdate, etc.) and this.state. Function components are plain functions that take props as input and return JSX, and use hooks (useState, useEffect, useRef, etc.) for state and side effects. Since hooks landed in React 16.8, function components are the default for new code; class components are kept for backward compatibility and are no longer the recommended pattern.
Default to function components. Class components are legacy: new APIs like Suspense data fetching, the use hook, Actions, Server Components, and the React Compiler are designed for function components only. The one remaining reason to write a class today is implementing an error boundary, which still requires static getDerivedStateFromError / componentDidCatch.
React Fiber is a complete rewrite of the React core algorithm, designed to improve performance and enable new features like async rendering, error boundaries, and incremental rendering. It breaks down the rendering process into smaller chunks, allowing React to pause, abort, or prioritize updates as needed.
Find in-depth explanations and track study progress here ->
Reconciliation is the process by which React updates the DOM to match the virtual DOM efficiently. It involves comparing the new virtual DOM tree with the previous one and determining the minimum number of changes required to update the actual DOM. This process ensures optimal performance by avoiding unnecessary re-renders.

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

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

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

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

Find in-depth explanations and track study progress here ->
Context in React can lead to performance issues if not handled carefully, causing unnecessary re-renders of components that consume the context, even if only part of the context changes. Overusing context for state management can also make the code harder to maintain and understand. It's best to use context sparingly and consider other state management solutions like Redux or Zustand for more complex scenarios.
Find in-depth explanations and track study progress here ->
React anti-patterns are practices that can lead to inefficient or hard-to-maintain code. Common examples include:
useEffect to derive state from props (compute it during render instead)useReducer or a state libraryuseState for values that don't drive rendering (use useRef instead)The older class-component anti-patterns (using componentWillMount for data fetching or relying on componentWillReceiveProps) refer to APIs that were renamed to UNSAFE_* and no longer apply to function-component code.
Find in-depth explanations and track study progress here ->
Choosing between React state, context, and external state managers depends on your application's complexity. Use React state for local component state, context for global state shared across multiple components, and external managers like Redux or MobX for complex state management requiring advanced features like optimizing re-renders.
Find in-depth explanations and track study progress here ->
setState is called in React?When setState is called in React:
Example:
function Counter() {const [count, setCount] = React.useState(0);const increment = () => {setCount(count + 1); // Calls setState to update state};return <button onClick={increment}>Count: {count}</button>;}
In this example, calling setState (via setCount) triggers a re-render with the updated count.
Prop drilling is when you pass data from a parent component to a deeply nested child component through props, even if intermediate components don't use it.
Example:
function Grandparent() {const data = 'Hello from Grandparent';return <Parent data={data} />;}function Parent({ data }) {return <Child data={data} />;}function Child({ data }) {return <p>{data}</p>;}
In this example, data is passed through multiple components, even though only the Child component uses it. Prop drilling is acceptable for small applications where the component hierarchy is shallow. When global state is needed to be accessed in deeper levels of the app, using context and/or external state managers might be better.
Lazy loading in React is a technique where components are loaded only when they are needed, rather than at the initial page load. This helps reduce the initial load time and improve performance by splitting the code into smaller chunks.
Example:
import React, { Suspense, lazy } from 'react';const LazyComponent = lazy(() => import('./LazyComponent'));function App() {return (<Suspense fallback={<div>Loading...</div>}><LazyComponent /></Suspense>);}
In this example, LazyComponent is loaded only when it's rendered, and while loading, a fallback UI (Loading...) is displayed.
Synthetic events in React are a wrapper around native DOM events that ensure consistent behavior across browsers. They normalize the way events are handled, providing a unified API for React applications.
These events are wrapped in the SyntheticEvent object and expose the usual methods like preventDefault() and stopPropagation(). Since React 17, the root event listener is attached to the React root container (not document), which makes nested React trees work correctly together.
Example:
function MyComponent() {const handleClick = (event) => {event.preventDefault();console.log('Button clicked');};return <button onClick={handleClick}>Click me</button>;}
Older sources mention event pooling, where React reused the event object after the handler ran, which made the event unusable in async code. Event pooling was removed in React 17, so you can read or pass the event object asynchronously without calling event.persist().
Class lifecycle methods only apply to class components, which are no longer the recommended pattern. The function-component equivalents (using useEffect) are shown at the end.
React class components have lifecycle methods for different phases:
constructor: Initializes state or binds methodscomponentDidMount: Runs after the component mounts, useful for API calls or subscriptionscomponentDidMount() {console.log('Component mounted');}
shouldComponentUpdate: Determines if the component should re-rendercomponentDidUpdate: Runs after updates, useful for side effectscomponentWillUnmount: Cleans up (e.g., removing event listeners).componentWillUnmount() {console.log('Component will unmount');}
In function components, all of the above are expressed with useEffect:
useEffect(() => {// componentDidMount + componentDidUpdateconsole.log('Mounted or updated');return () => {// componentWillUnmountconsole.log('Will unmount');};},[/* deps */],);
Concurrent features were introduced in React 18 (the experimental "Concurrent Mode" branding from React 17 is no longer used). They let React pause, interrupt, and resume rendering work instead of running it as a single blocking pass. This keeps the UI responsive: urgent updates like typing or clicks can preempt slower work like rendering a large list or filtering search results.
The features are opt-in via specific APIs (useTransition, useDeferredValue, and <Suspense> for data fetching), not a global mode switch.
React's scheduler assigns priority to updates based on how they're triggered. Updates from direct user interaction (click, input, focus) are treated as urgent and rendered synchronously. Updates wrapped in startTransition or read through useDeferredValue are non-urgent: React can interrupt them to handle a more urgent update, then resume. That's what allows a heavy filter or list render to coexist with typing into a search box without blocking it.
To avoid blocking the UI, use Web Workers, setTimeout, or requestIdleCallback for offloading heavy computations. Alternatively, break tasks into smaller parts and use React's Suspense or useMemo to only recompute when necessary.
Example using setTimeout for deferring computation:
const [data, setData] = useState(null);useEffect(() => {setTimeout(() => {const result = computeExpensiveData();setData(result);}, 0);}, []);
Server-side rendering (SSR) involves rendering components on the server before sending fully rendered HTML to clients, improving initial load times and SEO through efficient hydration processes.

Find in-depth explanations and track study progress here ->
Static generation pre-renders HTML at build time instead of runtime; this approach enhances performance by delivering static content quickly while improving SEO outcomes.
Find in-depth explanations and track study progress here ->
Higher-order components (HOCs) are functions that take a component and return a new one with added props or behavior, facilitating logic reuse across components.
const withExtraProps = (WrappedComponent) => {return (props) => <WrappedComponent {...props} extraProp="value" />;};const EnhancedComponent = withExtraProps(MyComponent);
HOCs were the dominant pattern for cross-cutting logic (auth, data fetching, theming) before hooks. Custom hooks cover almost all of those use cases now without the wrapper-component nesting. HOCs still appear in older codebases and some libraries (e.g., connect from react-redux), but new code should prefer a custom hook.
Find in-depth explanations and track study progress here ->
The presentational vs container pattern split components into two roles: presentational components handled rendering (markup, styling) and received data via props, while container components handled state, data fetching, and behavior, then passed data down.
// Container: handles state/datafunction UserListContainer() {const [users, setUsers] = useState([]);useEffect(() => {fetchUsers().then(setUsers);}, []);return <UserList users={users} />;}// Presentational: pure renderingfunction UserList({ users }) {return (<ul>{users.map((u) => (<li key={u.id}>{u.name}</li>))}</ul>);}
This pattern was popular before hooks; its original author (Dan Abramov) has since said it's no longer worth following as a hard rule. With hooks, the same separation is usually expressed by extracting a custom hook (e.g., useUsers()) rather than a wrapper component. New code typically blends the two roles into a single component plus a custom hook.
Find in-depth explanations and track study progress here ->
Render props in React allow code sharing between components through a prop that is a function. This function returns a React element, enabling data to be passed to child components.
function DataFetcher({ url, render }) {const [data, setData] = useState(null);useEffect(() => {fetch(url).then((res) => res.json()).then(setData);}, [url]);return render(data);}// Usage<DataFetcherurl="/api/data"render={(data) => <div>{data ? data.name : 'Loading...'}</div>}/>;
Like HOCs, render props were a pre-hooks solution for sharing stateful logic. Most of those use cases are now solved with a custom hook (const data = useFetch(url)), which composes more naturally and avoids the render-prop pyramid. Render props are still useful in narrow cases where the consumer needs to control what to render based on parent-managed state (e.g., headless component libraries).
Find in-depth explanations and track study progress here ->
The composition pattern in React involves building components by combining smaller, reusable ones instead of using inheritance. This encourages creating complex UIs by passing components as children or props.
function WelcomeDialog() {return (<Dialog><h1>Welcome</h1><p>Thank you for visiting our spacecraft!</p></Dialog>);}function Dialog(props) {return <div className="dialog">{props.children}</div>;}
Find in-depth explanations and track study progress here ->
To re-render the view on browser resize, use the useEffect hook to listen for the resize event and update state.
Example:
import React, { useState, useEffect } from 'react';function ResizeComponent() {const [windowWidth, setWindowWidth] = useState(window.innerWidth);useEffect(() => {const handleResize = () => setWindowWidth(window.innerWidth);window.addEventListener('resize', handleResize);return () => window.removeEventListener('resize', handleResize);}, []);return <div>Window width: {windowWidth}px</div>;}export default ResizeComponent;
This updates the state and re-renders the component whenever the window is resized.
Asynchronous data loading uses useEffect alongside useState hooks; fetching data inside useEffect updates state with fetched results ensuring re-renders occur with new data.
import React, { useState, useEffect } from 'react';const FetchAndDisplayData = () => {const [info, updateInfo] = useState(null);const [isLoading, toggleLoading] = useState(true);useEffect(() => {const retrieveData = async () => {try {const res = await fetch('https://api.example.com/data');const data = await res.json();updateInfo(data);} catch (err) {console.error('Error fetching data:', err);} finally {toggleLoading(false);}};retrieveData();}, []);return (<div>{isLoading ? (<p>Fetching data, please wait...</p>) : (<pre>{JSON.stringify(info, null, 2)}</pre>)}</div>);};export default FetchAndDisplayData;
Find in-depth explanations and track study progress here ->
Common pitfalls in data fetching with React include failing to handle loading and error states, neglecting to clean up subscriptions which can cause memory leaks, and improperly using lifecycle methods or hooks. Always ensure proper handling of these states, clean up after components, and utilize useEffect for side effects in functional components.
Find in-depth explanations and track study progress here ->
Understanding React Router is important in front end interviews because most real-world applications need client-side routing to handle navigation, dynamic URLs, and nested layouts. Proficiency with routing shows you can structure applications effectively and provide a seamless user experience.
React Router is a popular routing library for React applications that enables navigation between different components based on the URL. It provides declarative routing, allowing you to define routes and their corresponding components in a straightforward manner.
React Router maps URL paths to components, enabling navigation in single-page apps. Dynamic routing allows you to use URL parameters to render components based on dynamic values.
import { BrowserRouter, Routes, Route, useParams } from 'react-router-dom';function UserPage() {const { id } = useParams(); // Access dynamic parameterreturn <h1>User ID: {id}</h1>;}export default function App() {return (<BrowserRouter><Routes><Route path="/user/:id" element={<UserPage />} /> {/* Dynamic path */}</Routes></BrowserRouter>);}
Key features:
:id captures dynamic data from the URL.useParams Hook: Accesses these dynamic values for rendering.Nested routes allow you to create hierarchies of components, and useParams helps access dynamic route parameters.
Key techniques:
<Outlet>: Renders child routes within a parent layoutuseParams: Retrieves route parameters for dynamic routingimport {BrowserRouter,Routes,Route,Outlet,useParams,} from 'react-router-dom';function UserProfile() {const { userId } = useParams();return <h2>User ID: {userId}</h2>;}function App() {return (<BrowserRouter><Routes><Route path="user/:userId" element={<Outlet />}><Route path="profile" element={<UserProfile />} /></Route></Routes></BrowserRouter>);}
BrowserRouter: Uses the HTML5 History API to manage navigation, enabling clean URLs without the hash (#). It requires server-side configuration to handle routes correctly, especially for deep linking.
HashRouter: Uses the hash (#) portion of the URL to simulate navigation. It doesn't require server-side configuration, as the hash is never sent to the server. This makes it suitable for environments where server-side routing isn't possible (e.g., static hosting).
React Router is a routing library for React that provides a declarative API for defining routes and handling navigation. It manages components and URLs.
History library is a lower-level utility that only manages browser history (e.g., pushing and popping history entries). It doesn't handle UI rendering or routing, making it more generic and not React-specific.
React Router uses the history library internally but adds additional features like routing and component management.
<Router> components of React Router v6?In React Router v6, the key <Router> components are:
<BrowserRouter>: Uses the HTML5 history API to keep the UI in sync with the URL. It's commonly used for web applications.<HashRouter>: Uses URL hash fragments (#) to manage routing, making it suitable for static file hosting or legacy browsers that don't support the HTML5 history API.<MemoryRouter>: Keeps the URL in memory (no address bar changes), useful for non-browser environments like tests or embedded apps.<StaticRouter>: Used for server-side rendering (SSR), where routing is handled without a browser, typically in Node.js environments.Each of these routers serves different use cases but provides the same routing functionality within a React app.
The push and replace methods of the history library are used to manage the browser's history stack and control navigation.
push:history.push('/new-page')replace:history.replace('/new-page')In React Router v6, you can navigate programmatically by using the useNavigate hook. First, import useNavigate from react-router-dom and call it to get the navigate function. Then, you can use navigate('/new-page') to navigate to a different route.
For example:
import { useNavigate } from 'react-router-dom';function MyComponent() {const navigate = useNavigate();const goToPage = () => navigate('/new-page');return <button onClick={goToPage}>Go to New Page</button>;}
In React Router v5, the useHistory hook provides access to the history object, which you can use to push a new route. For example, history.push('/new-page') will navigate to the specified route.
For example:
import { useHistory } from 'react-router-dom';function MyComponent() {const history = useHistory();const goToPage = () => history.push('/new-page');return <button onClick={goToPage}>Go to New Page</button>;}
Both methods allow you to navigate programmatically in React Router.
To implement private routes, create a component that checks if the user is authenticated before rendering the desired route.
Example:
import { Navigate } from 'react-router-dom';function PrivateRoute({ children }) {return isAuthenticated ? children : <Navigate to="/login" />;}
PrivateRoute: Checks authentication and either renders the children (protected routes) or redirects to the login page.<Navigate>: Replaces the deprecated <Redirect> for redirecting in React Router v6+.Use the useLocation hook to get the current route, and conditionally apply styles for the active state.
Example:
import { useLocation } from 'react-router-dom';function NavBar() {const location = useLocation();return (<nav><ul><li className={location.pathname === '/home' ? 'active' : ''}>Home</li><li className={location.pathname === '/about' ? 'active' : ''}>About</li></ul></nav>);}
To handle 404 errors or page not found in React Router, create a catch-all route at the end of your route configuration that renders a custom 404 component.
Example:
import { Routes, Route } from 'react-router-dom';function NotFound() {return <h1>404 - Page Not Found</h1>;}function App() {return (<Routes><Route path="/" element={<Home />} /><Route path="/about" element={<About />} /><Route path="*" element={<NotFound />} /></Routes>);}
In this example, the NotFound component is rendered when no other routes match the URL, indicating a 404 error.
In React Router v6, you can use the useSearchParams hook to access query parameters from the URL.
Example:
import { useSearchParams } from 'react-router-dom';function MyComponent() {const [searchParams] = useSearchParams();const queryParam = searchParams.get('paramName');return <div>Query Param: {queryParam}</div>;}
This hook allows you to retrieve and manipulate query parameters in React Router v6.
To perform an automatic redirect after login in React Router, use the useNavigate hook to navigate to the desired route after successful authentication.
Example:
import { useNavigate } from 'react-router-dom';function Login() {const navigate = useNavigate();const handleLogin = () => {// Perform login logicnavigate('/dashboard');};return (<div><button onClick={handleLogin}>Login</button></div>);}
In this example, the handleLogin function navigates to the /dashboard route after successful login.
In React Router v6, you can pass props to a route component using the element prop in the <Route> component.
Example:
import { Routes, Route } from 'react-router-dom';function MyComponent({ propValue }) {return <div>Prop Value: {propValue}</div>;}function App() {return (<Routes><Route path="/my-route" element={<MyComponent propValue="Hello" />} /></Routes>);}
In this example, the propValue prop is passed to the MyComponent component when rendering the /my-route route.
Understanding internationalization (i18n) in React is important in front end interviews because many products serve global audiences and must support multiple languages and locales. Showing you can implement i18n demonstrates attention to accessibility, user experience, and readiness to build applications for diverse users.
Localization typically involves libraries like react-i18next or react-intl. Set up translation files for different languages and configure the library within your app using provided hooks or components.
// Example using react-i18nextimport { useTranslation } from 'react-i18next';const MyComponent = () => {const { t } = useTranslation();return <p>{t('welcome_message')}</p>;};
Find in-depth explanations and track study progress here ->
react-intl?react-intl is a library that provides internationalization (i18n) support for React applications. It helps in formatting numbers, dates, strings, and handling translation/localization. It integrates with the Intl API in JavaScript to provide locale-specific data and translation management.
react-intl?react-intl?<FormattedMessage />, <FormattedNumber />, <FormattedDate />, etc., to format content.useIntl for formatting messages, numbers, or dates imperatively within components.FormattedMessage as a placeholder using react-intl?You can use the FormattedMessage component to handle placeholders within strings. Placeholders are replaced dynamically with variables in the translated string.
import { FormattedMessage } from 'react-intl';function WelcomeMessage() {return (<FormattedMessageid="welcome"defaultMessage="Hello, {name}!"values={{ name: 'John' }}/>);}
Here, {name} is a placeholder, and John will replace it.
You can access the current locale using the useIntl hook or the IntlProvider's locale prop.
Using useIntl:
import { useIntl } from 'react-intl';function LocaleDisplay() {const intl = useIntl();return <div>Current locale: {intl.locale}</div>;}
Using IntlProvider:
<IntlProvider locale="en" messages={messages}><MyComponent /></IntlProvider>
Here, locale="en" defines the current locale.
react-intl?You can format dates using the <FormattedDate /> component or the useIntl hook's formatDate method.
Using <FormattedDate /> component:
import { FormattedDate } from 'react-intl';function DateComponent() {return (<FormattedDatevalue={new Date()}year="numeric"month="long"day="2-digit"/>);}
Using useIntl hook:
import { useIntl } from 'react-intl';function DateComponent() {const intl = useIntl();const formattedDate = intl.formatDate(new Date(), {year: 'numeric',month: 'long',day: '2-digit',});return <div>{formattedDate}</div>;}
These methods allow you to format the date in a locale-sensitive manner.
Understanding testing in React is important in front end interviews because it shows you can write reliable, maintainable code and catch bugs early through unit, integration, and UI tests. Proficiency with tools like Jest and React Testing Library signals that you prioritize code quality and can work effectively in team environments with CI/CD workflows.
Testing React applications can be done using Jest and React Testing Library. Jest serves as the testing framework while React Testing Library provides utilities for testing components similarly to user interactions.
Find in-depth explanations and track study progress here ->
Jest is a JavaScript testing framework that provides a test runner, assertion library, and mocking support. It's commonly used for testing React applications due to its simplicity and integration with tools like React Testing Library.
React Testing Library is a testing utility for React that helps test components in a way that resembles how users interact with the application. It provides functions to render components, interact with them, and assert on the rendered output.
To test React components using React Testing Library, you can:
render.getByText, queryByRole, etc.Example:
import { render, screen, fireEvent } from '@testing-library/react';import MyComponent from './MyComponent';test('renders component', () => {render(<MyComponent />);const button = screen.getByRole('button');fireEvent.click(button);expect(screen.getByText('Clicked!')).toBeInTheDocument();});
In this example, the test renders MyComponent, clicks a button, and asserts that the text 'Clicked!' is present.
To test asynchronous code in React components, you can use async/await with waitFor from React Testing Library to handle asynchronous operations like data fetching or API calls.
Example:
import { render, screen, waitFor } from '@testing-library/react';import MyComponent from './MyComponent';test('fetches data and renders it', async () => {render(<MyComponent />);await waitFor(() => {expect(screen.getByText('Data loaded')).toBeInTheDocument();});});
In this example, the test waits for the data to be loaded before asserting that the text 'Data loaded' is present.
To mock API calls in React component tests, you can use Jest's jest.mock to mock the API module and return mock data. This allows you to simulate API responses without making actual network requests.
Example:
import { render, screen } from '@testing-library/react';jest.mock('./api', () => ({fetchData: jest.fn(() => Promise.resolve('mocked data')),}));import MyComponent from './MyComponent';test('fetches data and renders it', async () => {render(<MyComponent />);expect(screen.getByText('Loading...')).toBeInTheDocument();expect(await screen.findByText('mocked data')).toBeInTheDocument();});
In this example, the fetchData function from the api module is mocked to return 'mocked data' for testing purposes.
Render the hook inside a test using renderHook from @testing-library/react, then call act to drive any state updates.
import { renderHook, act } from '@testing-library/react';import useCounter from './useCounter';test('increments counter', () => {const { result } = renderHook(() => useCounter());act(() => {result.current.increment();});expect(result.current.count).toBe(1);});
Older sources import renderHook from @testing-library/react-hooks. That package was deprecated and merged into @testing-library/react in v13; use the import shown above.
Same approach as above: render the hook with renderHook and assert on result.current. For hooks that depend on context (e.g., a router or theme provider), pass a wrapper option.
import { renderHook, act } from '@testing-library/react';import useCustomHook from './useCustomHook';test('hook behavior', () => {const { result } = renderHook(() => useCustomHook());act(() => {result.current.doSomething();});expect(result.current.value).toBe('expected value');});// With a context provider:const wrapper = ({ children }) => (<MyProvider value="test">{children}</MyProvider>);const { result } = renderHook(() => useCustomHook(), { wrapper });
Shallow rendering renders a component one level deep: its children are not rendered, only referenced as React elements. The intent was to isolate the component under test from its children.
The two implementations were react-test-renderer/shallow (a low-level API) and Enzyme's shallow() (a popular wrapper around it).
// Enzyme-style example (historical)import { shallow } from 'enzyme';const wrapper = shallow(<Button label="Click Me" />);expect(wrapper.text()).toBe('Click Me');
Don't use shallow rendering in new code. Enzyme is unmaintained and has no official React 17+ adapter. The React docs recommend React Testing Library, which renders components the way users see them and asserts on accessible output, so tests don't break on internal refactors. Use RTL's render with screen.getByRole / getByText instead.
Snapshot Testing in React is a testing technique that captures the rendered output of a component and saves it as a snapshot. Subsequent test runs compare the current output with the saved snapshot to detect any unexpected changes. If the output differs from the snapshot, the test fails, indicating that the component's output has changed.
Here's an example of using Snapshot Testing with Jest:
import React from 'react';import renderer from 'react-test-renderer';import MyComponent from './MyComponent';test('renders correctly', () => {const tree = renderer.create(<MyComponent />).toJSON();expect(tree).toMatchSnapshot();});
In this example, the renderer.create function renders the MyComponent and converts it to a JSON tree. The toMatchSnapshot function saves the snapshot of the component's output. Subsequent test runs compare the current output with the saved snapshot, ensuring the component's output remains consistent.
To test React components that use context, you can wrap the component in a context provider with the desired context values for testing. This allows you to simulate the context values and test the component's behavior based on those values.
Example:
import { render } from '@testing-library/react';import { MyContextProvider } from './MyContextProvider';import MyComponent from './MyComponent';test('renders correctly with context', () => {const { getByText } = render(<MyContextProvider value="test value"><MyComponent /></MyContextProvider>,);expect(getByText('test value')).toBeInTheDocument();});
In this example, the MyComponent is wrapped in a MyContextProvider with a specific context value for testing. The test verifies that the component renders correctly with the provided context value.
To test React components that use Redux, you can use the redux-mock-store library to create a mock store with the desired state for testing. This allows you to simulate the Redux store and test the component's behavior based on the state.
Example:
import { render } from '@testing-library/react';import configureStore from 'redux-mock-store';import { Provider } from 'react-redux';import MyComponent from './MyComponent';const mockStore = configureStore([]);test('renders correctly with Redux state', () => {const store = mockStore({ counter: 0 });const { getByText } = render(<Provider store={store}><MyComponent /></Provider>,);expect(getByText('Counter: 0')).toBeInTheDocument();});
In this example, the MyComponent is wrapped in a Provider with a mock Redux store containing the initial state { counter: 0 } for testing. The test verifies that the component renders correctly with the provided Redux state.
react-test-renderer was a utility for rendering React components to a plain JS object tree (rather than the DOM), useful for snapshot testing without a browser environment.
import TestRenderer from 'react-test-renderer';import MyComponent from './MyComponent';const renderer = TestRenderer.create(<MyComponent />);const tree = renderer.toJSON();expect(tree).toMatchSnapshot();
react-test-renderer is deprecated as of React 19, and the React team recommends migrating off it. For component tests, use React Testing Library with a DOM environment (jsdom for Jest, or built-in for Vitest). For snapshot tests, serialize the DOM produced by RTL's render:
import { render } from '@testing-library/react';const { container } = render(<MyComponent />);expect(container).toMatchSnapshot();
React 19 added Actions and form integrations, the use hook, stable Server Components, and the React Compiler. These are common interview topics in 2026.
React 19 adds:
use hook: reads promises and context during render.<form action={fn}>.ref as a regular prop on function components (no more forwardRef).<title>, <meta>, and stylesheets out of JSX.Together, these move data mutations and async UI state into React itself, instead of leaving them as patterns each app reinvents.
By convention, an Action is an async function passed to a React API that runs it inside a transition: useActionState, startTransition (from useTransition), or a <form action={...}> prop. React tracks pending state, surfaces errors, and applies updates inside a transition so the UI stays responsive. This removes the usual boilerplate of toggling a loading flag, wrapping in try/catch, and managing error and data state separately.
import { useActionState } from 'react';async function updateName(prevState, formData) {const name = formData.get('name');const error = await saveName(name);if (error) return { error };return { name };}function NameForm() {const [state, dispatchAction, isPending] = useActionState(updateName, {name: '',});return (<form action={dispatchAction}><input name="name" defaultValue={state.name} /><button disabled={isPending}>Save</button>{state.error && <p>{state.error}</p>}</form>);}
useActionState hook do?useActionState takes an action function and an initial state, and returns [state, dispatchAction, isPending]. Calling dispatchAction (usually by passing it to <form action>) runs the action, marks isPending true, and replaces the state with the action's return value when it resolves. One hook covers what you'd otherwise write as three separate useState calls for data, loading, and error.
useOptimistic do?useOptimistic renders an optimistic version of state immediately while an action is in flight, then automatically reverts to the real state when the action settles. Useful for chat messages, likes, list reordering, or anywhere the network round-trip would feel laggy.
import { useOptimistic } from 'react';function MessageList({ messages, sendMessage }) {const [optimisticMessages, addOptimistic] = useOptimistic(messages,(state, newMessage) => [...state, { text: newMessage, sending: true }],);async function handleSend(formData) {const text = formData.get('text');addOptimistic(text);await sendMessage(text);}return (<>{optimisticMessages.map((m, i) => (<p key={i} style={{ opacity: m.sending ? 0.5 : 1 }}>{m.text}</p>))}<form action={handleSend}><input name="text" /></form></>);}
use hook and how is it different from useEffect + fetch?use reads the value of a Promise or Context during render. When given a Promise, it suspends the component until the promise resolves (handled by the nearest <Suspense> boundary) and then returns the resolved value. Unlike useEffect, use can be called conditionally and inside loops, and the resolved data is available synchronously after suspension, so there's no loading state to thread through the tree.
import { use, Suspense } from 'react';function Profile({ userPromise }) {const user = use(userPromise); // suspends until resolvedreturn <h1>{user.name}</h1>;}// Server Component: render runs once per request, so the promise is stable.// In a Client Component, create the promise outside render (or via `cache()`)// to avoid making a new one on every re-render.async function Page() {const userPromise = fetchUser();return (<Suspense fallback={<p>Loading...</p>}><Profile userPromise={userPromise} /></Suspense>);}
Server Components render on the server and stream their output to the client. They never ship JavaScript to the browser, can await data directly (no useEffect round-trip), and can access server-only resources like the database or filesystem. They cannot use hooks like useState or useEffect, attach event handlers, or use browser-only APIs; those still belong in Client Components (files marked 'use client').
| Server Component | Client Component | |
|---|---|---|
| Where it runs | Server (build or request time) | Browser (after hydration) |
| JS shipped | None | Yes |
| State / effects | Not allowed | Allowed |
| Event handlers | Not allowed | Allowed |
Can await data directly | Yes | No (use use or fetch in effect) |
| Can import the other | Yes (renders Client Components) | No (cannot import Server Components, only receive them as props/children) |
Server Components are typically the outer shell that fetches data; Client Components are the interactive leaves marked with 'use client'.
The React Compiler is an opt-in build-time tool that analyzes your components and automatically inserts memoization equivalent to useMemo, useCallback, and React.memo where it's safe. It removes the need for manual memoization in most cases. It enforces the Rules of React strictly: if your code violates them (e.g., mutating props, calling hooks conditionally), the compiler skips that component instead of producing incorrect output.
useTransition and useDeferredValue?Both mark updates as non-urgent so React can keep the UI responsive. The difference is where you put the control:
useTransition wraps the state setter at the call site. You decide when a particular update should be a transition (e.g., a search submit).useDeferredValue wraps a value at the consumer. It hands you a lagging copy of the value that updates after urgent renders, useful when the data source isn't under your control (e.g., a value coming through props).// useTransition: control at the dispatch siteconst [isPending, startTransition] = useTransition();startTransition(() => setQuery(input));// useDeferredValue: control at the read siteconst deferredQuery = useDeferredValue(query);return <ExpensiveResults query={deferredQuery} />;
action prop work in React 19?React 19 lets you pass a function directly to <form action> (and <button formAction>). React calls the function with a FormData argument when the form is submitted, runs it inside a transition, and resets uncontrolled inputs on success. Combine it with useActionState or useFormStatus for pending state and error handling without manual onSubmit plumbing.
import { useFormStatus } from 'react-dom';function SubmitButton() {const { pending } = useFormStatus();return <button disabled={pending}>{pending ? 'Saving...' : 'Save'}</button>;}function ProfileForm() {async function save(formData) {await updateProfile(Object.fromEntries(formData));}return (<form action={save}><input name="name" /><SubmitButton /></form>);}
These 100+ questions should give you a good idea on what to expect in React interviews. If you're looking for more in-depth React interview preparation materials, check out these:
You can also explore the Top ReactJS Interview Questions repo - a collection of 50 commonly asked questions compiled from real interview experiences.

JavaScript is an essential skill for anyone pursuing a career in web development, but securing a job in this field can be particularly challenging for newcomers. A critical part of the hiring process is the technical interview, where your JavaScript expertise will be thoroughly evaluated.
To support your preparation and build your confidence, we've put together a list of the top 75+ must-know JavaScript interview questions and answers frequently encountered in interviews.
What's new in the May 2026 update
- 25 new questions on modern JavaScript: optional chaining, nullish coalescing,
Promise.allSettled/Promise.any,AbortController, generators,error.cause, immutable array methods (toSorted/toReversed),Object.groupBy, Setunion/intersection/difference, iterator helpers,structuredClone, private class fields, and more; see the Modern JavaScript (ES2020+) section below.
If you're looking for additional JavaScript interview preparation materials, also check out these resources:
Debouncing is a smart way to handle events that fire repeatedly within a short time, such as typing in a search box or resizing a window. Instead of executing a function every single time the event is triggered, debouncing ensures the function runs only after the event stops firing for a specified time.
It prevents performance bottlenecks by reducing the number of unnecessary function calls, making your app smoother and more efficient.
The debounce method delays a function's execution until after a defined "waiting period" has passed since the last event. Let's see an example using Lodash:
import { debounce } from 'lodash';const searchInput = document.getElementById('search-input');const debouncedSearch = debounce(() => {// Perform the search operation hereconsole.log('Searching for:', searchInput.value);}, 300);searchInput.addEventListener('input', debouncedSearch);
While debouncing waits until user activity stops, throttling ensures the function runs at fixed intervals, regardless of how often the event occurs. Each technique suits specific use cases, such as search boxes (debouncing) versus scroll events (throttling).
Practice implementing a Debounce function on GreatFrontEnd ->
Promise.all()Promise.all() is a powerful method in JavaScript that allows you to handle multiple asynchronous tasks simultaneously. It takes an array of promises and returns a single promise that resolves when all the promises resolve, or rejects if any one of them fails.
This method is perfect when you need to wait for several independent asynchronous tasks to finish before proceeding, like fetching data from multiple APIs.
Here's how Promise.all() works with multiple API requests:
const promise1 = fetch('https://api.example.com/data/1');const promise2 = fetch('https://api.example.com/data/2');const promise3 = fetch('https://api.example.com/data/3');Promise.all([promise1, promise2, promise3]).then((responses) => {// Executes only when all promises are resolved.console.log('All responses:', responses);}).catch((error) => {// Catches any error from any promise.console.error('Error:', error);});
Promise.allPractice implementing a Promise.all function on GreatFrontEnd ->
Deep equality involves comparing two objects or arrays to determine if they are structurally identical. Unlike shallow equality, which only checks if object references are the same, deep equality examines whether all nested values are equal.
Here's a simple deepEqual implementation:
function deepEqual(obj1, obj2) {if (obj1 === obj2) return true;if (obj1 == null ||typeof obj1 !== 'object' ||obj2 == null ||typeof obj2 !== 'object')return false;let keys1 = Object.keys(obj1);let keys2 = Object.keys(obj2);if (keys1.length !== keys2.length) return false;for (let key of keys1) {if (!keys2.includes(key) || !deepEqual(obj1[key], obj2[key])) return false;}return true;}// Example usageconst object1 = {name: 'John',age: 30,address: {city: 'New York',zip: '10001',},};const object2 = {name: 'John',age: 30,address: {city: 'New York',zip: '10001',},};console.log(deepEqual(object1, object2)); // true
This function uses recursion to check nested properties, ensuring all values match in both objects or arrays. It's a critical concept for comparing complex data structures in frontend development.
Practice implementing Deep Equal on GreatFrontEnd ->
An EventEmitter is a utility that enables objects to listen for and emit events. It implements the observer pattern, allowing you to subscribe to actions or changes and handle them when triggered. This concept is fundamental in both JavaScript and Node.js for managing event-driven programming.
const eventEmitter = new EventEmitter();// Subscribe to an eventeventEmitter.on('customEvent', (data) => {console.log('Event emitted with data:', data);});// Emit the eventeventEmitter.emit('customEvent', { message: 'Hello, world!' });
EventEmitter allows flexible communication between components, making it useful in scenarios like state management, logging, or real-time updates.
Practice implementing an Event Emitter on GreatFrontEnd ->
Array.prototype.reduce()?Array.prototype.reduce() is a versatile method for iterating through an array and reducing it to a single value. It processes each element with a callback function, carrying over an accumulator to build the final result. Common use cases include summing numbers, flattening arrays, or even building complex objects.
const numbers = [1, 2, 3, 4, 5];const sum = numbers.reduce(function (accumulator, currentValue) {return accumulator + currentValue;}, 0);console.log(sum); // Output: 15
reduce?Practice implementing Array.protoype.reduce on GreatFrontEnd ->
Flattening transforms a nested array into a single-level array, making it more manageable. Since ES2019, JavaScript provides the Array.prototype.flat() method for this.
const nestedArray = [1, [2, [3, [4, [5]]]]];const flatArray = nestedArray.flat(Infinity);console.log(flatArray); // Output: [1, 2, 3, 4, 5]
Here, .flat(Infinity) ensures the entire array is flattened, no matter how deep. For less deeply nested arrays, you can specify the depth.
Before ES2019, custom solutions were common:
// Custom recursive array flattenerfunction flattenArray(arr) {return arr.reduce((acc, val) =>Array.isArray(val) ? acc.concat(flattenArray(val)) : acc.concat(val),[],);}const nestedArray = [1, [2, [3, [4, [5]]]]];const flatArray = flattenArray(nestedArray);console.log(flatArray); // Output: [1, 2, 3, 4, 5]
Practice implementing a flatten function on GreatFrontEnd ->
Merging data is crucial when handling complex structures. JavaScript provides efficient ways to combine objects or arrays.
The spread operator is concise and intuitive for merging objects:
const obj1 = { a: 1, b: 2 };const obj2 = { b: 3, c: 4 };const mergedObj = { ...obj1, ...obj2 };console.log(mergedObj); // Output: { a: 1, b: 3, c: 4 }
Object.assign()Another approach is Object.assign():
const obj1 = { a: 1, b: 2 };const obj2 = { b: 3, c: 4 };const mergedObj = Object.assign({}, obj1, obj2);console.log(mergedObj); // Output: { a: 1, b: 3, c: 4 }
const array1 = [1, 2, 3];const array2 = [4, 5, 6];const mergedArray = [...array1, ...array2];console.log(mergedArray); // Output: [1, 2, 3, 4, 5, 6]
Array.concat()const array1 = [1, 2, 3];const array2 = [4, 5, 6];const mergedArray = array1.concat(array2);console.log(mergedArray); // Output: [1, 2, 3, 4, 5, 6]
For nested objects, you'll need custom logic or libraries:
function deepMerge(target, source) {for (const key in source) {if (source[key] instanceof Object && key in target) {Object.assign(source[key], deepMerge(target[key], source[key]));}}Object.assign(target || {}, source);return target;}const obj1 = { a: 1, b: { x: 10, y: 20 } };const obj2 = { b: { y: 30, z: 40 }, c: 3 };const mergedObj = deepMerge(obj1, obj2);console.log(mergedObj); // Output: { a: 1, b: { x: 10, y: 30, z: 40 }, c: 3 }
Alternatively, libraries like Lodash simplify deep merging:
const _ = require('lodash');const obj1 = { a: 1, b: { x: 10, y: 20 } };const obj2 = { b: { y: 30, z: 40 }, c: 3 };const mergedObj = _.merge({}, obj1, obj2);console.log(mergedObj); // Output: { a: 1, b: { x: 10, y: 30, z: 40 }, c: 3 }
Practice implementing a deep merge function on GreatFrontEnd ->
getElementsByClassNamegetElementsByClassName fetches elements matching a specific class and returns them as a live HTMLCollection.
// Fetch and loop through elementsconst elements = document.getElementsByClassName('example');for (let i = 0; i < elements.length; i++) {console.log(elements[i].textContent);}
You can combine class names for more specific selections:
const elements = document.getElementsByClassName('class1 class2');
HTMLCollection updates automatically if DOM elements are added or removed.
For more complex selectors, use querySelectorAll:
const elements = document.querySelectorAll('.example');
Practice Using getElementsByClassName on GreatFrontEnd ->
Memoization saves computed results to avoid redundant calculations.
function expensiveOperation(n) {console.log('Calculating for', n);return n * 2;}// Memoize functionfunction memoize(func) {const cache = {};return function (n) {if (cache[n] !== undefined) {console.log('From cache for', n);return cache[n];}const result = func(n);cache[n] = result;return result;};}const memoizedExpensiveOperation = memoize(expensiveOperation);console.log(memoizedExpensiveOperation(5)); // Calculating for 5, 10console.log(memoizedExpensiveOperation(5)); // From cache for 5, 10
Libraries like Lodash also provide a memoize utility.
Practice implementing a memoize function on GreatFrontEnd ->
getAccessing nested object properties risk errors if any property is undefined. Tools like Lodash's get or JavaScript's optional chaining (?.) help mitigate this.
const user = { address: { city: 'New York' } };console.log(_.get(user, 'address.city')); // 'New York'console.log(user.address?.city); // 'New York'
These methods safely retrieve nested properties without crashing the program.
Practice implementing a get function on GreatFrontEnd ->
Hoisting refers to how JavaScript moves variable and function declarations to the top of their scope during compilation. While only the declaration is hoisted (not the initialization), understanding hoisting helps in writing cleaner and bug-free code.
varVariables declared with var are hoisted and initialized as undefined. Accessing them before initialization results in undefined.
console.log(foo); // undefinedvar foo = 1;console.log(foo); // 1
let, const, and classVariables declared with let, const, and class are hoisted but exist in a "temporal dead zone" until their declaration is reached, causing a ReferenceError if accessed early.
console.log(y); // ReferenceErrorlet y = 'local';
Both the declaration and definition of functions are hoisted, allowing them to be called before their declaration.
foo(); // 'FOOOOO'function foo() {console.log('FOOOOO');}
For function expressions, only the variable is hoisted, not the function itself.
console.log(bar); // undefinedbar(); // TypeError: bar is not a functionvar bar = function () {console.log('BARRRR');};
Imports are hoisted, making them available throughout the module. However, their initialization happens before the module code executes.
foo.doSomething(); // Works fineimport foo from './modules/foo';
Modern JavaScript uses let and const to avoid hoisting pitfalls. Declare variables at the top of their scope for better readability and use tools like ESLint to enforce best practices:
By following these practices, you can write robust, maintainable code.
Read more about the concept of "Hoisting" on GreatFrontEnd ->
let, var, and const?In JavaScript, let, var, and const are used to declare variables, but they differ in scope, initialization, redeclaration, reassignment, and behavior when accessed before declaration.
Variables declared with var are function-scoped or global, while let and const are block-scoped (confined to the nearest {} block).
if (true) {var foo = 1;let bar = 2;const baz = 3;}console.log(foo); // 1console.log(bar); // ReferenceErrorconsole.log(baz); // ReferenceError
var and let can be declared without initialization, but const requires an initial value.
var a; // Validlet b; // Validconst c; // SyntaxError: Missing initializer
Variables declared with var can be redeclared, but let and const cannot.
var x = 10;var x = 20; // Allowedlet y = 10;let y = 20; // SyntaxError: Identifier 'y' has already been declared
var and let allow reassignment, while const does not.
let a = 1;a = 2; // Allowedconst b = 1;b = 2; // TypeError: Assignment to constant variable
All variables are hoisted, but var initializes to undefined, whereas let and const exist in a "temporal dead zone" until the declaration is reached.
console.log(foo); // undefinedvar foo = 'foo';console.log(bar); // ReferenceErrorlet bar = 'bar';
const for variables that don't change to ensure immutability.let when reassignment is needed.var due to its hoisting and scoping issues.Read more about the differences between let, var, and const on GreatFrontEnd ->
== and === in JavaScript?The == operator checks for equality after performing type conversion, while === checks for strict equality without type conversion.
==)== allows type coercion, which means JavaScript converts values to the same type before comparison. This can lead to unexpected results.
42 == '42'; // true0 == false; // truenull == undefined; // true
===)=== checks both value and type, avoiding the pitfalls of type coercion.
42 === '42'; // false0 === false; // falsenull === undefined; // false
=== for most comparisons as it avoids implicit type conversion and makes code more predictable.== only when comparing null or undefined for simplicity.let x = null;console.log(x == null); // trueconsole.log(x == undefined); // true
Object.is()Object.is() is similar to === but treats -0 and +0 as distinct and considers NaN equal to itself.
console.log(Object.is(-0, +0)); // falseconsole.log(Object.is(NaN, NaN)); // true
=== for strict comparisons to avoid bugs caused by type coercion.Object.is() for nuanced comparisons like distinguishing -0 and +0.Explore the differences between == and === on GreatFrontEnd ->
The event loop is the backbone of JavaScript's asynchronous behavior, enabling single-threaded execution without blocking.
setTimeout and HTTP requests on separate threadssetTimeout and UI eventsPromise callbacks, executed before macrotasksconsole.log('Start');setTimeout(() => console.log('Timeout 1'), 0);Promise.resolve().then(() => console.log('Promise 1'));setTimeout(() => console.log('Timeout 2'), 0);console.log('End');
Output:
StartEndPromise 1Timeout 1Timeout 2
Explanation:
Start, End) run first.Promise 1) follow.Timeout 1, Timeout 2) run last.Explore the event loop in JavaScript on GreatFrontEnd ->
Event delegation is an efficient way to manage events for multiple elements by attaching a single event listener to their common parent.
event.target to determine the clicked element.// HTML:// <ul id="item-list">// <li>Item 1</li>// <li>Item 2</li>// </ul>const itemList = document.getElementById('item-list');itemList.addEventListener('click', (event) => {if (event.target.tagName === 'LI') {console.log(`Clicked on ${event.target.textContent}`);}});
Explore event delegation in JavaScript on GreatFrontEnd ->
this works in JavaScriptThe value of this depends on how a function is called. Let's explore its different behaviors.
Using new: When creating objects, this refers to the newly created object.
function Person(name) {this.name = name;}const person = new Person('Alice');console.log(person.name); // 'Alice'
Using apply, call, or bind: Explicitly sets this to a specified object.
function greet() {console.log(this.name);}const person = { name: 'Alice' };greet.call(person); // 'Alice'
Method call: this refers to the object the method is called on.
const obj = {name: 'Alice',greet() {console.log(this.name);},};obj.greet(); // 'Alice'
Free function call: Defaults to the global object (window in browsers) or undefined in strict mode.
function greet() {console.log(this); // global object or undefined}greet();
Arrow functions: Capture this from their enclosing scope.
const obj = {name: 'Alice',greet: () => {console.log(this.name); // Inherits `this` from enclosing scope},};obj.greet(); // undefined
thisArrow functions simplify usage by capturing this from their lexical scope.
function Timer() {this.seconds = 0;setInterval(() => {this.seconds++;console.log(this.seconds);}, 1000);}const timer = new Timer();
Explore how this works in JavaScript on GreatFrontEnd ->
sessionStorage, and localStorage apart?When it comes to client-side storage, cookies, localStorage, and sessionStorage serve distinct roles:
// Set a cookie with an expiry datedocument.cookie = 'userId=12345; expires=Fri, 31 Dec 2025 23:59:59 GMT; path=/';// Read all cookiesconsole.log(document.cookie);// Delete a cookiedocument.cookie = 'userId=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';
localStorage// Store data in localStoragelocalStorage.setItem('username', 'john_doe');// Retrieve dataconsole.log(localStorage.getItem('username'));// Remove an itemlocalStorage.removeItem('username');// Clear all localStorage datalocalStorage.clear();
sessionStoragelocalStorage (around 5MB).// Store data in sessionStoragesessionStorage.setItem('sessionId', 'abcdef');// Retrieve dataconsole.log(sessionStorage.getItem('sessionId'));// Remove an itemsessionStorage.removeItem('sessionId');// Clear all sessionStorage datasessionStorage.clear();
Learn more about cookies, sessionStorage, and localStorage on GreatFrontEnd ->
<script>, <script async>, and <script defer> differ?<script>When using the <script> tag without attributes, it fetches and executes the script immediately, pausing HTML parsing.
Use case: Critical scripts needed before page rendering.
<script src="main.js"></script>
<script async>With async, the script loads in parallel to HTML parsing and executes as soon as it's ready.
Use case: Independent scripts like analytics or ads.
<script async src="analytics.js"></script>
<script defer>When using defer, the script loads alongside HTML parsing but only executes after the HTML is fully parsed.
Use Case: Scripts that rely on a complete DOM structure.
<script defer src="deferred.js"></script>
Discover more about <script>, <script async>, and <script defer> on GreatFrontEnd ->
null, undefined?Variables not defined using var, let, or const are considered undeclared and can cause global scope issues.
undefinedA declared variable that hasn't been assigned a value is undefined.
nullRepresents the intentional absence of any value. It's an explicit assignment. Example Code:
let a;console.log(a); // undefinedlet b = null;console.log(b); // nulltry {console.log(c); // ReferenceError: c is not defined} catch (e) {console.log('c is undeclared');}
Read more about null, undefined, and undeclared variables on GreatFrontEnd ->
.call() vs .apply()?Both .call and .apply let you invoke a function with a specified this value. The key difference lies in how arguments are passed:
.call: Accepts arguments as a comma-separated list..apply: Accepts arguments as an array.Memory aid:
function sum(a, b) {return a + b;}console.log(sum.call(null, 1, 2)); // 3console.log(sum.apply(null, [1, 2])); // 3
Learn more about .call and .apply on GreatFrontEnd ->
Function.prototype.bind work?The bind method is used to create a new function with a specific this value and, optionally, preset arguments. This ensures that the function always has the correct this context, regardless of how or where it's called.
bind:this is correctly set for the function.const john = {age: 42,getAge: function () {return this.age;},};console.log(john.getAge()); // 42const unboundGetAge = john.getAge;console.log(unboundGetAge()); // undefinedconst boundGetAge = john.getAge.bind(john);console.log(boundGetAge()); // 42const mary = { age: 21 };const boundGetAgeMary = john.getAge.bind(mary);console.log(boundGetAgeMary()); // 21
Explore Function.prototype.bind on GreatFrontEnd ->
Using arrow functions for methods in constructors automatically binds the this context to the constructor, avoiding the need to manually bind it. This eliminates issues caused by this referring to unexpected contexts.
const Person = function (name) {this.name = name;this.sayName1 = function () {console.log(this.name);};this.sayName2 = () => {console.log(this.name);};};const john = new Person('John');const dave = new Person('Dave');john.sayName1(); // Johnjohn.sayName2(); // Johnjohn.sayName1.call(dave); // Davejohn.sayName2.call(dave); // John
Arrow functions are particularly useful in React class components, ensuring methods maintain the correct context when passed to child components.
Explore the advantage for using the arrow syntax for a method in a constructor on GreatFrontEnd ->
Prototypal inheritance is a way for objects to share properties and methods through their prototype chain.
null.new to create objects.function Animal(name) {this.name = name;}Animal.prototype.sayName = function () {console.log(`My name is ${this.name}`);};function Dog(name, breed) {Animal.call(this, name);this.breed = breed;}Dog.prototype = Object.create(Animal.prototype);Dog.prototype.bark = function () {console.log('Woof!');};let fido = new Dog('Fido', 'Labrador');fido.bark(); // "Woof!"fido.sayName(); // "My name is Fido"
Explore how prototypal inheritance works on GreatFrontEnd ->
function Person(){}, const person = Person(), and const person = new Person()?function Person(){}: A function declaration, typically used for constructors if written in PascalCase.const person = Person(): Calls the function normally and assigns the result to person. No object creation happens unless explicitly returned.const person = new Person(): Invokes the function as a constructor, creating a new object and setting its prototype to Person.prototype.function foo() {}foo(); // "Hello!"function foo() {console.log('Hello!');}
var foo = function() {}foo(); // TypeError: foo is not a functionvar foo = function () {console.log('Hello!');};
Here are various approaches to creating objects in JavaScript:
Object literals: The simplest and most common way to create an object is using curly braces {} with key-value pairs.
const person = {firstName: 'John',lastName: 'Doe',};
Object constructor: Use the built-in Object constructor with the new keyword.
const person = new Object();person.firstName = 'John';person.lastName = 'Doe';
Object.create() method: Create an object with a specific prototype.
const personPrototype = {greet() {console.log(`Hello, my name is ${this.name}.`);},};const person = Object.create(personPrototype);person.name = 'John';person.greet(); // Hello, my name is John.
ES2015 classes: Define objects using the class syntax for a blueprint-like structure.
class Person {constructor(name, age) {this.name = name;this.age = age;}greet() {console.log(`Hi, I'm ${this.name} and I'm ${this.age} years old.`);}}const john = new Person('John', 30);john.greet(); // Hi, I'm John and I'm 30 years old.
Constructor functions: Use a function as a template for creating multiple objects.
function Person(name, age) {this.name = name;this.age = age;}const john = new Person('John', 30);console.log(john.name); // John
Explore various ways to create objects in JavaScript on GreatFrontEnd ->
A higher-order function is a function that either:
Accepts another function as an argument:
function greet(name) {return `Hello, ${name}!`;}function greetUser(greeter, name) {console.log(greeter(name));}greetUser(greet, 'Alice'); // Hello, Alice!
Returns another function:
function multiplier(factor) {return function (num) {return num * factor;};}const double = multiplier(2);console.log(double(4)); // 8
Explore the definition of a higher-order function on GreatFrontEnd ->
ES5 constructor functions use function constructors and prototypes for object creation and inheritance.
function Person(name, age) {this.name = name;this.age = age;}Person.prototype.greet = function () {console.log(`Hi, I'm ${this.name} and I'm ${this.age} years old.`);};const john = new Person('John', 30);john.greet(); // Hi, I'm John and I'm 30 years old.
ES2015 Classes use the class keyword for cleaner and more intuitive syntax.
class Person {constructor(name, age) {this.name = name;this.age = age;}greet() {console.log(`Hi, I'm ${this.name} and I'm ${this.age} years old.`);}}const john = new Person('John', 30);john.greet(); // Hi, I'm John and I'm 30 years old.
static in ES2015.extends and super keywords in ES2015.Explore differences between ES2015 classes and ES5 constructor functions on GreatFrontEnd ->
Event bubbling is the process where an event triggers on the target element and then propagates upwards through its ancestors in the DOM.
const parent = document.getElementById('parent');const child = document.getElementById('child');parent.addEventListener('click', () => {console.log('Parent clicked');});child.addEventListener('click', () => {console.log('Child clicked');});
Clicking the child element will log both "Child clicked" and "Parent clicked" due to bubbling.
Use event.stopPropagation() to prevent the event from propagating upwards.
child.addEventListener('click', (event) => {event.stopPropagation();console.log('Child clicked only');});
Explore event bubbling on GreatFrontEnd ->
Event capturing, also called "trickling", is the reverse of bubbling. The event propagates from the root element down to the target element.
Capturing is enabled by passing { capture: true } to addEventListener() as the third argument.
const parent = document.getElementById('parent');const child = document.getElementById('child');parent.addEventListener('click',() => {console.log('Parent capturing');},{ capture: true },);child.addEventListener('click', () => {console.log('Child clicked');});
Clicking the child will log "Parent capturing" first, followed by "Child clicked".
Explore event capturing on GreatFrontEnd ->
mouseenter and mouseover differ?mouseentermouseoverExplore the differences between mouseenter and mouseover on GreatFrontEnd ->
const fs = require('fs');const data = fs.readFileSync('file.txt', 'utf8');console.log(data); // Blocks until the file is fully readconsole.log('Program ends');
console.log('Start');fetch('https://api.example.com/data').then((response) => response.json()).then((data) => console.log(data)) // Non-blocking.catch((error) => console.error(error));console.log('End');
Explore the difference between synchronous and asynchronous functions on GreatFrontEnd ->
AJAX (Asynchronous JavaScript and XML) is a technique that allows web pages to fetch and send data asynchronously, enabling dynamic updates without reloading the entire page.
XMLHttpRequest; fetch() is the modern alternative.XMLHttpRequest:let xhr = new XMLHttpRequest();xhr.onreadystatechange = function () {if (xhr.readyState === XMLHttpRequest.DONE) {if (xhr.status === 200) {console.log(xhr.responseText);} else {console.error('Request failed');}}};xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos/1', true);xhr.send();
fetch():fetch('https://jsonplaceholder.typicode.com/todos/1').then((response) => response.json()).then((data) => console.log(data)).catch((error) => console.error('Fetch error:', error));
Explore AJAX in detail on GreatFrontEnd ->
Explore the advantages and disadvantages of using AJAX on GreatFrontEnd ->
XMLHttpRequest and fetch()?XMLHttpRequestonprogress.onerror event.let xhr = new XMLHttpRequest();xhr.open('GET', 'https://example.com/api', true);xhr.onload = function () {if (xhr.status === 200) {console.log(xhr.responseText);}};xhr.send();
fetch().catch() for better error management.AbortController for cancellations.fetch('https://example.com/api').then((response) => response.json()).then((data) => console.log(data)).catch((error) => console.error(error));
fetch() has cleaner syntax and better Promise integration.XMLHttpRequest supports progress tracking, which fetch() does not.Explore the differences between XMLHttpRequest and fetch() on GreatFrontEnd ->
JavaScript features a mix of primitive and non-primitive (reference) data types.
true or false.Tip: Use the typeof operator to determine the type of a variable.
Explore the various data types in JavaScript on GreatFrontEnd ->
JavaScript provides multiple ways to iterate over objects and arrays.
for...inLoops over all enumerable properties, including inherited ones.
for (const property in obj) {if (Object.hasOwn(obj, property)) {console.log(property);}}
Object.keys()Retrieves an array of an object's own enumerable properties.
Object.keys(obj).forEach((key) => console.log(key));
Object.entries()Returns an array of [key, value] pairs.
Object.entries(obj).forEach(([key, value]) => console.log(`${key}: ${value}`));
Object.getOwnPropertyNames()Includes both enumerable and non-enumerable properties.
Object.getOwnPropertyNames(obj).forEach((prop) => console.log(prop));
for LoopClassic approach for iterating through arrays:
for (let i = 0; i < arr.length; i++) {console.log(arr[i]);}
Array.prototype.forEach()Executes a callback for each array item.
arr.forEach((element, index) => console.log(element, index));
for...ofIdeal for looping through iterable objects like arrays.
for (const element of arr) {console.log(element);}
Array.prototype.entries()Iterates with both index and value.
for (const [index, element] of arr.entries()) {console.log(index, ':', element);}
Explore iteration techniques on GreatFrontEnd ->
...)The spread operator is used to expand elements of arrays or objects.
Copying arrays/objects:
const array = [1, 2, 3];const newArray = [...array]; // [1, 2, 3]
Merging arrays/objects:
const arr1 = [1, 2];const arr2 = [3, 4];const mergedArray = [...arr1, ...arr2]; // [1, 2, 3, 4]
Passing function arguments:
const nums = [1, 2, 3];console.log(Math.max(...nums)); // 3
...)The rest operator collects multiple elements into an array or object.
Function parameters:
function sum(...numbers) {return numbers.reduce((a, b) => a + b);}sum(1, 2, 3); // 6
Destructuring:
const [first, ...rest] = [1, 2, 3];console.log(rest); // [2, 3]
Explore spread and rest syntax on GreatFrontEnd ->
Mapsize property.const map = new Map();map.set('key', 'value');console.log(map.size); // 1
Object.keys(), Object.values(), or Object.entries().size property.const obj = { key: 'value' };console.log(Object.keys(obj).length); // 1
Explore the difference between Map and plain objects on GreatFrontEnd ->
Map/Set and WeakMap/WeakSetWeakMap and WeakSet keys must be objects, while Map and Set accept any data type.WeakMap and WeakSet allow garbage collection of keys, making them useful for managing memory.Map and Set have a size property.WeakMap and WeakSet are not iterable.// Map Exampleconst map = new Map();map.set({}, 'value');console.log(map.size); // 1// WeakMap Exampleconst weakMap = new WeakMap();let obj = {};weakMap.set(obj, 'value');obj = null; // Key is garbage-collected
Explore the differences between Map/Set and WeakMap/WeakSet on GreatFrontEnd ->
Arrow functions simplify function syntax, making them ideal for inline callbacks.
// Traditional function syntaxconst numbers = [1, 2, 3, 4, 5];const doubledNumbers = numbers.map(function (number) {return number * 2;});console.log(doubledNumbers); // [2, 4, 6, 8, 10]// Arrow function syntaxconst doubledWithArrow = numbers.map((number) => number * 2);console.log(doubledWithArrow); // [2, 4, 6, 8, 10]
Explore a use case for the new arrow function syntax on GreatFrontEnd ->
A callback is a function passed as an argument to another function, executed after the completion of an asynchronous task.
function fetchData(callback) {setTimeout(() => {const data = { name: 'John', age: 30 };callback(data);}, 1000);}fetchData((data) => {console.log(data); // { name: 'John', age: 30 }});
Explore the concept of a callback function in asynchronous operations on GreatFrontEnd ->
Debouncing delays execution of a function until a specified time has elapsed since its last invocation.
function debounce(func, delay) {let timeoutId;return (...args) => {clearTimeout(timeoutId);timeoutId = setTimeout(() => func.apply(this, args), delay);};}
Throttling ensures a function executes at most once within a set time interval.
function throttle(func, limit) {let inThrottle;return (...args) => {if (!inThrottle) {func.apply(this, args);inThrottle = true;setTimeout(() => (inThrottle = false), limit);}};}
Explore the concept of debouncing and throttling on GreatFrontEnd ->
Destructuring simplifies extracting values from arrays or objects into individual variables.
// Array destructuringconst [a, b] = [1, 2];// Object destructuringconst { name, age } = { name: 'John', age: 30 };
Explore the concept of destructuring assignment on GreatFrontEnd ->
Hoisting moves function declarations to the top of their scope during the compilation phase. However, function expressions and arrow functions do not get hoisted in the same way.
// Function declarationhoistedFunction(); // Works finefunction hoistedFunction() {console.log('This function is hoisted');}// Function expressionnonHoistedFunction(); // Throws an errorvar nonHoistedFunction = function () {console.log('This function is not hoisted');};
Explore the concept of hoisting on GreatFrontEnd ->
Classes in ES2015 use extends for inheritance and super to access parent constructors and methods.
class Animal {constructor(name) {this.name = name;}speak() {console.log(`${this.name} makes a noise.`);}}class Dog extends Animal {constructor(name, breed) {super(name);this.breed = breed;}speak() {console.log(`${this.name} barks.`);}}const dog = new Dog('Rex', 'German Shepherd');dog.speak(); // Rex barks.
Explore the concept of inheritance in ES2015 classes on GreatFrontEnd ->
Lexical scoping determines variable access based on where functions are defined, not where they're called.
function outerFunction() {let outerVariable = 'I am outside!';function innerFunction() {console.log(outerVariable); // I am outside!}innerFunction();}outerFunction();
Explore the concept of lexical scoping on GreatFrontEnd ->
JavaScript has three main types of scope: global, function, and block.
// Global scopevar globalVar = 'I am global';function myFunction() {// Function scopevar functionVar = 'I am in a function';if (true) {// Block scopelet blockVar = 'I am in a block';console.log(blockVar); // Accessible here}// console.log(blockVar); // Error}
Explore the concept of scope in JavaScript on GreatFrontEnd ->
The spread operator (...) expands elements of an iterable (like arrays) or properties of objects into individual elements.
// Copying an arrayconst arr1 = [1, 2, 3];const arr2 = [...arr1];// Merging arraysconst mergedArray = [...arr1, [4, 5]];// Copying an objectconst obj1 = { a: 1, b: 2 };const obj2 = { ...obj1 };// Passing as function argumentsconst sum = (x, y, z) => x + y + z;const nums = [1, 2, 3];sum(...nums); // 6
Explore the spread operator on GreatFrontEnd ->
this work in event handlers?In JavaScript, this in event handlers refers to the element that triggered the event. Its context can be explicitly bound using bind(), arrow functions, or direct assignment.
const button = document.querySelector('button');button.addEventListener('click', function () {console.log(this); // Refers to the button});const obj = {handleClick: function () {console.log(this); // Refers to obj},};button.addEventListener('click', obj.handleClick.bind(obj));
Explore the concept of this in event handlers on GreatFrontEnd ->
The next 25 questions cover ES2020–ES2025 additions that come up in modern JavaScript interviews.
?.) do, and where does it short-circuit?Optional chaining (?.) returns undefined if the value to its left is null or undefined, instead of throwing. It works on property access, function calls, and array indexing.
const user = { profile: null };console.log(user.profile?.name); // undefined, no throwconsole.log(user.callbacks?.onSave?.()); // undefinedconsole.log(user.tags?.[0]); // undefined
Important: it short-circuits the rest of the chain the moment any ?. operand is nullish, so user?.a.b.c will short-circuit at user? and never evaluate .a.b.c. It does not protect against 0, '', false, or NaN; those are not nullish.
??) and how does it differ from ||??? returns its right-hand side only when the left side is null or undefined. || returns the right-hand side for any falsy value: 0, '', false, NaN, null, undefined.
const port = 0;console.log(port || 3000); // 3000, 0 is falsyconsole.log(port ?? 3000); // 0, 0 is not nullishconst name = '';console.log(name || 'Anonymous'); // 'Anonymous'console.log(name ?? 'Anonymous'); // ''
Use ?? when "no value provided" is the only case you want to replace. || is right when any falsy value should be treated as "missing", like defaulting a flag.
||=, &&=, ??=)?ES2021's logical assignment operators combine a logical check with assignment, assigning only when the check passes.
const config = { retries: 0, host: '' };config.host ||= 'localhost'; // assigns, '' is falsyconfig.retries ??= 3; // does NOT assign, 0 is not nullishconfig.debug &&= 'verbose'; // does NOT assign, debug is undefined
a ||= b → assigns b only if a is falsy.a &&= b → assigns b only if a is truthy.a ??= b → assigns b only if a is nullish.They're short-circuiting: the right side runs only when the assignment will happen.
var vs let in a setTimeout loop)for (var i = 0; i < 3; i++) {setTimeout(() => console.log(i), 0);}// 3, 3, 3
var is function-scoped, so all three callbacks close over the same i. By the time the timers fire (after the synchronous loop finishes), i is 3.
Swap var for let:
for (let i = 0; i < 3; i++) {setTimeout(() => console.log(i), 0);}// 0, 1, 2
let creates a fresh binding per iteration, so each callback captures its own i.
Pre-let, the workaround was an IIFE:
for (var i = 0; i < 3; i++) {(function (j) {setTimeout(() => console.log(j), 0);})(i);}
Promise.allSettled() differ from Promise.all()?Promise.all() rejects as soon as any input promise rejects, and you lose the results of the others. Promise.allSettled() waits for every promise to settle and returns an array describing each outcome: { status: 'fulfilled', value } or { status: 'rejected', reason }.
const results = await Promise.allSettled([fetch('/api/a'),fetch('/api/b'),fetch('/api/c'),]);for (const result of results) {if (result.status === 'fulfilled') {console.log('ok:', result.value);} else {console.error('failed:', result.reason);}}
Use allSettled when you want every result regardless of failures, e.g., loading multiple independent widgets where one failure shouldn't blank the page.
Explore the difference between Promise.all and Promise.allSettled on GreatFrontEnd ->
Promise.any() and how does it handle rejections?Promise.any() resolves with the value of the first promise to fulfill. It only rejects if all input promises reject, and the rejection is an AggregateError containing every individual reason.
try {const fastest = await Promise.any([fetch('https://mirror-1.example.com/data'),fetch('https://mirror-2.example.com/data'),fetch('https://mirror-3.example.com/data'),]);console.log('first to respond:', fastest);} catch (err) {console.error(err.errors); // array of all rejection reasons}
Use it for racing mirrors, fallbacks, or "first response wins" patterns.
fetch request with AbortController?AbortController exposes a signal you pass to any abort-aware API (fetch, addEventListener, streams, observers). Calling .abort() cancels the operation and rejects the associated promise with an AbortError.
const controller = new AbortController();const promise = fetch('/api/slow', { signal: controller.signal });// Cancel after 5 secondssetTimeout(() => controller.abort(), 5000);try {const res = await promise;console.log(await res.json());} catch (err) {if (err.name === 'AbortError') {console.log('Request cancelled');}}
Common uses: cancelling in-flight requests when a component unmounts, debouncing search-as-you-type, and timing out long requests. You can also pass the same signal to multiple operations to cancel them as a group.
Explore aborting web requests with AbortController on GreatFrontEnd ->
async/await and raw Promises?async/await is syntactic sugar over Promises. Both run the same machinery; the difference is readability and control flow.
// Raw promise chainfunction loadUser(id) {return fetch(`/users/${id}`).then((res) => res.json()).then((user) => fetch(`/orgs/${user.orgId}`)).then((res) => res.json());}// async/await equivalentasync function loadUser(id) {const user = await (await fetch(`/users/${id}`)).json();const org = await (await fetch(`/orgs/${user.orgId}`)).json();return org;}
async/await lets you use try/catch, regular if/for, and reads top-to-bottom. Raw Promises shine when you want parallelism (Promise.all) or compose pipelines functionally.
Explore how async/await simplifies asynchronous code on GreatFrontEnd ->
async/await related to them?A generator (function*) is a function that can pause and resume. Each yield suspends execution and returns a value to the caller; next() resumes it.
function* counter() {yield 1;yield 2;yield 3;}const c = counter();console.log(c.next()); // { value: 1, done: false }console.log(c.next()); // { value: 2, done: false }console.log(c.next()); // { value: 3, done: true }
You can also send values back in via next(value), making generators bidirectional coroutines:
function* dialog() {const name = yield 'What is your name?';yield `Hello, ${name}!`;}const d = dialog();d.next(); // { value: 'What is your name?', done: false }d.next('Ada'); // { value: 'Hello, Ada!', done: false }
async/await is sugar over generators; an async function is a generator that yields promises, with the runtime calling .next() when each resolves. redux-saga uses generators directly to make async control flow testable.
error.cause and why is it useful?error.cause (ES2022) is a standard way to attach the underlying error when re-throwing, preserving the original error and its stack without string-mashing.
async function loadUser(id) {try {return await fetchUser(id);} catch (originalError) {throw new Error(`Failed to load user ${id}`, { cause: originalError });}}try {await loadUser(42);} catch (err) {console.error(err.message); // 'Failed to load user 42'console.error(err.cause); // the original network error, with full stack}
Before cause, the inner error was usually stringified into the outer message (new Error('Failed: ' + e.message)), losing the inner stack. With cause, devtools, console.error, and logging libraries (Sentry, Pino) walk the chain automatically.
Use it at every boundary where you wrap a low-level error into a higher-level one.
toSorted, toReversed, toSpliced, with)?ES2023 added four methods that return a new array instead of mutating the original. They mirror their classic counterparts but are safe with frozen data, React state, or any place where mutation causes bugs.
const arr = [3, 1, 2];const sorted = arr.toSorted(); // [1, 2, 3], arr unchangedconst reversed = arr.toReversed(); // [2, 1, 3], arr unchangedconst spliced = arr.toSpliced(1, 1, 9, 9); // [3, 9, 9, 2]const replaced = arr.with(0, 99); // [99, 1, 2]console.log(arr); // [3, 1, 2], still original
Before these methods, you had to write [...arr].sort() or arr.slice().reverse() to avoid mutating. They make immutable-style code one call shorter and clearer.
Array.prototype.findLast() do?findLast() (ES2023) returns the last element that matches a predicate. findLastIndex() returns its index. They're the mirror of find() / findIndex().
const events = [{ type: 'click', t: 100 },{ type: 'scroll', t: 200 },{ type: 'click', t: 300 },];const lastClick = events.findLast((e) => e.type === 'click');console.log(lastClick); // { type: 'click', t: 300 }
The pre-2023 alternative was [...arr].reverse().find(...): O(n) extra work and harder to read.
Object.groupBy() / Map.groupBy()?Object.groupBy() and Map.groupBy() (ES2024) group an iterable's items by the return value of a callback. Object.groupBy uses string keys; Map.groupBy allows any value as a key.
const people = [{ name: 'Ada', team: 'eng' },{ name: 'Lin', team: 'design' },{ name: 'Rao', team: 'eng' },];const byTeam = Object.groupBy(people, (p) => p.team);// { eng: [{Ada}, {Rao}], design: [{Lin}] }const teamObj = { id: 1 };const byRef = Map.groupBy(people, (p) => (p.team === 'eng' ? teamObj : null));// Map { teamObj => [{Ada}, {Rao}], null => [{Lin}] }
They replace the reduce((acc, x) => ...) grouping pattern.
union, intersection, difference)?ES2025 added set-algebra methods to Set. They take any iterable on the right side (not just another Set) and always return a new Set.
const a = new Set([1, 2, 3]);const b = new Set([3, 4, 5]);a.union(b); // Set { 1, 2, 3, 4, 5 }a.intersection(b); // Set { 3 }a.difference(b); // Set { 1, 2 }a.symmetricDifference(b); // Set { 1, 2, 4, 5 }a.isSubsetOf(b); // falsea.isSupersetOf(b); // falsea.isDisjointFrom(b); // false
Before this, an intersection was new Set([...a].filter(x => b.has(x))). The native methods are also faster, because implementations iterate the smaller side.
.map, .filter, .take on iterators)?ES2025 iterator helpers add .map, .filter, .take, .drop, .flatMap, .reduce, .some, .every, .find, and .toArray directly to iterators (and generators). Unlike array methods, they're lazy, so values flow through one at a time.
function* naturals() {let n = 1;while (true) yield n++;}const firstFiveSquares = naturals().map((n) => n * n).take(5).toArray();console.log(firstFiveSquares); // [1, 4, 9, 16, 25]
Laziness means infinite sequences don't allocate everything, and pipelines don't materialize intermediate arrays.
AsyncIterator.prototype.map and friends do the same over for await...of sources.
Array.fromAsync?Array.fromAsync (ES2024) is the async counterpart of Array.from. It awaits each value from an async iterable (or a sync iterable of promises) and collects them into an array.
async function* fetchPages(urls) {for (const url of urls) {const res = await fetch(url);yield await res.json();}}const allPages = await Array.fromAsync(fetchPages(['/api/p/1', '/api/p/2', '/api/p/3']),);
Pages are fetched sequentially. For parallel fetching, use Promise.all(urls.map(fetch)) instead. Array.fromAsync fits when each step depends on the previous, or when you just want to materialize an async iterable.
Symbol.iterator?Any object with a [Symbol.iterator]() method becomes iterable and usable with for...of, spread, destructuring, and Array.from. The method must return an iterator (an object with next() returning { value, done }).
The easy way is to delegate to a generator:
class Range {constructor(from, to) {this.from = from;this.to = to;}*[Symbol.iterator]() {for (let n = this.from; n <= this.to; n++) yield n;}}const r = new Range(1, 4);for (const n of r) console.log(n); // 1 2 3 4console.log([...r]); // [1, 2, 3, 4]const [first, ...rest] = r; // 1, [2, 3, 4]
For async sources, implement [Symbol.asyncIterator]() instead and consume with for await...of. NodeList, Map, Set, and Node streams all expose themselves through this same protocol.
A shallow copy duplicates the top level only; nested objects are shared by reference. A deep copy recursively duplicates everything.
const original = { user: { name: 'Ada' }, tags: ['x'] };// Shallow copies, nested refs sharedconst a = { ...original };const b = Object.assign({}, original);a.user.name = 'Lin';console.log(original.user.name); // 'Lin', mutation leaked// Deep copiesconst c = JSON.parse(JSON.stringify(original)); // lossyconst d = structuredClone(original); // preferred
Pick by use case:
{...obj} / Object.assign: fast, fine when you only mutate the top level.JSON.parse(JSON.stringify(...)): deep but lossy: undefined, functions, Date, Map, Set, RegExp, cycles all break. Avoid in 2026.structuredClone: deep, preserves Date/Map/Set and cycles. Default deep-copy choice.cloneDeep (lodash): handles things structuredClone doesn't (functions, prototypes), but at the cost of bundle size.The classic React bug, setState({ ...state, list: state.list }) followed by mutating state.list, is shallow-copy leakage.
structuredClone() and how is it different from JSON.parse(JSON.stringify(...))?structuredClone() is a built-in that deep-clones a value using the structured clone algorithm. Unlike the JSON round-trip, it handles Date, Map, Set, RegExp, ArrayBuffer, typed arrays, and cyclic references correctly.
const original = {date: new Date(),map: new Map([['k', 'v']]),set: new Set([1, 2, 3]),};original.self = original; // cycleconst copy = structuredClone(original);console.log(copy.date instanceof Date); // trueconsole.log(copy.map instanceof Map); // trueconsole.log(copy.self === copy); // true, cycle preserved
It does not clone functions, DOM nodes, or prototypes. For plain-data deep copies, prefer structuredClone over the JSON trick; it's safer and faster.
#field)?Private fields, prefixed with #, are accessible only inside the class that declares them. They're enforced by the language, not a naming convention like _field.
class Counter {#count = 0;increment() {this.#count++;}get value() {return this.#count;}}const c = new Counter();c.increment();console.log(c.value); // 1console.log(c.#count); // SyntaxError
Subclasses can't reach private fields of their parents, and Object.keys() / for...in won't enumerate them. Use them when you need true encapsulation: internal state that consumers (or future subclasses) must not touch.
CommonJS (require / module.exports) is Node.js's legacy module system. ES Modules (import / export) are the standard, supported in browsers and modern Node.js.
// CommonJSconst fs = require('fs');module.exports = { foo: 1 };// ES Modulesimport fs from 'node:fs';export const foo = 1;
Key differences:
await.import from CommonJS, but a CommonJS require of ESM needs await import().New code should default to ESM. Use CommonJS only when targeting old Node or legacy tooling.
Explore the differences between CommonJS and ES Modules on GreatFrontEnd ->
import() and when would you use it?Dynamic import() is a function-like syntax that loads a module at runtime and returns a promise resolving to its namespace object. Unlike static import, it can take a variable specifier and run conditionally.
button.addEventListener('click', async () => {const { default: Chart } = await import('./Chart.js');new Chart(document.getElementById('canvas')).render();});
Common uses:
Most bundlers (webpack, Vite, esbuild) treat dynamic import() as a code-split boundary automatically.
A tagged template literal lets a function process a template string. The tag function receives the static string parts as its first argument and the interpolated values as the rest.
function html(strings, ...values) {return strings.reduce((out, str, i) => {const safe = values[i] != null ? escapeHtml(values[i]) : '';return out + str + safe;}, '');}const name = '<script>alert(1)</script>';const out = html`<p>Hello, ${name}!</p>`;// "<p>Hello, <script>alert(1)</script>!</p>"
Use them for safe HTML/SQL building, i18n message formatting, GraphQL queries (gql`...`), or styled-components-style CSS-in-JS.
Explore tagged templates on GreatFrontEnd ->
String.prototype.replaceAll() do?replaceAll() (ES2021) replaces every occurrence of a substring or pattern. Before it, replace with a string argument only replaced the first match, forcing a global regex (/.../g) for the common case.
const s = 'cat, cat, cat';console.log(s.replace('cat', 'dog')); // 'dog, cat, cat'console.log(s.replaceAll('cat', 'dog')); // 'dog, dog, dog'console.log(s.replaceAll(/CAT/gi, 'dog')); // 'dog, dog, dog'
If you pass a regex, it must have the g flag; otherwise replaceAll throws. The function form of the second argument is supported, same as replace.
BigInt and when should you use it?BigInt is a primitive type for integers of arbitrary size, written with an n suffix or via BigInt(...). Standard number loses precision beyond 2^53 - 1 (Number.MAX_SAFE_INTEGER); BigInt does not.
const big = 9007199254740993n;console.log(big + 1n); // 9007199254740994nconsole.log(Number.MAX_SAFE_INTEGER + 2); // 9007199254740992, wrong
Caveats: you can't mix BigInt and Number in arithmetic (1n + 1 throws), Math.* doesn't accept BigInt, and JSON.stringify throws on BigInt. Reach for it when handling 64-bit IDs, monetary values in minor units, cryptography, or timestamps in nanoseconds: anywhere 2^53 is a real ceiling.
That's the list. The next step is practice: work through each question until you can explain the concept and write the code from scratch without looking.
More JavaScript interview prep:
A broader set of +190 JavaScript interview questions is also available in our GitHub repo.

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

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

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

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

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

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

HTML, CSS, and JavaScript are fundamental skills for any aspiring web developer, and securing a job in this field can be a challenging endeavor, especially for beginners. A critical part of the interview process is the technical interview, where your proficiency in these core web technologies is thoroughly assessed.
To help you prepare and boost your confidence, we’ve compiled a list of the top 50 essential interview questions and answers covering HTML, CSS, and JavaScript that are frequently asked in interviews. These are followed by two supplementary sections: additional HTML-focused questions that are increasingly common in modern front-end rounds, and integration questions that examine how HTML, CSS, and JavaScript interact in practice. Where relevant, questions include what interviewers look for in a strong answer, as well as nuances that are commonly overlooked in surface-level explanations.
If you're looking for additional JavaScript interview preparation materials, also check out these resources:
Hoisting refers to JavaScript's behavior of moving variable and function declarations to the top of their scope during the compilation phase. While declarations are hoisted, initializations are not.
console.log(foo); // undefinedvar foo = 1;console.log(foo); // 1
Visualized as:
var foo;console.log(foo); // undefinedfoo = 1;console.log(foo); // 1
let, const, and classThese are hoisted but remain uninitialized, leading to a ReferenceError if accessed before declaration.
console.log(bar); // ReferenceErrorlet bar = 'value';
Function declarations are fully hoisted (both declaration and definition), while function expressions are only partially hoisted (declaration without initialization).
console.log(declared()); // Worksfunction declared() {return 'Declared function';}console.log(expr); // undefinedconsole.log(expr()); // TypeError: expr is not a functionvar expr = function () {return 'Function expression';};
Import statements are hoisted, making imported modules available throughout the file.
import foo from './foo';foo.doSomething(); // Accessible
Explore the concept of "hoisting" in JavaScript on GreatFrontEnd
let, var, and const Differ?var: Function-scoped or globally scoped.let and const: Block-scoped, confined to their nearest enclosing block.function test() {var a = 1;let b = 2;const c = 3;}console.log(a); // ReferenceErrorconsole.log(b); // ReferenceErrorconsole.log(c); // ReferenceError
var and let: Can be declared without initialization.const: Must be initialized during declaration.var a;let b;const c; // SyntaxError: Missing initializer
var: Allows redeclaration in the same scope.let and const: Redeclaration is not allowed.var x = 1;var x = 2; // Validlet y = 1;let y = 2; // SyntaxError
var and let: Reassignment is allowed.const: Reassignment is not allowed.const z = 1;z = 2; // TypeError
var: Hoisted and initialized to undefined.let and const: Hoisted but not initialized, causing a ReferenceError if accessed before declaration.console.log(a); // undefinedvar a = 1;console.log(b); // ReferenceErrorlet b = 2;
Explore the differences between let, var, and const on GreatFrontEnd
== and ===?==):42 == '42'; // true0 == false; // truenull == undefined; // true
===):42 === '42'; // false0 === false; // falsenull === undefined; // false
Prefer === to avoid unexpected behavior caused by type coercion, except when comparing against null or undefined.
var value = null;console.log(value == null); // trueconsole.log(value === null); // true
Explore the difference between == and === on GreatFrontEnd
The event loop allows JavaScript to handle asynchronous tasks on a single thread, ensuring smooth execution without blocking.
setTimeout and UI events.Promise callbacks.console.log('Start');setTimeout(() => console.log('Timeout'), 0);Promise.resolve().then(() => console.log('Promise'));console.log('End');
Output:
StartEndPromiseTimeout
Explore the event loop in JavaScript on GreatFrontEnd
Event delegation uses a single event listener on a parent element to manage events on its child elements. This approach takes advantage of event bubbling, improving efficiency.
document.getElementById('parent').addEventListener('click', (event) => {if (event.target.tagName === 'BUTTON') {console.log(`Clicked ${event.target.textContent}`);}});
Explore event delegation in JavaScript on GreatFrontEnd
this Work in JavaScript?The value of this depends on how a function is invoked:
window in browsers) in non-strict mode. In strict mode, which is the default inside ES modules and class bodies, the default binding is undefined.call, apply, or bind.this from the surrounding scope.const obj = {name: 'Alice',greet() {console.log(this.name);},};obj.greet(); // Alice
Explore how this works in JavaScript on GreatFrontEnd
localStorage, and sessionStorage Differ?document.cookie = 'token=abc123; expires=Fri, 31 Dec 2025 23:59:59 GMT; path=/';console.log(document.cookie);
localStorage:localStorage.setItem('key', 'value');console.log(localStorage.getItem('key'));
sessionStorage:sessionStorage.setItem('key', 'value');console.log(sessionStorage.getItem('key'));
Explore the difference between cookies, localStorage, and sessionStorage on GreatFrontEnd
<script>, <script async>, and <script defer>?<script>:<script async>:<script defer>:<script src="main.js"></script><script async src="async.js"></script><script defer src="defer.js"></script>
Explore the difference between <script>, <script async>, and <script defer> on GreatFrontEnd
null, undefined, and Undeclared Variables Differ?null:Explicitly represents no value. Use === to check.
undefined:Indicates a variable has been declared but not assigned a value.
Variables not declared will throw a ReferenceError.
let a;console.log(a); // undefinedlet b = null;console.log(b); // null
Explore the difference between null, undefined, and undeclared variables on GreatFrontEnd
.call and .apply?.call:Accepts arguments as a comma-separated list.
.apply:Accepts arguments as an array.
function sum(a, b) {return a + b;}console.log(sum.call(null, 1, 2)); // 3console.log(sum.apply(null, [1, 2])); // 3
Explore the difference between .call and .apply on GreatFrontEnd
Function.prototype.bind and Why Is It Useful?The Function.prototype.bind method allows you to create a new function with a specific this context and optional preset arguments. It’s particularly useful for ensuring a function has the correct this context when passed to another function or used as a callback.
const john = {age: 42,getAge: function () {return this.age;},};console.log(john.getAge()); // 42const unboundGetAge = john.getAge;console.log(unboundGetAge()); // undefinedconst boundGetAge = john.getAge.bind(john);console.log(boundGetAge()); // 42const mary = { age: 21 };const boundGetAgeMary = john.getAge.bind(mary);console.log(boundGetAgeMary()); // 21
this: bind is often used to fix the this value for a method, ensuring it always refers to the intended object.bind.bind allows methods from one object to be used on another object.Explore Function.prototype.bind on GreatFrontEnd
Arrow functions automatically bind the this value to the surrounding lexical scope, which eliminates issues with context in methods. This behavior makes code more predictable and easier to debug.
const Person = function (name) {this.name = name;this.sayName1 = function () {console.log(this.name);};this.sayName2 = () => {console.log(this.name);};};const john = new Person('John');const dave = new Person('Dave');john.sayName1(); // Johnjohn.sayName2(); // Johnjohn.sayName1.call(dave); // Davejohn.sayName2.call(dave); // John
this when passed as a callback, such as event handlers assigned to DOM elements or methods passed to third-party APIs. While modern React favors functional components with hooks, this pattern is still relevant in class-based components, Web Components, and plain JavaScript modules.Explore the advantage of using the arrow syntax for a method in a constructor on GreatFrontEnd
Prototypal inheritance allows objects to inherit properties and methods from other objects through the prototype chain.
Every JavaScript object has a prototype, which is another object from which it inherits properties.
function Person(name, age) {this.name = name;this.age = age;}Person.prototype.sayHello = function () {console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);};const john = new Person('John', 30);john.sayHello(); // Hello, my name is John and I am 30 years old.
JavaScript looks for properties and methods on the object and continues up the chain until it finds the property or reaches null.
Used with new to create objects and set their prototype.
function Animal(name) {this.name = name;}Animal.prototype.sayName = function () {console.log(`My name is ${this.name}`);};function Dog(name, breed) {Animal.call(this, name);this.breed = breed;}Dog.prototype = Object.create(Animal.prototype);Dog.prototype.bark = function () {console.log('Woof!');};const fido = new Dog('Fido', 'Labrador');fido.sayName(); // My name is Fidofido.bark(); // Woof!
Explore how prototypal inheritance works on GreatFrontEnd
function Person(){}, const person = Person(), and const person = new Person()?function Person() {} is a standard function declaration. When written in PascalCase, it conventionally represents a constructor function.
const person = Person() calls the function and executes its code but does not create a new object.
const person = new Person() creates a new object, setting its prototype to Person.prototype.
function foo() {console.log('Function declaration');}
const foo = function () {console.log('Function expression');};
Explore the differences between function declarations and expressions on GreatFrontEnd
const person = { firstName: 'John', lastName: 'Doe' };
Object() Constructor:const person = new Object();person.firstName = 'John';person.lastName = 'Doe';
Object.create():const proto = {greet() {console.log('Hello!');},};const person = Object.create(proto);person.greet(); // Hello!
class Person {constructor(name, age) {this.name = name;this.age = age;}}
Explore ways to create objects in JavaScript on GreatFrontEnd
Higher-order functions either:
function multiplier(factor) {return function (number) {return number * factor;};}const double = multiplier(2);console.log(double(5)); // 10
Explore higher-order functions on GreatFrontEnd
function Person(name) {this.name = name;}Person.prototype.greet = function () {console.log(`Hello, I’m ${this.name}`);};
class Person {constructor(name) {this.name = name;}greet() {console.log(`Hello, I’m ${this.name}`);}}
Key Differences:
extends and super.Explore ES2015 classes and ES5 constructors on GreatFrontEnd
Event bubbling is when an event starts at the target element and propagates up through its ancestors.
parent.addEventListener('click', () => console.log('Parent clicked'));child.addEventListener('click', () => console.log('Child clicked'));
Clicking the child triggers both handlers.
Explore event bubbling on GreatFrontEnd
Event capturing is when an event starts at the root and propagates down to the target element.
parent.addEventListener('click', () => console.log('Parent capturing'), true);
Explore event capturing on GreatFrontEnd
mouseenter and mouseover Events Differ in JavaScript and Browsers?mouseentermouseoverExample:
const fs = require('fs');const data = fs.readFileSync('large-file.txt', 'utf8');console.log(data); // Blocks until file is readconsole.log('End of the program');
Example:
console.log('Start of the program');fetch('https://api.example.com/data').then((response) => response.json()).then((data) => console.log(data)) // Non-blocking.catch((error) => console.error(error));console.log('End of program');
Understand the distinctions between synchronous and asynchronous functions on GreatFrontEnd
AJAX (Asynchronous JavaScript and XML) encompasses a collection of web development techniques that utilize various client-side technologies to build asynchronous web applications. Unlike traditional web applications where every user interaction results in a complete page reload, AJAX enables web apps to send and retrieve data from a server asynchronously. This allows for dynamic updates to specific parts of a web page without disrupting the overall page display and behavior.
Key Highlights:
XMLHttpRequest, though fetch() is now the preferred choice for modern web development.XMLHttpRequest APIExample:
let xhr = new XMLHttpRequest();xhr.onreadystatechange = function () {if (xhr.readyState === XMLHttpRequest.DONE) {if (xhr.status === 200) {console.log(xhr.responseText);} else {console.error('Request failed: ' + xhr.status);}}};xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos/1', true);xhr.send();
XMLHttpRequest, assigns a callback to handle state changes, opens a connection to a specified URL, and sends the request.fetch() APIExample:
fetch('https://jsonplaceholder.typicode.com/todos/1').then((response) => {if (!response.ok) {throw new Error('Network response was not ok');}return response.json();}).then((data) => console.log(data)).catch((error) => console.error('Fetch error:', error));
.then() to parse JSON data, and handles errors using .catch().fetchfetch() starts an asynchronous request to obtain a resource from a given URL.
Example:
fetch('https://api.example.com/data', {method: 'GET', // or 'POST', 'PUT', 'DELETE', etc.headers: {'Content-Type': 'application/json',},});
fetch() returns a Promise that resolves to a Response object representing the server's reply.The Response object provides methods to handle the content, such as .json(), .text(), and .blob().
Example:
fetch('https://api.example.com/data').then((response) => response.json()).then((data) => console.log(data)).catch((error) => console.error('Error:', error));
fetch() operates asynchronously, allowing the browser to perform other tasks while awaiting the server's response..then(), .catch()) are processed in the microtask queue as part of the event loop.fetch() allows configuration of various request settings, including HTTP method, headers, body, credentials, and caching behavior..catch() or try/catch with async/await.Learn how to explain AJAX in detail on GreatFrontEnd
AJAX (Asynchronous JavaScript and XML) facilitates the asynchronous exchange of data between web pages and servers, enabling dynamic content updates without necessitating full page reloads.
pushState, replaceState) to keep URLs in sync with application state; otherwise, users cannot bookmark or share specific views.Explore the benefits and drawbacks of using AJAX on GreatFrontEnd
XMLHttpRequest and fetch() Differ?Both XMLHttpRequest (XHR) and fetch() facilitate asynchronous HTTP requests in JavaScript, but they vary in syntax, handling mechanisms, and features.
setRequestHeader method.send method.body property within the options parameter is used to include the request body.responseType property to manage different response formats.Response object with .then methods for accessing data.onerror event..catch method.abort() method.AbortController for canceling requests.onprogress event.Choosing Between Them: fetch() is generally favored for its cleaner syntax and Promise-based handling, though XMLHttpRequest remains useful for specific scenarios like progress tracking.
Discover the distinctions between XMLHttpRequest and fetch() on GreatFrontEnd
JavaScript encompasses a variety of data types, which are categorized into two main groups: primitive and non-primitive (reference) types.
true or false.Identifying Data Types: JavaScript is dynamically typed, meaning variables can hold different types of data at various times. The typeof operator is used to determine a variable's type.
Explore the variety of data types in JavaScript on GreatFrontEnd
Looping through object properties and array items is a fundamental task in JavaScript, and there are multiple methods to accomplish this. Below are some of the common approaches:
for...in LoopIterates over all enumerable properties of an object, including inherited ones.
for (const property in obj) {if (Object.hasOwn(obj, property)) {console.log(property);}}
Object.keys()Returns an array containing the object's own enumerable property names.
Object.keys(obj).forEach((property) => console.log(property));
Object.entries()Provides an array of the object's own enumerable string-keyed [key, value] pairs.
Object.entries(obj).forEach(([key, value]) => console.log(`${key}: ${value}`));
Object.getOwnPropertyNames()Returns an array of all properties (including non-enumerable ones) directly found on the object.
Object.getOwnPropertyNames(obj).forEach((property) => console.log(property));
for LoopA traditional loop for iterating over array elements.
for (let i = 0; i < arr.length; i++) {console.log(arr[i]);}
Array.prototype.forEach()Executes a provided function once for each array element.
arr.forEach((element, index) => console.log(element, index));
for...of LoopIterates over iterable objects like arrays.
for (let element of arr) {console.log(element);}
Array.prototype.entries()Provides both the index and value of each array element within a for...of loop.
for (let [index, elem] of arr.entries()) {console.log(index, ': ', elem);}
Introduced in ES2015, the spread syntax (...) is a powerful feature for copying and merging arrays and objects without altering the originals. It's widely used in functional programming, Redux, and RxJS.
Cloning Arrays/Objects: Creates shallow copies.
const array = [1, 2, 3];const newArray = [...array]; // [1, 2, 3]const obj = { name: 'John', age: 30 };const newObj = { ...obj, city: 'New York' }; // { name: 'John', age: 30, city: 'New York' }
Combining Arrays/Objects: Merges them into a new entity.
const arr1 = [1, 2, 3];const arr2 = [4, 5, 6];const mergedArray = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]const obj1 = { foo: 'bar' };const obj2 = { qux: 'baz' };const mergedObj = { ...obj1, ...obj2 }; // { foo: 'bar', qux: 'baz' }
Passing Function Arguments: Spreads array elements as individual arguments.
const numbers = [1, 2, 3];Math.max(...numbers); // Equivalent to Math.max(1, 2, 3)
Array vs. Object Spreads: Only iterables can be spread into arrays, while arrays can also be spread into objects.
const array = [1, 2, 3];const obj = { ...array }; // { 0: 1, 1: 2, 2: 3 }
The rest syntax (...) collects multiple elements into an array or object, functioning as the opposite of spread syntax.
Function Parameters: Gathers remaining arguments into an array.
function addFiveToNumbers(...numbers) {return numbers.map((x) => x + 5);}const result = addFiveToNumbers(4, 5, 6, 7); // [9, 10, 11, 12]
Array Destructuring: Collects remaining elements into a new array.
const [first, second, ...remaining] = [1, 2, 3, 4, 5];// first: 1, second: 2, remaining: [3, 4, 5]
Object Destructuring: Gathers remaining properties into a new object.
const { e, f, ...others } = { e: 1, f: 2, g: 3, h: 4 };// e: 1, f: 2, others: { g: 3, h: 4 }
Rest Parameter Rules: Must be the final parameter in a function.
function addFiveToNumbers(arg1, ...numbers, arg2) {// Error: Rest element must be last element.}
Understand the benefits of spread syntax and how it differs from rest syntax on GreatFrontEnd
Map Object Differ from a Plain Object in JavaScript?size property to easily determine the number of key-value pairs.forEach, keys(), values(), and entries().Object.keys(), Object.values(), or Object.entries() to iterate.// Mapconst map = new Map();map.set('key1', 'value1');map.set({ key: 'key2' }, 'value2');console.log(map.size); // 2, the object reference is preserved as a distinct key// Plain Objectconst obj = { key1: 'value1' };obj[{ key: 'key2' }] = 'value2';console.log(Object.keys(obj)); // ['key1', '[object Object]']// The object key has been coerced to the string "[object Object]"
Discover the differences between a Map object and a plain object in JavaScript on GreatFrontEnd
Map/Set and WeakMap/WeakSet?The primary distinctions between Map/Set and WeakMap/WeakSet in JavaScript are outlined below:
Key Types:
Map and Set accept keys of any type, including objects, primitives, and functions.WeakMap and WeakSet exclusively use objects as keys, disallowing primitive values like strings or numbers.Memory Management:
Map and Set maintain strong references to their keys and values, preventing their garbage collection.WeakMap and WeakSet use weak references for keys (objects), allowing garbage collection if there are no other strong references.Key Enumeration:
Map and Set have enumerable keys that can be iterated over.WeakMap and WeakSet do not allow enumeration of keys, making it impossible to retrieve lists of keys or values directly.Size Property:
Map and Set provide a size property indicating the number of elements.WeakMap and WeakSet lack a size property since their size can change due to garbage collection.Use Cases:
Map and Set are suitable for general-purpose data storage and caching.WeakMap and WeakSet are ideal for storing metadata or additional object-related information without preventing the objects from being garbage collected when they are no longer needed.Learn about the differences between Map/Set and WeakMap/WeakSet on GreatFrontEnd
=> Function Syntax?One effective application of JavaScript's arrow function syntax is streamlining callback functions, especially when concise, inline function definitions are needed. Consider the following example:
Scenario: Doubling Array Elements with map
Imagine you have an array of numbers and you want to double each number using the map method.
// Traditional function syntaxconst numbers = [1, 2, 3, 4, 5];const doubledNumbers = numbers.map(function (number) {return number * 2;});console.log(doubledNumbers); // Output: [2, 4, 6, 8, 10]
By utilizing arrow function syntax, the same outcome can be achieved more succinctly:
// Arrow function syntaxconst numbers = [1, 2, 3, 4, 5];const doubledNumbers = numbers.map((number) => number * 2);console.log(doubledNumbers); // Output: [2, 4, 6, 8, 10]
Explore a use case for the new arrow => function syntax on GreatFrontEnd
In the realm of asynchronous programming, a callback function is passed as an argument to another function and is executed once a particular task completes, such as data retrieval or handling input/output operations. Here's a straightforward explanation:
function fetchData(callback) {setTimeout(() => {const data = { name: 'John', age: 30 };callback(data);}, 1000);}fetchData((data) => {console.log(data); // { name: 'John', age: 30 }});
Explore the concept of a callback function in asynchronous operations on GreatFrontEnd
Debouncing and throttling are techniques used to control the rate at which functions are executed, optimizing performance and managing event-driven behaviors in JavaScript applications.
Debouncing: Delays the execution of a function until a specified period has elapsed since its last invocation. This is particularly useful for scenarios like handling search input where you want to wait until the user has finished typing before executing a function.
function debounce(func, delay) {let timeoutId;return (...args) => {clearTimeout(timeoutId);timeoutId = setTimeout(() => func.apply(this, args), delay);};}
Throttling: Restricts a function to be executed no more than once within a given timeframe. This is beneficial for handling events that fire frequently, such as window resizing or scrolling.
function throttle(func, limit) {let inThrottle;return (...args) => {if (!inThrottle) {func.apply(this, args);inThrottle = true;setTimeout(() => (inThrottle = false), limit);}};}
These strategies help in enhancing application performance by preventing excessive function calls.
Explore the concept of debouncing and throttling on GreatFrontEnd
Destructuring assignment in JavaScript provides a concise way to extract values from arrays or properties from objects into individual variables.
// Array destructuringconst [a, b] = [1, 2];// Object destructuringconst { name, age } = { name: 'John', age: 30 };
This syntax employs square brackets for arrays and curly braces for objects, allowing for streamlined variable assignments directly from data structures.
Explore the concept of destructuring assignment for objects and arrays on GreatFrontEnd
Hoisting in JavaScript refers to the behavior where function declarations are moved to the top of their containing scope during the compilation phase. This allows functions to be invoked before their actual definition in the code. Conversely, function expressions and arrow functions must be defined prior to their invocation to avoid errors.
// Function declarationhoistedFunction(); // Works finefunction hoistedFunction() {console.log('This function is hoisted');}// Function expressionnonHoistedFunction(); // Throws an errorvar nonHoistedFunction = function () {console.log('This function is not hoisted');};
Explore the concept of hoisting with regards to functions on GreatFrontEnd
In ES2015, JavaScript introduces the class syntax with the extends keyword, enabling one class to inherit properties and methods from another. The super keyword is used to access the parent class's constructor and methods.
class Animal {constructor(name) {this.name = name;}speak() {console.log(`${this.name} makes a noise.`);}}class Dog extends Animal {constructor(name, breed) {super(name);this.breed = breed;}speak() {console.log(`${this.name} barks.`);}}const dog = new Dog('Rex', 'German Shepherd');dog.speak(); // Output: Rex barks.
In this example, the Dog class inherits from the Animal class, demonstrating how classes facilitate inheritance and method overriding in JavaScript.
Explore the concept of inheritance in ES2015 classes on GreatFrontEnd
Lexical scoping in JavaScript determines how variable names are resolved based on their location within the source code. Nested functions have access to variables from their parent scopes, enabling them to utilize and manipulate these variables.
function outerFunction() {let outerVariable = 'I am outside!';function innerFunction() {console.log(outerVariable); // 'I am outside!'}innerFunction();}outerFunction();
In this scenario, innerFunction can access outerVariable because of lexical scoping rules, which allow inner functions to access variables defined in their outer scope.
Explore the concept of lexical scoping on GreatFrontEnd
Scope in JavaScript defines the accessibility of variables and functions in different parts of the code. There are three primary types of scope:
let or const within a block (e.g., within {}) are accessible only within that block.// Global scopevar globalVar = 'I am global';function myFunction() {// Function scopevar functionVar = 'I am in a function';if (true) {// Block scopelet blockVar = 'I am in a block';console.log(blockVar); // Accessible here}// console.log(blockVar); // Throws an error}console.log(globalVar); // Accessible here// console.log(functionVar); // Throws an error
In this example, globalVar is accessible globally, functionVar is confined to myFunction, and blockVar is restricted to the if block.
Explore the concept of scope in JavaScript on GreatFrontEnd
The spread operator (...) in JavaScript allows iterable elements (like arrays or objects) to be expanded into individual elements. It's versatile and can be used for copying, merging, and passing array elements as function arguments.
// Copying an arrayconst arr1 = [1, 2, 3];const arr2 = [...arr1];// Merging arraysconst arr3 = [4, 5, 6];const mergedArray = [...arr1, ...arr3];// Copying an objectconst obj1 = { a: 1, b: 2 };const obj2 = { ...obj1 };// Merging objectsconst obj3 = { c: 3, d: 4 };const mergedObject = { ...obj1, ...obj3 };// Passing array elements as function argumentsconst sum = (x, y, z) => x + y + z;const numbers = [1, 2, 3];console.log(sum(...numbers)); // Output: 6
The spread operator simplifies operations such as copying arrays or objects, merging multiple arrays or objects into one, and spreading elements of an array as individual arguments to functions.
Explore the concept of the spread operator and its uses on GreatFrontEnd
this Binding Work in Event Handlers?In JavaScript, the this keyword refers to the object that is executing the current piece of code. Within event handlers, this typically points to the DOM element that triggered the event. However, its value can change depending on how the handler is defined and invoked. To ensure this references the intended context, techniques like using bind(), arrow functions, or explicitly setting the context are employed.
These methods help maintain the correct reference for this within event handling functions, ensuring consistent and predictable behavior across various event-driven scenarios in JavaScript applications.
Explore the concept of this binding in event handlers on GreatFrontEnd
A Block Formatting Context (BFC) is a pivotal concept in CSS that influences how block-level elements are rendered and interact on a webpage. It creates an isolated environment where block boxes are laid out, ensuring that elements like floats, absolutely positioned elements, inline-blocks, table-cells, table-captions, and those with an overflow value other than visible (except when propagated to the viewport) establish a new BFC.
Grasping how to initiate a BFC is essential because, without it, the containing box might fail to encompass floated child elements. This issue is akin to collapsing margins but is often more deceptive, causing entire boxes to collapse unexpectedly.
A BFC is formed when an HTML box satisfies at least one of the following criteria:
float property is set to a value other than none.position property is assigned a value that is neither static nor relative.display property is set to table-cell, table-caption, inline-block, flex, inline-flex, grid, or inline-grid.overflow property is set to a value other than visible.Within a BFC, each box's left outer edge aligns with the left edge of its containing block (or the right edge in right-to-left layouts). Additionally, vertical margins between adjacent block-level boxes within a BFC collapse into a single margin.
Discover Block Formatting Context (BFC) and its Operation on GreatFrontEnd
z-index and How is a Stacking Context Created?The z-index property in CSS manages the vertical stacking order of overlapping elements. It only influences positioned elements: those with a position value other than static, and their descendants or flex items.
In the absence of a z-index value, elements stack based on their order in the Document Object Model (DOM), with elements appearing later in the HTML markup rendered on top of earlier ones at the same hierarchy level. Positioned elements (those with non-static positioning) and their children will always overlay elements with default static positioning, regardless of their order in the HTML structure.
A stacking context is essentially a group of elements that share a common stacking order. Within a local stacking context, the z-index values of child elements are relative to that context rather than the entire document. Elements outside of this context, such as sibling elements of a local stacking context, cannot interpose between layers within it. For instance, if element B overlays element A, a child of element A, element C, cannot surpass element B in the stacking order even if it has a higher z-index than element B.
Each stacking context operates independently; after stacking its child elements, the entire context is treated as a single entity within the parent stacking context's order. Certain CSS properties, like an opacity less than 1, a filter that isn't none, or a transform that isn't none, can trigger the creation of a new stacking context.
Learn about z-index and Stacking Contexts on GreatFrontEnd
This topic relates to writing efficient CSS, specifically how browsers interpret and apply CSS selectors. Browsers process selectors from right to left, starting with the most specific (the key selector) and moving outward. They first identify all elements that match the rightmost part of the selector and then traverse up the DOM tree to verify if those elements meet the remaining parts of the selector.
For example, consider the selector p span. Browsers will first locate all <span> elements and then check each span's ancestor chain to determine if it is within a <p> element. Once a <p> ancestor is found for a given <span>, the browser confirms that the <span> matches the selector and ceases further traversal for that element.
The efficiency of selector matching is influenced by the length of the selector chain. The shorter the chain, the quicker the browser can verify matches.
Understand How Browsers Match CSS Selectors on GreatFrontEnd
The CSS box model is a fundamental concept that describes the rectangular boxes generated for elements in the document tree, determining how they are laid out and displayed. Each box comprises a content area (such as text or images) surrounded by optional padding, border, and margin areas.
The box model is responsible for calculating:
width, height, padding, and border.height is specified, a block element's height adjusts to its content plus padding (unless floats are involved).width is set, a non-floated block element expands to fit its parent's width minus padding, unless a max-width is specified.
table, figure, and input have inherent width values and may not expand fully.span do not have a default width and will not expand to fit.height and width are determined by its content.box-sizing: content-box), padding and border are not included in an element's width and height.Note: Margins do not contribute to the actual size of the box; they affect the space outside the box. The box's area is confined to the border and does not extend into the margin.
Understanding the box-sizing property is crucial as it alters how an element's height and width are calculated.
box-sizing: content-box: The default behavior where only the content size is considered.box-sizing: border-box: Includes padding and border in the element's total width and height, excluding margin.Many CSS frameworks adopt box-sizing: border-box globally for a more intuitive sizing approach.
Explore the Box Model and Its Control in CSS on GreatFrontEnd
display Property? Provide Examples.The display property in CSS dictates how an element is rendered in the document flow. Common values include none, block, inline, inline-block, flex, grid, table, table-row, table-cell, and list-item.
Description:
none
Hides the element; it does not occupy any space in the layout. All child elements are also hidden. The element is treated as if it does not exist in the DOM.
block
The element occupies the full width available, starting on a new line.
inline
The element does not start on a new line and only occupies as much width as necessary.
inline-block
Combines characteristics of both inline and block. The element flows with text but can have width and height set.
flex
Defines the element as a flex container, enabling the use of flexbox layout for its children.
grid
Defines the element as a grid container, allowing for grid-based layout of its children.
table
Makes the element behave like a <table> element.
table-row
Makes the element behave like a <tr> (table row) element.
table-cell
Makes the element behave like a <td> (table cell) element.
list-item
Makes the element behave like a <li> (list item) element, enabling list-specific styling such as list-style-type and list-style-position.
For a comprehensive list of display property values, refer to the CSS Display | MDN.
Understand the CSS display Property with Examples on GreatFrontEnd
relative, fixed, absolute, sticky, and static Positioning Differ?In CSS, an element's positioning is determined by its position property, which can be set to relative, fixed, absolute, sticky, or static. Here's how each behaves:
static: The default positioning. Elements flow naturally within the document. The top, right, bottom, left, and z-index properties have no effect.
relative: The element is positioned relative to its normal position. Adjustments using top, right, bottom, or left move the element without affecting the layout of surrounding elements, leaving a gap where it would have been.
absolute: The element is removed from the normal document flow and positioned relative to its nearest positioned ancestor (an ancestor with a position other than static). If no such ancestor exists, it positions relative to the initial containing block. Absolutely positioned elements do not affect the position of other elements and can have width and height specified.
fixed: Similar to absolute, but the element is positioned relative to the viewport, meaning it stays in the same place even when the page is scrolled.
sticky: A hybrid of relative and fixed. The element behaves like relative until it crosses a specified threshold (e.g., scroll position), after which it behaves like fixed, sticking to its position within its parent container.
Understanding these positioning schemes is vital for controlling the layout and behavior of elements, especially in responsive and dynamic designs.
Learn About Positioning Schemes in CSS on GreatFrontEnd
Designing and developing for multilingual websites involves various considerations to ensure accessibility and usability across different languages and cultures. This process is part of internationalization (i18n).
lang attribute on the <html> tag to specify the page's language.en_US, zh_CN).<link rel="alternate" hreflang="other_locale" href="url_for_other_locale"> to inform search engines about alternate language versions of the page.<link rel="alternate" href="url_for_fallback" hreflang="x-default" />.en-US vs. en-GB, zh-CN vs. zh-TW).Accept-Language headers and IP addresses.Dynamic Content: Instead of concatenating strings (e.g., "The date today is " + date), use template strings with parameter substitution to accommodate different grammar structures across languages.
Example:
// Englishconst message = `I will travel on ${date}`;// Chineseconst message = `我会在${date}出发`;
Understand Multilingual Design Considerations on GreatFrontEnd
block, inline, and inline-block Display Types Differ?The display property in CSS determines how elements are rendered on the page. The block, inline, and inline-block values have distinct behaviors and use cases:
| Property | block | inline-block | inline |
|---|---|---|---|
| Size | Fills up the width of its parent container. | Depends on content. | Depends on content. |
| Positioning | Start on a new line and tolerates no HTML elements next to it (except when you add float) | Flows along with other content and allows other elements beside it. | Flows along with other content and allows other elements beside it. |
Can specify width and height | Yes | Yes | No. Will ignore if being set. |
Can be aligned with vertical-align | No | Yes | Yes |
| Margins and paddings | All sides respected. | All sides respected. | Only horizontal sides respected. Vertical sides, if specified, do not affect layout. Vertical space it takes up depends on line-height, even though the border and padding appear visually around the content. |
| Float | - | - | Becomes like a block element where you can set vertical margins and paddings. |
| Use Cases | Layout elements like <div>, <p>, <section>. | Used for buttons, images, and form fields that need custom sizes but stay in line with text. | Links <a>, text formatting <span>, text styling - bold <b>, italics <i>. |
Learn the Differences Between block, inline, and inline-block on GreatFrontEnd
translate() Over Absolute Positioning, or Vice Versa?The translate() function is a part of the CSS transform property and offers a different approach to positioning compared to absolute positioning. Here's why you might choose one over the other:
Using translate():
position: relative.transform or opacity does not trigger browser reflows or repaints; instead, it initiates a composition layer. This results in smoother and more efficient animations, as translate() leverages the GPU for rendering..element {transform: translateX(50px);}
Using absolute Positioning:
.element {position: absolute;top: 20px;left: 30px;}
Why Choose translate()?
For animations and dynamic movements where performance and smoothness are critical, translate() is more efficient. It avoids the costly reflows associated with changing layout-affecting properties like top and left.
Why Choose absolute Positioning?
When you need to position elements precisely without regard to their original place in the document flow, absolute positioning is the way to go. It's essential for creating overlays, modals, and tooltips that need to appear in specific locations on the screen.
Understand When to Use translate() vs. Absolute Positioning on GreatFrontEnd
* { box-sizing: border-box; } Do and What Are Its Advantages?Applying * { box-sizing: border-box; } in your CSS ensures that all elements on the page use the border-box model for calculating their width and height.
By default, elements use box-sizing: content-box, where the width and height only account for the content area. When you set box-sizing: border-box, the width and height properties include the element's padding and border, but not the margin.
| Property | box-sizing: content-box (default) | box-sizing: border-box |
|---|---|---|
| content | Yes | Yes |
padding | No | Yes |
border | No | Yes |
margin | No | No |
padding and border within the width and height makes it easier to calculate the size of elements, aligning more closely with designers' expectations.padding or border to elements, as it doesn't alter the total size.box-sizing: border-box globally to maintain consistency and predictability in element sizing.Explore What * { box-sizing: border-box; } Does and Its Benefits on GreatFrontEnd
The following HTML-focused topics are among the most frequently asked in front-end interviews. They cover semantic markup, HTML5 features, meta tags, form validation, and link security, all foundational areas where a precise answer often distinguishes a strong candidate from one with only surface-level familiarity.
Semantic HTML is the practice of using HTML elements that describe the meaning of their content, rather than relying on generic containers styled to resemble specific structures. Elements such as <header>, <nav>, <main>, <article>, <section>, <aside>, and <footer> convey the role of the content they contain, while <div> and <span> are non-semantic and carry no inherent meaning.
<!-- Non-semantic: the purpose of each container is not expressed in the markup --><div class="header"><div class="nav">...</div></div><div class="main"><div class="article">...</div></div><div class="footer">...</div><!-- Semantic: the markup itself describes the page structure --><header><nav>...</nav></header><main><article>...</article></main><footer>...</footer>
Benefits of semantic HTML include:
<nav>, <main>, or landmark regions using assistive-technology shortcuts.<button>, for example, is keyboard-accessible, submits forms, and respects the operating system's focus styles without additional JavaScript or CSS.What interviewers look for: A strong answer names concrete semantic elements and explains their impact on assistive technology and SEO, rather than describing semantic HTML as simply "cleaner code". Awareness that <div> and <span> remain the correct choice when no semantic element applies is equally important; overusing semantic tags where they do not fit is as problematic as avoiding them.
Commonly overlooked nuance: Semantic HTML is not only about choosing the right tag; it is about the accessibility tree the browser constructs from the markup. A <div role="button"> is semantically a button to assistive technology but loses native keyboard activation, focus styling, and form-submission behavior, making <button> strictly preferable whenever it applies.
HTML5 became the current W3C standard in 2014 and has continued to evolve as part of the WHATWG HTML Living Standard. It introduced several major improvements over earlier versions of HTML.
The simplified <!DOCTYPE html>
HTML5 decouples the doctype from the legacy SGML and XHTML DTD system. A single line, <!DOCTYPE html>, is all that is required at the top of every HTML5 document and instructs the browser to render the page in standards mode.
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>Document</title></head><body>...</body></html>
Key HTML5 features
<header>, <nav>, <main>, <article>, <section>, <aside>, <footer>, <figure>, <time>, and others.<audio> and <video> elements remove the need for browser plugins such as Flash.<canvas> for bitmap rendering and native SVG support for vector graphics.email, url, number, date, color, range, tel, search) and validation attributes (required, pattern, min, max).localStorage and sessionStorage for client-side key-value storage.What interviewers look for: A strong answer groups features by category rather than listing them at random, and demonstrates awareness that HTML is now a Living Standard maintained by the WHATWG rather than a versioned specification. Candidates who can tie features to practical use cases, for example, "<canvas> for interactive graphics, <video> for native playback", demonstrate applied understanding rather than rote memorization.
Commonly overlooked nuance: The <!DOCTYPE html> declaration is case-insensitive but must still appear as the very first content in the document. Any whitespace or markup before it can trigger quirks mode in some browsers, producing subtle layout and box-model differences that are difficult to debug after the fact.
Meta tags are instructions placed within the <head> of an HTML document that describe the page to browsers, search engines, and social platforms. They are not visible to users directly but significantly influence how the page is rendered, discovered, and shared.
<head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><metaname="description"content="A concise summary of the page, typically 150-160 characters." /><meta name="robots" content="index, follow" /><!-- Open Graph for social sharing --><meta property="og:title" content="Page Title" /><metaproperty="og:description"content="Description shown when the page is shared." /><meta property="og:image" content="https://example.com/preview.png" /><meta property="og:type" content="website" /><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image" /></head>
Key meta tags and their purpose:
charset="UTF-8" declares the character encoding. It must appear within the first 1024 bytes of the document, so it is conventionally the first element in the <head>.name="viewport" controls layout on mobile devices. Without it, mobile browsers fall back to a desktop-width rendering and zoom out. The canonical value is width=device-width, initial-scale=1.name="description" provides the summary that search engines may display in result listings. It does not directly influence rankings, but a compelling description improves click-through rate.name="robots" instructs crawlers whether to index the page and follow its links. Common values include index, noindex, follow, and nofollow.og:*) and Twitter Card tags control how the page appears when shared on social platforms, including the preview title, description, and image.What interviewers look for: A strong answer separates the viewport meta tag (critical for responsive rendering) from SEO-focused tags (description, robots) and social-sharing tags (Open Graph, Twitter). Distinguishing name="description" (a search-engine hint) from og:description (a social-sharing hint) demonstrates practical familiarity with the ecosystem.
Commonly overlooked nuance: The keywords meta tag has been ignored by major search engines, including Google, since 2009; citing it as an SEO tool is a signal of outdated material. The viewport meta tag is also load-bearing for Core Web Vitals; an incorrect value can regress Cumulative Layout Shift on mobile devices even when the page appears visually correct.
HTML5 introduced native, browser-provided form validation through a combination of input types, attributes, and the Constraint Validation API. This allows basic client-side validation to be expressed declaratively in markup, with no JavaScript required for common cases.
<form><label><input type="email" name="email" required /></label><label>Password (8+ characters, at least one number)<inputtype="password"name="password"requiredminlength="8"pattern="^(?=.*\d).{8,}$" /></label><label>Age (18-100)<input type="number" name="age" min="18" max="100" required /></label><label>Website (optional)<input type="url" name="website" /></label><button type="submit">Sign up</button></form>
Native validation features include:
email, url, number, tel, date, color, range.required, minlength, maxlength, min, max, step, and pattern (a regular expression).element.checkValidity(), element.reportValidity(), and element.setCustomValidity(message) for customising error messages without replacing the native behavior.:valid, :invalid, :required, and :optional pseudo-classes, which allow styling based on validation state directly from CSS.What interviewers look for: A strong answer distinguishes between the three layers: input types (which enforce a format), constraint attributes (which enforce bounds), and the Constraint Validation API (which allows programmatic customization without sacrificing native behavior). Applying novalidate on the form and reimplementing everything in JavaScript is a common anti-pattern worth identifying.
Commonly overlooked nuance: Native validation messages are localized to the user's browser language, styled consistently with the operating system, and positioned near the offending field. These benefits are difficult to replicate with a custom JavaScript solution. Client-side validation also never replaces server-side validation; it is a user-experience enhancement, not a security measure, because tampering with the DOM trivially bypasses any client-side check.
target="_blank" Links Include rel="noopener noreferrer"?Opening a link in a new tab using target="_blank" has historically introduced both security and performance concerns. Modern best practice addresses them by adding the rel="noopener noreferrer" attribute.
<!-- Always use noopener (and typically noreferrer) with target="_blank" --><ahref="https://external-site.example"target="_blank"rel="noopener noreferrer">External site</a>
What each keyword does:
noopener prevents the newly opened page from accessing the original page through window.opener. Without it, the destination page can redirect the opener to a phishing site via window.opener.location = '...', a technique known as reverse tabnabbing.noreferrer prevents the browser from sending the Referer header to the destination, and also implies noopener. It is useful when the originating URL should not be disclosed to the third-party site.Modern browser defaults:
All major browsers (Chrome, Firefox, Safari, and Edge) now set rel="noopener" implicitly when target="_blank" is used. This change was rolled out progressively between 2020 and 2022. However, explicitly declaring rel="noopener noreferrer" remains recommended because:
noreferrer must still be added explicitly when the Referer header should be suppressed.What interviewers look for: A strong answer clearly explains the reverse-tabnabbing attack and distinguishes the roles of noopener and noreferrer. Awareness of the modern implicit default demonstrates currency with the platform without treating the explicit attribute as optional.
Commonly overlooked nuance: rel="noopener" only affects the opener relationship. It does not prevent the destination page from performing other actions, such as opening popups, attempting fingerprinting, or tracking the user. Stronger cross-origin isolation at the tab level requires additional mechanisms, such as the Cross-Origin-Opener-Policy HTTP header.
<article>, <section>, and <div>?Although these three elements can render identically by default, they convey distinct semantic meanings that influence the accessibility tree and the document outline.
<article> represents a self-contained, independently distributable piece of content, such as a blog post, product card, or comment. A helpful test is to consider whether the content would still make sense when syndicated or displayed in isolation.<section> represents a thematic grouping of content that belongs under a heading. A <section> without an accompanying heading is almost always the wrong element.<div> is a generic container with no semantic meaning, intended as a styling or scripting hook when no semantic element applies.<article><h2>How the event loop works</h2><section><h3>The call stack</h3><p>...</p></section><section><h3>The microtask queue</h3><p>...</p></section></article>
What interviewers look for: A strong answer considers whether a <section> has an appropriate heading before using it. Using <section> in place of <nav>, or applying <article> to decorative UI, indicates the element names have been memorized without an understanding of how assistive technologies interpret them.
Commonly overlooked nuance: These elements are frequently described as interchangeable semantic containers, but a <section> without an accessible name produces a generic region with no navigational value. Nesting <article> within another <article> also implicitly demotes the inner element within the document outline.
Front-end interviews increasingly focus on how the three core technologies interact rather than on any one in isolation. The following questions examine the boundaries where HTML, CSS, and JavaScript meet: subtle behaviors of one affect the others in ways that can surface as hard-to-diagnose bugs in production.
<link> Tags in <head> and JavaScript <script> Tags Just Before </body>?The placement of stylesheet and script tags directly affects how quickly a browser can display content to the user. The guidance traces back to the browser's rendering pipeline and the distinction between render-blocking and parser-blocking resources.
CSS in <head>
<head>, so delaying CSS also delays the visible render.JavaScript just before </body>
<script> tags are parser-blocking. When the browser encounters one during HTML parsing, it must pause parsing, download the script, execute it, and only then resume.<body> allows the entire document to be parsed and the initial paint to occur before any script runs.Modern alternatives with async and defer
<head><!-- CSS blocks rendering, so load as early as possible --><link rel="stylesheet" href="styles.css" /><!-- defer: downloads in parallel, runs after parsing completes, preserves order --><script src="app.js" defer></script><!-- async: downloads in parallel, runs as soon as available (order not guaranteed) --><script src="analytics.js" async></script></head>
With defer, scripts can safely be placed in the <head> because they are guaranteed to run only after HTML parsing completes, in document order. This is the preferred modern approach for application scripts.
What interviewers look for: A strong answer connects placement to the critical rendering path. The rationale is not stylistic, but about enabling parallel downloads and reducing first-paint time. Awareness of defer and async as modern alternatives indicates that the classic rule is a fallback for when those attributes are not an option, not an absolute requirement.
Commonly overlooked nuance: async scripts run as soon as they are downloaded, which can occur mid-parse. If one async script depends on another, their execution order is undefined. For ordered execution, for example, a library before a script that uses it, defer is the correct choice.
The critical rendering path is the sequence of steps the browser performs to convert HTML, CSS, and JavaScript into pixels on the screen. Understanding it is foundational to diagnosing performance issues, interpreting Core Web Vitals, and writing code that does not unnecessarily block rendering.
The five stages
display: none are excluded from the render tree; elements hidden with visibility: hidden remain in it.How HTML, CSS, and JavaScript each affect the path
<script> tags until each script has downloaded and executed.<head> is downloaded and parsed. CSS is also parser-blocking for any scripts that follow, because scripts may query computed styles.async and defer allow parallel downloading. defer preserves ordering and runs before DOMContentLoaded; async runs as soon as the download completes, in any order.Strategies for optimizing the critical rendering path
<link rel="preload"> or the media attribute.defer or async on scripts that do not need to run synchronously.<link rel="preconnect"> and <link rel="preload"> hints to give the browser early information about which resources will be needed.What interviewers look for: A strong answer names the five stages in order and identifies which resources block which stages. Connecting the critical rendering path to Core Web Vitals, particularly Largest Contentful Paint, demonstrates an understanding of the practical impact on measurable performance metrics.
Commonly overlooked nuance: Layout and paint are triggered not only during the initial load but on almost every JavaScript-driven style or DOM change. Reading offsetHeight immediately after every write forces a condition called layout thrashing, which can cause significant jank. Batching reads and writes, or scheduling work inside requestAnimationFrame, avoids the issue.
display: none, visibility: hidden, and opacity: 0 Differ for JavaScript APIs and Accessibility?All three techniques hide content visually, but they have substantially different effects on layout, interactivity, and assistive technology.
| Technique | Occupies layout space | Focusable via Tab | Exposed to screen readers | getBoundingClientRect dimensions |
|---|---|---|---|---|
display: none | No | No | No | All zeros |
visibility: hidden | Yes | No | No | Actual dimensions |
opacity: 0 | Yes | Yes | Yes | Actual dimensions |
aria-hidden="true"* | Yes | Yes | No | Actual dimensions |
* aria-hidden affects only the accessibility tree; it does not hide the element visually or remove it from the focus order. Combining aria-hidden="true" with tabindex="-1" is often required to fully remove an element from assistive-technology interaction.
Key implications for JavaScript and accessibility:
opacity: 0 remains fully interactive: it still receives pointer events, still enters the tab order, and is still read aloud by screen readers. Using it alone as a hiding technique is a frequent source of accessibility bugs.display: none cannot be measured through getBoundingClientRect or offsetWidth, and scroll positions cannot be computed while it is hidden. A common pattern when measurement is required before display is to temporarily apply visibility: hidden (which retains layout) rather than display: none.tabindex="-1" addresses focus only, while aria-hidden="true" addresses the accessibility tree only.What interviewers look for: Recognition that "hidden" is not a single concept. Strong answers distinguish between visual rendering, layout occupation, focusability, and accessibility-tree exposure, and select a technique that matches the intent. For transient hiding of an actionable element, the hidden attribute or display: none is usually correct; for visual-only transitions, pairing opacity with visibility at the end of the transition is safer.
Commonly overlooked nuance: Combining opacity: 0 with pointer-events: none addresses pointer input but does not remove the element from keyboard focus or screen-reader output. Accessible hiding requires attention to all four dimensions, not only visual appearance.
innerHTML Break Event Listeners Attached to Child Elements?This is a classic question at the intersection of HTML and JavaScript, and its failure mode is particularly subtle: no error is thrown, and the affected listeners simply stop firing.
When a value is assigned to innerHTML, the browser performs the following steps:
const list = document.getElementById('list');list.querySelector('.item').addEventListener('click', handleClick);// The listener is still attached and functional at this point.list.innerHTML += '<li class="item">New item</li>';// The original <li> has been destroyed and replaced; the listener no longer fires.
Recommended approaches, in order of preference:
list.addEventListener('click', (e) => {if (e.target.closest('.item')) handleClick(e);});
appendChild, insertAdjacentHTML, or replaceChildren.innerHTML update, which is fragile and generally discouraged.What interviewers look for: Recognition that reassigning innerHTML destroys existing descendants rather than simply updating the DOM. Adopting event delegation as the primary solution demonstrates senior-level understanding, whereas relying on re-attaching listeners after each update reflects a reactive rather than architectural approach.
Single-page applications, modals, toasts, and client-side route transitions all share a common challenge: the user's focus and the screen reader's reading context do not follow DOM changes automatically. They must be managed explicitly.
Three common scenarios illustrate the distinction:
Route transitions in a single-page application. Following navigation, focus should be moved to the new page's primary heading (<h1>) or to a <main tabindex="-1">, and the new page title should be announced through a live region. Without this, a screen reader user may remain positioned at the previous page's footer with no indication that the view has changed.
router.afterEach(() => {document.querySelector('main').focus();document.title = newTitle;});
Modals and dialogs. Focus should be trapped inside the dialog while it is open and returned to the triggering element when the dialog closes. The native <dialog> element manages this behavior automatically; custom modal implementations must replicate it manually.
const trigger = document.activeElement;dialog.showModal();dialog.addEventListener('close', () => trigger.focus());
Inline content insertion, such as form validation errors, search results, or toast notifications. Focus should generally remain with the user, who is often in the middle of typing. Instead, a live region (aria-live="polite" for results, role="alert" for errors) should be used to announce the update without disrupting the current interaction.
What interviewers look for: A strong answer distinguishes between types of DOM changes rather than applying a uniform rule. Moving focus on every DOM change is as disruptive as moving it on none. Senior-level responses separate navigational context changes (move focus), user-initiated overlays (trap focus and return it on close), and ambient updates such as toasts or search results (announce via a live region without moving focus).
Both concepts address the same underlying challenge: making a website usable across a range of browsers, devices, and network conditions. They approach the problem from opposite directions.
Progressive enhancement
A bottom-up approach that starts with a minimal, functional baseline of HTML and then layers on enhancements as the browsing environment supports them.
<!-- Functional without JavaScript: the form submits via a standard GET request --><form action="/search" method="GET"><input type="search" name="q" required /><button type="submit">Search</button></form><script>// JavaScript enhances the experience with inline results, but the non-JS// baseline still works if the script fails to load or execute.document.querySelector('form').addEventListener('submit', async (e) => {e.preventDefault();const results = await fetchResults(e.target.q.value);renderResults(results);});</script>
Graceful degradation
A top-down approach that starts with a fully-featured modern experience and ensures the site remains usable when features are missing.
<noscript> blocks, or polyfills.When each approach applies
What interviewers look for: A strong answer identifies both approaches as strategies for resilience rather than ideological opposites. Candidates who can describe scenarios in which each is appropriate, rather than asserting that one is universally correct, demonstrate the judgment expected of a senior front-end engineer.
Commonly overlooked nuance: Progressive enhancement is often mischaracterized as "making the site work without JavaScript". The more useful framing is layered functionality: the CSS layer should still work if JavaScript fails, the HTML layer should still work if CSS fails, and the text content should still be readable if all styling fails. This layered view applies equally within a JavaScript-heavy single-page application, where the initial server-rendered HTML can provide a usable baseline before hydration completes.
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.
If you prefer GitHub, our open source repo contains all +190 JavaScript interview questions. You’re welcome to explore, fork, or contribute to the collection.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

If you have 5+ years of JavaScript under your belt, the bar in interviews shifts. You're expected to know the language well, but what gets you hired is judgment: knowing which tradeoffs matter in production, where the language gets weird, and how the framework you use is built on top of it.
This article covers 20 questions that come up in senior and lead engineer loops at large tech companies, plus three open-ended scenarios that test depth rather than recall.
If you're looking for additional JavaScript interview preparation materials, also check out these resources:
Three open-ended scenarios that come up in senior loops. Unlike recall questions, there's no single right answer; what's being evaluated is how you decompose the problem.
"Your team's dashboard runs in a browser tab for 6+ hours per day. Users report it grows to ~1 GB of memory usage. Walk me through your debugging approach".
A solid answer walks through the layers:
Detached HTMLDivElement points to DOM nodes still referenced from JavaScript after they were removed from the tree (typical cause: an event listener on window/document that captured the node in a closure and was never removed). A growing count of plain Objects often points to an unbounded cache or Map that nothing evicts.setInterval not cleared on unmount, addEventListener on window/document without cleanup, observers (IntersectionObserver, MutationObserver, ResizeObserver) not disconnected, subscriptions to external stores not unsubscribed.What separates a strong answer is naming specific tools and constructor names, rather than just listing "common causes of memory leaks".
"Write the minimum-latency code for this dependency graph. Why might
Promise.allbe the wrong tool?"
async function loadCheckout(userId) {// Step 1 must complete before Step 3 because Step 3 needs the cart id.const cart = await getCart(userId);// Steps 2 and 3 are independent and can run concurrently.const [user, lineItems] = await Promise.all([getUser(userId),getCartItems(cart.id),]);return { cart, user, lineItems };}
Wrapping all three in Promise.all doesn't work; getCartItems needs cart.id, which isn't known until getCart resolves. Promise.all only parallelizes inputs that are already independent. When there's a dependency chain, await the dependency first, then Promise.all the leaves. (For a deeper comparison, see Promise.all vs Promise.allSettled.)
setInterval(updateUI, 16) for an animation: what's wrong with this?Three issues:
setInterval doesn't synchronize with paint. You'll drift in and out of phase with the refresh cycle, getting visible stutter. requestAnimationFrame schedules work to run right before the next paint.setInterval is throttled in background tabs (Chrome drops to ~1 Hz, then to ~1/min under "intensive throttling" after 5 minutes hidden), but it still fires. requestAnimationFrame simply pauses while the tab is hidden, saving CPU and battery.setInterval will fire as soon as the previous one finishes, with no breathing room. rAF naturally skips a frame and resumes on the next paint.For animations driven from JS, requestAnimationFrame is the default. For purely declarative timing (e.g. fading in an element from 0 to 1 opacity over 300 ms), the Web Animations API or CSS transitions are usually a better fit since they run off the main thread.
For the questions below, what separates a passing answer from a strong one is rarely correctness; it's the depth of the connection drawn between the language feature and its real-world consequence:
| Question | Surface answer | Deeper answer |
|---|---|---|
| "What is a closure?" | "A function that remembers its scope" | "Functions retain access to their lexical environment after the outer function returns. This is what makes React hooks work; each useState call closes over a specific slot on the fiber. The flip side is that closures pin their captured variables in memory, which is why stale-closure bugs in useEffect happen when the deps array is wrong". |
| "How do Promises work?" | "They have .then and .catch" | "Promises are state machines: pending → fulfilled or rejected, with handlers scheduled as microtasks (so they run before the next macrotask but after the current synchronous block). await desugars to .then, which is why await in a loop runs sequentially while Promise.all runs concurrently. Unhandled rejections fire an unhandledrejection event you can listen for globally to catch missing .catch paths". |
"What does this refer to?" | "The object that called the function" | "It depends on the call site, not where the function was defined: for a regular function it's the receiver, for arrow functions it's the lexical this from the enclosing scope, for new it's the new instance, and in strict mode it's undefined for an unbound call. Class fields with arrow functions are commonly used specifically to avoid this getting lost when a method is passed as a callback". |
The deeper answer in each case ties the language feature to a specific failure mode and, where it applies, to where the consequence shows up in a framework like React.
Anonymous functions provide a concise way to define functions, especially useful for simple operations or callbacks. They are commonly used in:
map(), filter(), and reduce().Some examples:
// Encapsulating Code using IIFE(function () {// Some code here.})();// CallbackssetTimeout(function () {console.log('Hello world!');}, 1000);// Functional programming constructsconst arr = [1, 2, 3];const double = arr.map(function (el) {return el * 2;});console.log(double); // [2, 4, 6]
Explore a typical use case for anonymous functions in JavaScript on GreatFrontEnd
A closure is a function that retains access to variables from its enclosing scope even after the outer function has finished executing. The function effectively carries its original environment with it.
function outerFunction() {const outerVar = 'I am outside of innerFunction';function innerFunction() {console.log(outerVar); // `innerFunction` can still access `outerVar`.}return innerFunction;}const inner = outerFunction(); // `inner` now holds a reference to `innerFunction`.inner(); // "I am outside of innerFunction"// Even though `outerFunction` has completed execution, `inner` still has access to variables defined inside `outerFunction`.
Closures are useful for:
Explore what a closure is in JavaScript, and why you would use one on GreatFrontEnd
Pros:
Avoid callback hell: Promises simplify nested callbacks.
// Callback hellgetData1((data) => {getData2(data, (data) => {getData3(data, (result) => {console.log(result);});});});
Sequential code: Easier to write and read using .then().
Parallel code: Simplifies managing multiple promises with Promise.all().
Promise.all([getData1(), getData2(), getData3()]).then((results) => {console.log(results);}).catch((error) => {console.error('Error:', error);});
Cons:
Explore the pros and cons of using Promises instead of callbacks in JavaScript on GreatFrontEnd
AbortController in JavaScript?AbortController allows you to cancel ongoing asynchronous operations like fetch requests. To use it:
const controller = new AbortController();
2.Pass the signal: Add the signal to the fetch request options.controller.abort() to cancel the request.Here is an example of how to use AbortControllers with the fetch() API:
const controller = new AbortController();const signal = controller.signal;fetch('YOUR API', { signal }).then((response) => {// Handle response}).catch((error) => {if (error.name === 'AbortError') {console.log('Request aborted');} else {console.error('Error:', error);}});// Call abort() to abort the requestcontroller.abort();
Some of its use cases can be:
fetch() request on a user actionExplore how to abort a web request using AbortController in JavaScript on GreatFrontEnd
In JavaScript it's very easy to extend a built-in/native object. You can simply extend a built-in object by adding properties and functions to its prototype.
String.prototype.reverseString = function () {return this.split('').reverse().join('');};console.log('hello world'.reverseString()); // Outputs 'dlrow olleh'// Instead of extending the built-in object, write a pure utility function to do it.function reverseString(str) {return str.split('').reverse().join('');}console.log(reverseString('hello world')); // Outputs 'dlrow olleh'
While this may seem like a good idea at first, it is dangerous in practice. Imagine your code uses a few libraries that both extend the Array.prototype by adding the same contains method, the implementations will overwrite each other and your code will have unpredictable behavior if these two methods do not work the same way.
Extending built-in objects can lead to issues such as:
Explore why extending built-in JavaScript objects is not a good idea on GreatFrontEnd
In the browser, the global scope refers to the top-level context where variables, functions, and objects are accessible throughout the code. This scope is represented by the window object. Variables and functions declared outside of any function or block (excluding modules) are added to the window object, making them globally accessible.
For example:
// This runs in the global scope, not within a module.let globalVar = 'Hello, world!';function greet() {console.log('Greetings from the global scope!');}console.log(window.globalVar); // 'Hello, world!'window.greet(); // 'Greetings from the global scope!'
In this example, globalVar and greet are attached to the window object and can be accessed from anywhere in the global scope.
Generally, it's advisable to avoid polluting the global namespace unless necessary. Key reasons include:
In JavaScript, modules are reusable pieces of code that encapsulate functionality, making it easier to manage, maintain, and structure your applications. Modules allow you to break down your code into smaller, manageable parts, each with its own scope.
CommonJS is an older module system that was initially designed for server-side JavaScript development with Node.js. It uses the require() function to load modules and the module.exports or exports object to define the exports of a module.
// my-module.jsconst value = 42;module.exports = { value };// main.jsconst myModule = require('./my-module.js');console.log(myModule.value); // 42
ES Modules (ECMAScript Modules) are the standardized module system introduced in ES6 (ECMAScript 2015). They use the import and export statements to handle module dependencies.
// my-module.jsexport const value = 42;// main.jsimport { value } from './my-module.js';console.log(value); // 42
Explore the differences between CommonJS modules and ES modules in JavaScript on GreatFrontEnd
Immutability is a core principle in functional programming but it has lots to offer to object-oriented programs as well.
Mutable objects in JavaScript allow for modifications to their properties and values after creation. This behavior is default for most objects.
let mutableObject = {name: 'John',age: 30,};// Modify the objectmutableObject.name = 'Jane';console.log(mutableObject); // Output: { name: 'Jane', age: 30 }
Mutable objects like mutableObject above can have their properties changed directly, making them flexible for dynamic updates.
In contrast, immutable objects cannot be modified once created. Any attempt to change their content results in the creation of a new object with the updated values.
const immutableObject = Object.freeze({name: 'John',age: 30,});// Attempting to modify the objectimmutableObject.name = 'Jane'; // This change won't affect the objectconsole.log(immutableObject); // Output: { name: 'John', age: 30 }
Here, immutableObject remains unchanged after creation due to Object.freeze(), which prevents modifications to its properties.
The primary difference lies in modifiability. Mutable objects allow changes to their properties directly, while immutable objects ensure the integrity of their initial state by disallowing direct modifications.
const vs immutable objectsA common confusion is that declaring a variable using const makes the value immutable, which is not true at all.
Using const prevents the reassignment of variables but doesn't make non-primitive values immutable.
// Using constconst person = { name: 'John' };person = { name: 'Jane' }; // Error: Assignment to constant variableperson.name = 'Jane'; // Allowed, person.name is now 'Jane'// Using Object.freeze() to create an immutable objectconst frozenPerson = Object.freeze({ name: 'John' });frozenPerson.name = 'Jane'; // Silently ignored in sloppy mode; throws TypeError in strict modefrozenPerson = { name: 'Jane' }; // Error: Assignment to constant variable
In the first example with const, reassigning a new object to person is not allowed, but modifying the name property is permitted. In the second example, Object.freeze() makes the frozenPerson object immutable, preventing any changes to its properties.
Explore the difference between mutable and immutable objects in JavaScript on GreatFrontEnd
Static class members in JavaScript, denoted by the static keyword, are accessed directly on the class itself, not on instances. They serve multiple purposes:
class Config {static API_KEY = 'your-api-key';static FEATURE_FLAG = true;}console.log(Config.API_KEY); // Output: 'your-api-key'console.log(Config.FEATURE_FLAG); // Output: true
class Arithmetic {static add(a, b) {return a + b;}static subtract(a, b) {return a - b;}}console.log(Arithmetic.add(2, 3)); // Output: 5console.log(Arithmetic.subtract(5, 2)); // Output: 3
class Singleton {static instance;static getInstance() {if (!this.instance) {this.instance = new Singleton();}return this.instance;}}const singleton1 = Singleton.getInstance();const singleton2 = Singleton.getInstance();console.log(singleton1 === singleton2); // Output: true
Explore why you might want to create static class members in JavaScript on GreatFrontEnd
Symbols used for in JavaScript?Symbols in JavaScript, introduced in ES6, are unique and immutable identifiers primarily used as object property keys to avoid name collisions. They can be created using the Symbol() function, ensuring each Symbol value is unique even if descriptions are identical. Symbol-keyed properties are skipped by for...in, Object.keys(), Object.getOwnPropertyNames(), and JSON.stringify(), which makes them useful as quasi-private property keys (they're still retrievable via Object.getOwnPropertySymbols() and Reflect.ownKeys()).
const sym1 = Symbol();const sym2 = Symbol('uniqueKey');console.log(typeof sym1); // "symbol"console.log(sym1 === sym2); // false, each symbol is uniqueconst obj = {};const sym = Symbol('uniqueKey');obj[sym] = 'value';console.log(obj[sym]); // "value"
Key characteristics include:
for...in, Object.keys(), Object.getOwnPropertyNames(), or JSON.stringify(), but they're still accessible via Object.getOwnPropertySymbols().Global Symbols can be created using Symbol.for('key'), allowing reuse across different parts of codebases:
const globalSym1 = Symbol.for('globalKey');const globalSym2 = Symbol.for('globalKey');console.log(globalSym1 === globalSym2); // trueconst key = Symbol.keyFor(globalSym1);console.log(key); // "globalKey"
There are some well known Symbol in JavaScript like:
Symbol.iterator: Defines the default iterator for an object.Symbol.toStringTag: Used to create a string description for an object.Symbol.hasInstance: Used to determine if an object is an instance of a constructor.Explore what Symbols are used for in JavaScript on GreatFrontEnd
JavaScript object getters and setters are essential for controlling access to object properties, offering customization when getting or setting values.
const user = {_firstName: 'John',_lastName: 'Doe',get fullName() {return `${this._firstName} ${this._lastName}`;},set fullName(value) {const parts = value.split(' ');this._firstName = parts[0];this._lastName = parts[1];},};console.log(user.fullName); // Output: 'John Doe'user.fullName = 'Jane Smith';console.log(user.fullName); // Output: 'Jane Smith'
Getters (fullName) compute values based on internal properties (_firstName and _lastName), while setters (fullName) update these properties based on assigned values ('Jane Smith'). These mechanisms enhance data encapsulation and allow for custom data handling in JavaScript objects.
Explore what JavaScript object getters and setters are for on GreatFrontEnd
Tools and techniques for debugging JavaScript code vary depending on the context:
debugger statement: Inserting debugger; in code triggers breakpoints when Devtools are open, pausing execution for inspection.console.log() debugging: Using console.log() statements to output variable values and debug messages.Explore what tools and techniques are used for debugging JavaScript code on GreatFrontEnd
Currying in JavaScript is a functional programming technique where a function with multiple arguments is transformed into a sequence of nested functions, each taking a single argument. This allows for partial application of the function's arguments, meaning you can fix some arguments ahead of time and then apply the remaining arguments later.
Here's a simple example of a curry function and why this syntax offers an advantage:
// Example of a curry functionfunction curry(fn) {return function curried(...args) {if (args.length >= fn.length) {return fn(...args);} else {return function (...moreArgs) {return curried(...args, ...moreArgs);};}};}// Example function to be curriedfunction multiply(a, b, c) {return a * b * c;}// Currying the multiply functionconst curriedMultiply = curry(multiply);// Applying curried functionsconst step1 = curriedMultiply(2); // partially apply 2const step2 = step1(3); // partially apply 3const result = step2(4); // apply the final argumentconsole.log(result); // Output: 24
Advantages of Curry Syntax:
Currying enhances the functional programming paradigm in JavaScript by enabling concise, composable, and reusable functions, promoting cleaner and more modular code.
Explore examples of a curry function and why this syntax offers an advantage on GreatFrontEnd
load event and the document DOMContentLoaded event?The DOMContentLoaded event is triggered once the initial HTML document has been fully loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading.
In contrast, the window's load event is fired only after the DOM and all dependent resources, such as stylesheets, images, and subframes, have completely loaded.
If three components in the same render tree call useUser(123), you don't want three identical network requests. A reasonable design has three parts:
An in-flight cache keyed by the request URL (or a stable tuple of arguments). The first caller starts the request and stores the promise; concurrent callers receive the same promise.
const inFlight = new Map();function dedupedFetch(key, requestFn) {if (inFlight.has(key)) return inFlight.get(key);const promise = requestFn().finally(() => inFlight.delete(key));inFlight.set(key, promise);return promise;}
A short-lived response cache so quick re-renders or navigations don't re-fetch. TTL depends on data freshness requirements: longer for reference data, shorter for user state.
A revalidation strategy so stale data eventually refreshes. The common pattern is "stale-while-revalidate": serve the cached value immediately, then fetch in the background and update the cache when the new data arrives.
This is what SWR, TanStack Query, and Apollo Client provide out of the box. Rolling your own makes sense for small apps with simple needs and tight bundle budgets; adopting a library is usually the right call once you need cache invalidation, retries, or devtools support.
A useful follow-up: what if two callers pass slightly different arguments that should produce the same response? The fix is to normalize the cache key (sort query params, canonicalize the URL) before lookup.
The same-origin policy restricts how scripts loaded from one origin can interact with resources from another origin. An origin is the combination of URI scheme, hostname, and port: https://example.com and https://api.example.com are different origins.
A key nuance: the SOP doesn't usually block a cross-origin request from being sent; it blocks JavaScript from reading the response (and from accessing the DOM of cross-origin frames). That's why a cross-origin fetch will still hit the server, but the response body is hidden from your script unless the server opts in via CORS headers. This prevents malicious scripts from silently exfiltrating data the user is authorized to see on another site.
Explore how JSONP works (and how it's not really Ajax) on GreatFrontEnd
Single Page Apps (SPAs) are highly interactive web applications that load a single HTML page and dynamically update content as the user interacts with the app. Unlike traditional server-side rendering, SPAs use client-side rendering, fetching new data via AJAX without full-page refreshes. This approach makes the app more responsive and reduces the number of HTTP requests.
Pros:
Cons:
On SEO: Googlebot has run an evergreen Chromium-based renderer for years and indexes JS-rendered content, but rendering can be deferred (sometimes by days), some non-Google crawlers and AI bots don't render JS at all, and social media link previews rely on initial HTML. The standard approaches:
Explore what a single page app is and how to make one SEO-friendly on GreatFrontEnd
In the past, developers often used Backbone for models, promoting an OOP approach by creating Backbone models and attaching methods to them.
While the module pattern remains useful, modern development often favors React/Redux, which employs a single-directional data flow based on the Flux architecture. Here, app data models are typically represented using plain objects, with utility pure functions to manipulate these objects. State changes are handled using actions and reducers, following Redux principles.
Avoid classical inheritance when possible. If you must use it, adhere to best practices and guidelines.
Explore how to organize your code on GreatFrontEnd
The classic example is a search-as-you-type input that fires fetch(query) on every keystroke. A slower earlier request can resolve after a faster later one, overwriting the correct results with stale data. Three approaches, in increasing order of how well they handle it:
Token check: capture an id at the start of each call and only commit the result if the id is still the latest:
let latestRequestId = 0;async function search(query) {const myRequestId = ++latestRequestId;const results = await fetch(`/search?q=${query}`).then((r) => r.json());if (myRequestId === latestRequestId) {setResults(results);}}
AbortController: cancel the previous request before starting a new one. The fetch rejects with an AbortError, which you can ignore:
let controller;async function search(query) {controller?.abort();controller = new AbortController();try {const results = await fetch(`/search?q=${query}`, {signal: controller.signal,}).then((r) => r.json());setResults(results);} catch (e) {if (e.name === 'AbortError') return;throw e;}}
A query layer (TanStack Query, SWR): handles deduplication, cancellation, stale-while-revalidate, and result ordering for you. Worth adopting once you have more than a handful of async data needs.
Token check is the simplest fallback and works in any environment, but the in-flight request still completes and consumes server resources. AbortController closes the connection on the client, which lets the browser stop downloading the response, though whether the server stops processing depends on whether the handler is wired to listen for the disconnect.
In React, the natural place to call controller.abort() is the cleanup function returned from useEffect, so a request started by an effect is cancelled when the component unmounts or the effect re-runs.
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
At the senior level, the questions don't get harder so much as the answers get deeper. You're expected to know the language, but what differentiates a strong candidate is connecting the language to its consequences: knowing where the sharp edges are, how the framework you use leans on them, and which tradeoffs you'd defend in a code review.
Prefer GitHub? You can explore all +190 JavaScript interview questions directly in our open source repo.

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

JavaScript fundamentals make up the bulk of any junior front-end interview, and most freshers lose points on the basics rather than the hard questions.
Below are 50 commonly-asked basic JavaScript interview questions with concise, interview-ready answers, preceded by the scenario-style questions and presentation pitfalls that decide most fresher rounds.
If you're looking for additional JavaScript interview preparation materials, also check out these resources:
Beyond definitions, interviewers often hand juniors a small piece of broken code and watch how they reason. These five scenarios show up in entry-level rounds at companies of all sizes, and they reward candidates who can talk through the why, not just type the fix.
var-in-for-loop with setTimeout"What do you expect this to log, and how would you fix it?"
for (var i = 0; i < 3; i++) {setTimeout(() => console.log(i), 0);}// Logs: 3, 3, 3
The interviewer is checking whether you can: (1) spot that var is function-scoped, so all three callbacks close over the same i which is 3 by the time the timers fire; (2) propose two fixes: replace var with let (block-scoped, each iteration gets its own i), or wrap the body in an IIFE that captures the current value. Bonus points: connect it back to closures and the event loop.
NaN?"I wired up a click handler on a
Counterclass. Each click logsNaNinstead of incrementing. What did I miss?"
class Counter {constructor() {this.count = 0;}handleClick() {this.count++;console.log(this.count);}}const counter = new Counter();document.querySelector('button').addEventListener('click', counter.handleClick);// Logs: NaN, `this` is the button, not the Counter
addEventListener invokes the handler with this set to the element the listener is attached to, not the Counter instance. So this.count reads undefined from the button; the ++ then sets button.count = NaN (yes, on the button), and the log shows NaN. The Counter's count is never touched. Fix it with c.handleClick.bind(c) or, more idiomatically, by defining the handler as an arrow class field so this is captured from the enclosing scope. Bonus points: name both fixes and explain the tradeoff (.bind() returns a new function each call, so if you don't store the bound reference you can't later removeEventListener it; arrow class fields auto-bind but live on each instance instead of the prototype, so you pay one function object per instance).
"I passed my array into a helper to get its new length. The helper returned a number, but my original array now has the new item in it too. What's going on?"
function addItem(items, x) {items.push(x);return items.length;}const cart = ['apple'];const newLength = addItem(cart, 'banana');console.log(cart); // ['apple', 'banana'], the caller's array changed
Objects and arrays aren't copied when passed to a function, the function gets a reference to the same array. Inside addItem, items and the outer cart point to the same array, so .push mutates what the caller sees. The fix is either to return a new array (return [...items, x]) and have the caller assign it back, or to make the mutation obvious in the function name (e.g., pushAndGetLength). Bonus points: contrast with primitives (numbers, strings, booleans), which are copied; that's why function inc(n) { n++; } doesn't change the caller's variable.
.then() runs before the previous .then() "finishes"fetch('/api/data').then((response) => {response.json();}).then((data) => {console.log(data); // undefined});
The first .then doesn't return response.json(), so the chain receives undefined from the implicit return. Promise chains pass values through the return value of each handler, and forgetting to return (or using a {}-bodied arrow when you meant a single-expression one) is a frequent async bug in junior code. Bonus points: suggest async/await, which sidesteps the issue because await makes the value the result of the expression directly.
.sort() returns the wrong order for numbers"I called
.sort()on[10, 2, 1, 100, 20]and got[1, 10, 100, 2, 20]. Is this a bug in V8?"
[10, 2, 1, 100, 20].sort();// [1, 10, 100, 2, 20]
Array.prototype.sort converts each element to a string and compares them lexicographically by default, so "10" comes before "2" for the same reason "banana" comes before "car". The fix is to pass a comparator: arr.sort((a, b) => a - b) for ascending numeric order. Bonus points: mention that the default also breaks for dates and currency strings, that .sort() mutates in place (so combine with [...arr].sort(...) if you need a new array), and that localeCompare is the right tool for human-readable string sorting.
Most freshers don't lose interviews on missing knowledge. They lose them on presentation patterns that drag the score down even when the answer is right. The most common ones:
Saying "a closure is when a function remembers its scope" is technically correct, but it's the kind of one-line definition you'd find on the first page of a tutorial, and interviewers can tell when there's nothing behind it. A better answer: "a closure is a function that retains access to its lexical environment even after the outer function returns," followed by why that matters, private state, partial application, callbacks that need access to creation-time data. Pair every definition with a concrete use case.
When the interviewer says "implement debounce," strong candidates ask: "Should it fire on the leading edge, the trailing edge, or both? Should the eventual call use the first arguments or the latest ones? Do you want a cancel to drop a pending call?" Weak candidates start coding immediately. The clarifying questions are part of the rubric, because they signal you've seen the gotchas before.
Coding for several minutes without explaining what you're doing is a common reason "good coder" candidates still fail. Narrate: "I'll handle the empty case first… now I'll add the recursion… I'm using a Map here because lookups are O(1)…" If you're stuck, say so out loud, because interviewers can hint, but only if they know where you are.
Juniors often write the happy path, run it on one input, see it pass, and stop. Stronger candidates finish a function, then list 3–5 edge cases out loud and try them: empty input, very large input, negative numbers, null/undefined, single-element collections. The interviewer is grading you on this, so if you don't volunteer it, points are left on the table.
== with ===Using == in interview code is fine if you can defend it ("== is fine here because we explicitly want type coercion to handle the form input as a string-or-number"). Using == and being unable to explain the coercion rules signals you don't understand the language. Default to === and only reach for == with a reason. (For the actual coercion rules, see == vs ===.)
A junior who answers "how would you debounce?" with "use Lodash" misses the point, because the interviewer wants the implementation. Same with "how would you flatten an array?" Saying array.flat(Infinity) is great as a follow-up, but the interview question is almost always "implement it yourself first". Native APIs are the bonus answer, not the primary one. (See our Flatten implementation challenge for the full set of approaches.)
After writing a function, walk through it with one or two example inputs, or better, actually run it if the platform allows. Submitting without trying anything leaves easy points on the table; the interviewer is watching whether you verify your own work before declaring done.
The questions below cover the fundamentals you're most likely to be asked in a junior or entry-level JavaScript interview. Each question is paired with a concise answer and links to a deeper quiz on GreatFrontEnd where you can practice the topic.
Hoisting describes the behavior of variable declarations in JavaScript. Declarations using var are "moved" to the top of their scope during compilation. Only the declaration is hoisted, not the initialization.
Example with var:
console.log(foo); // undefinedvar foo = 1;console.log(foo); // 1
Visualized as:
var foo;console.log(foo); // undefinedfoo = 1;console.log(foo); // 1
Variables with let, const, and class:
These are also hoisted but not initialized. Accessing them before declaration results in a ReferenceError.
console.log(y); // ReferenceError: Cannot access 'y' before initializationlet y = 'local';console.log(z); // ReferenceError: Cannot access 'z' before initializationconst z = 'local';console.log(Foo); // ReferenceError: Cannot access 'Foo' before initializationclass Foo {constructor() {}}
Function Expressions:
Only the declaration is hoisted.
console.log(bar); // undefinedbar(); // Uncaught TypeError: bar is not a functionvar bar = function () {console.log('BARRRR');};
Function Declarations:
Both declaration and definition are hoisted.
console.log(foo); // [Function: foo]foo(); // 'FOOOOO'function foo() {console.log('FOOOOO');}
Import Statements:
Imports are hoisted, making them available throughout the module, with side effects occurring before other code runs.
foo.doSomething(); // Works normally.import foo from './modules/foo';
Explore the concept of "hoisting" in JavaScript on GreatFrontEnd
let, var or const?var: Function-scoped or globally scoped.let and const: Block-scoped (only accessible within the nearest set of curly braces).Example:
function foo() {var bar = 1;let baz = 2;const qux = 3;console.log(bar); // 1console.log(baz); // 2console.log(qux); // 3}console.log(bar); // ReferenceErrorconsole.log(baz); // ReferenceErrorconsole.log(qux); // ReferenceError
if (true) {var bar = 1;let baz = 2;const qux = 3;}console.log(bar); // 1console.log(baz); // ReferenceErrorconsole.log(qux); // ReferenceError
var and let: Can be declared without an initial value.const: Must be initialized at the time of declaration.Example:
var foo; // Oklet bar; // Okconst baz; // SyntaxError
var: Allows redeclaration.let and const: Do not allow redeclaration.Example:
var foo = 1;var foo = 2; // Oklet baz = 3;let baz = 4; // SyntaxError
var and let: Allow reassignment.const: Does not allow reassignment.Example:
var foo = 1;foo = 2; // Oklet bar = 3;bar = 4; // Okconst baz = 5;baz = 6; // TypeError
var: Variables are hoisted and initialized to undefined.let and const: Variables are hoisted but not initialized, causing a ReferenceError if accessed before declaration.Example:
console.log(foo); // undefinedvar foo = 'foo';console.log(baz); // ReferenceErrorlet baz = 'baz';console.log(bar); // ReferenceErrorconst bar = 'bar';
== and === in JavaScript?==)Examples:
42 == '42'; // true0 == false; // truenull == undefined; // true[] == false; // true'' == false; // true
===)true.Examples:
42 === '42'; // false0 === false; // falsenull === undefined; // false[] === false; // false'' === false; // false
Use == only when comparing against null or undefined for convenience.
var a = null;console.log(a == null); // trueconsole.log(a == undefined); // true
Prefer === for all other comparisons to avoid pitfalls of type coercion and ensure both value and type are the same.
Explore the difference between == and === in JavaScript on GreatFrontEnd
The event loop is crucial for handling asynchronous operations in JavaScript, allowing single-threaded execution without blocking.
1. Call Stack:
2. Web APIs/Node.js APIs:
setTimeout(), HTTP requests) on separate threads.3. Task Queue (Macrotask Queue):
setTimeout(), setInterval(), and UI events.4. Microtask Queue:
Promise callbacks).console.log('Start');setTimeout(() => {console.log('Timeout 1');}, 0);Promise.resolve().then(() => {console.log('Promise 1');});setTimeout(() => {console.log('Timeout 2');}, 0);console.log('End');
Console Output:
StartEndPromise 1Timeout 1Timeout 2
Explanation:
Start and End are logged first (synchronous).Promise 1 is logged next (microtask).Timeout 1 and Timeout 2 are logged last (macrotasks).Understanding the event loop helps write efficient, non-blocking JavaScript code.
Explore the event loop in JavaScript on GreatFrontEnd
Event delegation is an efficient way to handle events on multiple child elements by attaching a single event listener to a common parent element. This is useful for managing events on many similar elements, like list items.
event.target to identify the actual element that triggered the event.// HTML:// <ul id="item-list">// <li>Item 1</li>// <li>Item 2</li>// <li>Item 3</li>// </ul>const itemList = document.getElementById('item-list');itemList.addEventListener('click', (event) => {if (event.target.tagName === 'LI') {console.log(`Clicked on ${event.target.textContent}`);}});
A single click listener on <ul> handles clicks on any <li> due to event bubbling.
Dynamic Content:
const buttonContainer = document.getElementById('button-container');const addButton = document.getElementById('add-button');buttonContainer.addEventListener('click', (event) => {if (event.target.tagName === 'BUTTON') {console.log(`Clicked on ${event.target.textContent}`);}});addButton.addEventListener('click', () => {const newButton = document.createElement('button');newButton.textContent = `Button ${buttonContainer.children.length + 1}`;buttonContainer.appendChild(newButton);});
Simplifying Code:
const userForm = document.getElementById('user-form');userForm.addEventListener('input', (event) => {const { name, value } = event.target;console.log(`Changed ${name}: ${value}`);});
this works in JavaScriptThe this keyword in JavaScript can be quite confusing as its value depends on how a function is called. Here are the main rules that determine the value of this:
new KeywordCreates a new object and sets this to that object.
function Person(name) {this.name = name;}const person = new Person('Alice');console.log(person.name); // 'Alice
apply, call, or bindExplicitly sets this to a specified object.
javascriptCopy codefunction greet() {console.log(this.name);}const person = { name: 'Alice' };greet.call(person); // 'Alice'
this is bound to the object the method is called on.
const obj = {name: 'Alice',greet: function () {console.log(this.name);},};obj.greet(); // 'Alice'
In non-strict mode, defaults to the global object (window in browsers); in strict mode, defaults to undefined.
function greet() {console.log(this); // global object or undefined}greet();
Inherit this from their lexical enclosing scope.
const obj = {name: 'Alice',greet: () => {console.log(this.name); // `this` refers to the enclosing scope},};obj.greet(); // undefined
thisES2015 introduced arrow functions which capture this from their lexical scope. This can simplify code but requires caution when integrating with libraries expecting traditional function context.
Example:
function Timer() {this.seconds = 0;setInterval(() => {this.seconds++; // `this` refers to the Timer instanceconsole.log(this.seconds);}, 1000);}const timer = new Timer();
Explore how this works in JavaScript on GreatFrontEnd
sessionStorage and localStorage.Cookies, localStorage, and sessionStorage are key client-side storage mechanisms in web applications, each serving distinct purposes:
Purpose: Stores small data pieces sent to the server with HTTP requests.
Capacity: Limited to around 4KB per domain.
Lifespan: Can have expiration dates; session cookies are cleared when the browser closes.
Access: Domain-specific; accessible across pages and subdomains.
Security: Supports HttpOnly and Secure flags to restrict JavaScript access and ensure HTTPS transmission.
Example Usage:
// Set a cookie with an expirydocument.cookie ='auth_token=abc123def; expires=Fri, 31 Dec 2024 23:59:59 GMT; path=/';// Read all cookies (no direct method for specific cookies)console.log(document.cookie);// Delete a cookiedocument.cookie ='auth_token=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';
localStoragePurpose: Stores data persistently on the client-side.
Capacity: Around 5MB per origin.
Lifespan: Data remains until explicitly cleared.
Access: Available across all tabs and windows within the same origin.
Security: All JavaScript on the page can access localStorage values.
Example Usage:
// Set an item in localStoragelocalStorage.setItem('key', 'value');// Get an item from localStorageconsole.log(localStorage.getItem('key'));// Remove an item from localStoragelocalStorage.removeItem('key');// Clear all data in localStoragelocalStorage.clear();
sessionStoragePurpose: Stores session-specific data that persists until the browser or tab is closed.
Capacity: Similar to localStorage, around 5MB per origin.
Lifespan: Cleared when the tab or browser closes; reloading the page retains data.
Access: Limited to the current tab or window.
Security: All JavaScript on the page can access sessionStorage values.
Example Usage:
// Set an item in sessionStoragesessionStorage.setItem('key', 'value');// Get an item from sessionStorageconsole.log(sessionStorage.getItem('key'));// Remove an item from sessionStoragesessionStorage.removeItem('key');// Clear all data in sessionStoragesessionStorage.clear();
Explore the difference between a cookie, sessionStorage and localStorage on GreatFrontEnd
<script>, <script async> and <script defer><script> TagThe <script> tag is used to include JavaScript in a web page. When used without async or defer attributes:
Example:
<!doctype html><html><head><title>Regular Script</title></head><body><h1>Regular Script Example</h1><p>This content appears before the script executes.</p><script src="regular.js"></script><p>This content appears after the script executes.</p></body></html>
<script async> TagExample:
<!doctype html><html><head><title>Async Script</title></head><body><h1>Async Script Example</h1><p>This content appears before the async script executes.</p><script async src="async.js"></script><p>This content may appear before or after the async script executes.</p></body></html>
<script defer> Tag:
DOMContentLoaded.Example:
<!doctype html><html><head><title>Deferred Script</title></head><body><h1>Deferred Script Example</h1><p>This content appears before the deferred script executes.</p><script defer src="deferred.js"></script><p>This content appears before the deferred script executes.</p></body></html>
Explore the difference between <script>, <script async> and <script defer> on GreatFrontEnd
null, undefined or undeclared?Undeclared: A variable that is not declared using var, let, or const will be created globally and can cause errors. Avoid them by using try/catch blocks to detect them.
undefined: A declared variable without an assigned value is undefined. Use === or typeof to check for undefined. Note that == will also return true for null.
null: A variable explicitly assigned null represents no value. Use === to check for null. Don't use == as it will also return true for undefined.
Best Practices:
null to variables if you don't intend to use them yet.Explore the difference between a variable that is: null, undefined or undeclared on GreatFrontEnd
.call and .apply in JavaScript?.call and .apply are used to invoke functions, setting this within the function. The difference lies in how they handle arguments:
Memory Aid:
Example:
javascriptCopy codefunction add(a, b) {return a + b;}console.log(add.call(null, 1, 2)); // 3console.log(add.apply(null, [1, 2])); // 3// ES6 with spread operatorconsole.log(add.call(null, ...[1, 2])); // 3
Explore the difference between .call and .apply in JavaScript on GreatFrontEnd
Function.prototype.bindFunction.prototype.bind creates a new function with a specific this context and optionally preset arguments. It's useful for maintaining the correct this value in methods passed to other functions.
Example:
const john = {age: 42,getAge: function () {return this.age;},};console.log(john.getAge()); // 42const unboundGetAge = john.getAge;console.log(unboundGetAge()); // undefinedconst boundGetAge = john.getAge.bind(john);console.log(boundGetAge()); // 42const mary = { age: 21 };const boundGetAgeMary = john.getAge.bind(mary);console.log(boundGetAgeMary()); // 21
Its main purposes are:
this to preserve context: The primary function of bind is to attach the this value of a function to a specific object. When you use func.bind(thisArg), it generates a new function with the same code as func, but with this permanently set to thisArg.bind also enables you to pre-set arguments for the new function. Any arguments provided to bind after thisArg will be prepended to the argument list when the new function is invoked.bind allows you to borrow methods from one object and use them on another object, even if the methods were not initially designed for that object.Explore Function.prototype.bind on GreatFrontEnd
The advantage of using the arrow syntax for a method in a constructor is that it automatically binds the this value to the constructor's this context. This means that when the method is called, it will always refer to the constructor's this context, rather than the global scope or some other unexpected context.
In traditional function expressions, the this value is determined by how the function is called, which can lead to unexpected behavior if not properly bound. By using an arrow function, you can ensure that the this value is always bound to the constructor's this context, making your code more predictable and easier to maintain.
For example, in the code snippet:
const Person = function (name) {this.name = name;this.sayName1 = function () {console.log(this.name);};this.sayName2 = () => {console.log(this.name);};};const john = new Person('John');const dave = new Person('Dave');john.sayName1(); // Johnjohn.sayName2(); // John// `this` can change for regular functions but not for arrow functionsjohn.sayName1.call(dave); // Davejohn.sayName2.call(dave); // John
The sayName1 method uses a traditional function expression, which means its this value is determined by how it's called. If you call john.sayName1.call(dave), the this value will be dave, and the method will log Dave to the console.
On the other hand, the sayName2 method uses an arrow function, which means its this value is automatically bound to the constructor's this context. If you call john.sayName2.call(dave), the this value will still be john, and the method will log John to the console.
This can be particularly helpful in React class components, where you often need to pass methods as props to child components. By using arrow functions, you can ensure that the methods always refer to the correct this context, without having to manually bind this in the constructor.
Explore the advantage for using the arrow syntax for a method in a constructor on GreatFrontEnd
Prototypical inheritance allows objects to inherit properties and methods from other objects using a prototype-based model.
Object.getPrototypeOf() and Object.setPrototypeOf().function Person(name, age) {this.name = name;this.age = age;}Person.prototype.sayHello = function () {console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`,);};let john = new Person('John', 30);john.sayHello(); // "Hello, my name is John and I am 30 years old."
JavaScript looks for properties/methods on the object, then its prototype, and so on up the chain until null.
Functions used with new to create objects, setting their prototype to the constructor's prototype.
function Animal(name) {this.name = name;}Animal.prototype.sayName = function () {console.log(`My name is ${this.name}`);};function Dog(name, breed) {Animal.call(this, name);this.breed = breed;}Dog.prototype = Object.create(Animal.prototype);Dog.prototype.bark = function () {console.log('Woof!');};let fido = new Dog('Fido', 'Labrador');fido.bark(); // "Woof!"fido.sayName(); // "My name is Fido"
Object.create()Creates a new object with a specified prototype.
let proto = {greet: function () {console.log(`Hello, my name is ${this.name}`);},};let person = Object.create(proto);person.name = 'John';person.greet(); // "Hello, my name is John"
Explore how prototypal inheritance works on GreatFrontEnd
function Person(){}, const person = Person(), and const person = new Person()?function Person(){} is a standard function declaration in JavaScript. When written in PascalCase, it follows the convention for functions intended to be used as constructors.
const person = Person() simply calls the function and executes its code. If no return value is specified, person will be undefined. This is not a constructor call and does not create a new object.
const person = new Person() creates a new object using the Person constructor function. The new keyword creates a new object and sets its prototype to Person.prototype. The this keyword inside the constructor function refers to the new object being created.
foo between function foo() {} and var foo = function() {}Syntax: function foo() {}
Description: Defines a named function that can be called throughout the enclosing scope.
Example:
function foo() {console.log('FOOOOO');}
Syntax: var foo = function() {}
Description: Defines a function and assigns it to a variable, often used in specific contexts.
Example:
var foo = function () {console.log('FOOOOO');};
Hoisting:
Function Declarations: The entire function is hoisted; can be called before its definition.
foo(); // 'FOOOOO'function foo() {console.log('FOOOOO');}
Function Expressions: Only the variable is hoisted, not the function body; calling it before definition results in an error.
foo(); // Uncaught TypeError: foo is not a functionvar foo = function () {console.log('FOOOOO');};
Name Scope:
Function Expressions: These can be named internally, but the name is only accessible within the function.
const myFunc = function namedFunc() {console.log(namedFunc); // Works};console.log(namedFunc); // undefined
Function Declarations:
Function Expressions:
Here are the various ways to create objects in JavaScript:
Object Literals ({}): Simplest way to create objects using key-value pairs within curly braces.
const person = {firstName: 'John',lastName: 'Doe',};
Object() Constructor: Using the new keyword with the built-in Object constructor to create an object.
const person = new Object();person.firstName = 'John';person.lastName = 'Doe';
Object.create() Method: Creating a new object using an existing object as a prototype.
const personPrototype = {greet() {console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`,);},};const person = Object.create(personPrototype);person.name = 'John';person.age = 30;person.greet(); // Output: Hello, my name is John and I'm 30 years old.
ES2015 Classes: Defining a blueprint for objects using classes, similar to other programming languages.
class Person {constructor(name, age) {this.name = name;this.age = age;}greet = function () {console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`,);};}const person = new Person('John', 30);person.greet(); // Output: Hello, my name is John and I'm 30 years old.
Constructor Functions: Reusable blueprints for objects, using the new keyword to create instances.
// Constructor functionfunction Person(name, age) {this.name = name;this.age = age;this.greet = function () {console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`,);};}const person = new Person('John', 30);person.greet(); // Output: Hello, my name is John and I'm 30 years old.
Note: Constructor functions are less commonly used now that ES2015 classes are widely supported.
Explore various ways to create objects in JavaScript on GreatFrontEnd
A higher-order function is a function that:
Takes another function as an argument: A function that accepts another function as a parameter.
function greet(name) {return `Hello, ${name}!`;}function greetName(greeter, name) {console.log(greeter(name));}greetName(greet, 'Alice'); // Output: Hello, Alice!
Returns a function as its result: A function that returns another function as its output.
function multiplier(factor) {return function (num) {return num * factor;};}const double = multiplier(2);console.log(double(5)); // Output: 10
In other words, a higher-order function is a function that operates on other functions, either by taking them as input or by producing them as output.
Explore the definition of a higher-order function on GreatFrontEnd
Uses function constructors and prototypes.
Example:
function Person(name, age) {this.name = name;this.age = age;}Person.prototype.greet = function () {console.log('Hello, my name is ' +this.name +' and I am ' +this.age +' years old.',);};var person1 = new Person('John', 30);person1.greet(); // Hello, my name is John and I am 30 years old.
Uses the class syntax, making code more readable and adding features.
Example:
class Person {constructor(name, age) {this.name = name;this.age = age;}greet() {console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`,);}}const person1 = new Person('John', 30);person1.greet(); // Hello, my name is John and I am 30 years old.
class keyword, more concise and easier to understandstatic keywordObject.create() and manual prototype chain settingextends keyword, simpler and more intuitivesuper keyword to call parent class's constructor and methodsExplore differences between ES2015 classes and ES5 function constructors on GreatFrontEnd
Event bubbling is a mechanism in the DOM (Document Object Model) where an event, such as a click, is first triggered on the target element and then propagates upward through the DOM tree to the root of the document.
Bubbling Phase:
Description: During the bubbling phase, the event starts at the target element and bubbles up through its ancestors in the DOM hierarchy. Event handlers attached to the target element and its ancestors can all potentially receive and respond to the event.
Example:
// HTML:// <div id="parent">// <button id="child">Click me!</button>// </div>const parent = document.getElementById('parent');const child = document.getElementById('child');parent.addEventListener('click', () => {console.log('Parent element clicked');});child.addEventListener('click', () => {console.log('Child element clicked');});
When you click the Click me! button, both the child and parent event handlers will be triggered due to event bubbling.
Stopping Event Bubbling:
Method: Use stopPropagation() to stop the event from bubbling up the DOM tree.
Example:
child.addEventListener('click', (event) => {console.log('Child element clicked');event.stopPropagation();});
Explore event bubbling on GreatFrontEnd
Event capturing is a propagation mechanism in the DOM where an event, such as a click, is first triggered at the root of the document and then flows down through the DOM tree to the target element.
Event Propagation Phases:
Enabling Event Capturing:
{ capture: true } as the third argument to addEventListener().Example:
// HTML:// <div id="parent">// <button id="child">Click me!</button>// </div>const parent = document.getElementById('parent');const child = document.getElementById('child');parent.addEventListener('click',() => {console.log('Parent element clicked (capturing)');},true, // Enable capturing phase);child.addEventListener('click', () => {console.log('Child element clicked');});
When you click the Click me! button, the parent element's capturing handler will be triggered before the child element's handler.
Stopping Propagation:
Use stopPropagation() to prevent the event from traveling further down the DOM tree during the capturing phase.
Example:
parent.addEventListener('click',(event) => {console.log('Parent element clicked (capturing)');event.stopPropagation(); // Stop event propagation},true,);child.addEventListener('click', () => {console.log('Child element clicked');});
In this example, only the parent event listener will be called when you click the "Click me!" button, as the event propagation is stopped at the parent element.
Explore event capturing on GreatFrontEnd
mouseenter and mouseover event in JavaScript and browsers?mouseentermouseoverExample:
const fs = require('fs');const data = fs.readFileSync('large-file.txt', 'utf8');console.log(data); // Blocks until file is readconsole.log('End of the program');
Example:
console.log('Start of the program');fetch('https://api.example.com/data').then((response) => response.json()).then((data) => console.log(data)) // Non-blocking.catch((error) => console.error(error));console.log('End of program');
Explore the difference between synchronous and asynchronous functions on GreatFrontEnd
AJAX is a set of web development techniques using various web technologies on the client side to create asynchronous web applications. Unlike traditional web applications where each user interaction triggers a full page reload, AJAX allows web applications to send data to and retrieve data from a server asynchronously without interfering with the display and behavior of the existing page. This enables dynamic updates to the web page without the need to reload it.
Key Points:
XMLHttpRequest, but fetch() is now preferred for modern web applications.XMLHttpRequest APIExample:
let xhr = new XMLHttpRequest();xhr.onreadystatechange = function () {if (xhr.readyState === XMLHttpRequest.DONE) {if (xhr.status === 200) {console.log(xhr.responseText);} else {console.error('Request failed: ' + xhr.status);}}};xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos/1', true);xhr.send();
XMLHttpRequest, sets up a callback function to handle state changes, opens a request to a URL, and sends the request.fetch() APIExample:
fetch('https://jsonplaceholder.typicode.com/todos/1').then((response) => {if (!response.ok) {throw new Error('Network response was not ok');}return response.json();}).then((data) => console.log(data)).catch((error) => console.error('Fetch error:', error));
.then() to parse JSON data, and manages errors with .catch().fetchfetch() initiates an asynchronous request to fetch a resource from a URL.
Example:
fetch('https://api.example.com/data', {method: 'GET', // or 'POST', 'PUT', 'DELETE', etc.headers: {'Content-Type': 'application/json',},});
fetch() returns a Promise that resolves to a Response object representing the server's response.The Response object offers methods to handle the body content, such as .json(), .text(), .blob().
Example:
fetch('https://api.example.com/data').then((response) => response.json()).then((data) => console.log(data)).catch((error) => console.error('Error:', error));
fetch() is asynchronous, allowing the browser to continue executing other tasks while waiting for the server response..then(), .catch()) are handled in the microtask queue as part of the event loop.fetch() configures various request aspects, such as HTTP method, headers, body, credentials, and caching..catch() or try/catch with async/await.Explore how to explain AJAX in as much detail as possible on GreatFrontEnd
AJAX (Asynchronous JavaScript and XML) enables web pages to send and retrieve data asynchronously, allowing for dynamic updates without full page reloads.
Explore the advantages and disadvantages of using AJAX on GreatFrontEnd
XMLHttpRequest and fetch()?Both XMLHttpRequest (XHR) and fetch() enable asynchronous HTTP requests in JavaScript, but differ in syntax, handling, and features.
setRequestHeader method.send method.body property in the options parameter.responseType to handle different formats..then for accessing data.onerror event..catch method.abort() method.AbortController for request cancellation.onprogress event.Choosing Between Them: fetch() is generally preferred due to its cleaner syntax and promise-based handling, but XMLHttpRequest may still be useful for specific cases like progress tracking.
Explore the differences between XMLHttpRequest and fetch() on GreatFrontEnd
JavaScript has various data types categorized into two groups: primitive and non-primitive (reference) types.
true or false.Determining Data Types: JavaScript is dynamically typed, meaning variables can hold different data types over time. Use the typeof operator to determine a variable's type.
Explore the various data types in JavaScript on GreatFrontEnd
Iterating over object properties and arrays is very common in JavaScript and we have various ways to achieve this. Here are some of the ways to do it:
for...in StatementLoops over all enumerable properties of an object, including inherited ones.
for (const property in obj) {if (Object.hasOwn(obj, property)) {console.log(property);}}
Object.keys()Returns an array of an object's own enumerable property names.
Object.keys(obj).forEach((property) => console.log(property));
Object.entries()Returns an array of a given object's own enumerable string-keyed property [key, value] pairs.
Object.entries(obj).forEach(([key, value]) =>console.log(`${key}: ${value}`),);
Object.getOwnPropertyNames()Returns an array of all properties (including non-enumerable ones) found directly upon a given object.
Object.getOwnPropertyNames(obj).forEach((property) =>console.log(property),);
for LoopTraditional loop over array elements.
for (let i = 0; i < arr.length; i++) {console.log(arr[i]);}
Array.prototype.forEach()Executes a provided function once for each array element.
arr.forEach((element, index) => console.log(element, index));
for...of StatementLoops over iterable objects like arrays.
for (let element of arr) {console.log(element);}
Array.prototype.entries()Provides both the index and value of each array element in a for...of loop.
for (let [index, elem] of arr.entries()) {console.log(index, ': ', elem);}
Introduced in ES2015, the spread syntax (...) is useful for copying and merging arrays and objects without modifying the originals. It's commonly used in functional programming, Redux, and RxJS.
Copying Arrays/Objects: Creates shallow copies.
const array = [1, 2, 3];const newArray = [...array]; // [1, 2, 3]const obj = { name: 'John', age: 30 };const newObj = { ...obj, city: 'New York' }; // { name: 'John', age: 30, city: 'New York' }
Merging Arrays/Objects: Merges them into a new one.
const arr1 = [1, 2, 3];const arr2 = [4, 5, 6];const mergedArray = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]const obj1 = { foo: 'bar' };const obj2 = { qux: 'baz' };const mergedObj = { ...obj1, ...obj2 }; // { foo: 'bar', qux: 'baz' }
Function Arguments: Passes array elements as individual arguments.
const numbers = [1, 2, 3];Math.max(...numbers); // Same as Math.max(1, 2, 3)
Array vs. Object Spreads: Only iterables can be spread into arrays; arrays can be spread into objects.
const array = [1, 2, 3];const obj = { ...array }; // { 0: 1, 1: 2, 2: 3 }
The rest syntax (...) gathers multiple elements into an array or object, the inverse of the spread syntax.
Function Parameters: Collects remaining arguments into an array.
function addFiveToNumbers(...numbers) {return numbers.map((x) => x + 5);}const result = addFiveToNumbers(4, 5, 6, 7); // [9, 10, 11, 12]
Array Destructuring: Collects remaining elements into a new array.
const [first, second, ...remaining] = [1, 2, 3, 4, 5];// first: 1, second: 2, remaining: [3, 4, 5]
Object Destructuring: Collects remaining properties into a new object.
const { e, f, ...others } = { e: 1, f: 2, g: 3, h: 4 };// e: 1, f: 2, others: { g: 3, h: 4 }
Rest Parameter Rules: Must be the last parameter.
function addFiveToNumbers(arg1, ...numbers, arg2) {// Error: Rest element must be last element.}
Map object and a plain object in JavaScript?size property to get the number of key-value pairs.forEach, keys(), values(), and entries().Object.keys(), Object.values(), or Object.entries() for iteration.// Mapconst map = new Map();map.set('key1', 'value1');map.set({ key: 'key2' }, 'value2');console.log(map.size); // 2// Plain Objectconst obj = { key1: 'value1' };obj[{ key: 'key2' }] = 'value2';console.log(Object.keys(obj).length); // 1 (keys are strings)
Explore the difference between a Map object and a plain object in JavaScript on GreatFrontEnd
Map/Set vs WeakMap/WeakSet?The main distinctions between Map/Set and WeakMap/WeakSet in JavaScript are as follows:
Map and Set accept keys of any type (objects, primitive values), whereas WeakMap and WeakSet exclusively use objects as keys, excluding primitive values like strings or numbers.Map and Set retain strong references to their keys and values, preventing their disposal by garbage collection. In contrast, WeakMap and WeakSet employ weak references for keys (objects), allowing these objects to be collected by garbage collection if no other strong references persist.Map and Set are enumerable and can be iterated over, while those in WeakMap and WeakSet are non-enumerable, precluding retrieval of key or value lists directly from them.Map and Set possess a size property that indicates the number of elements they contain. In contrast, WeakMap and WeakSet lack a size property because their size may vary as a result of garbage collection.Map and Set serve well as general-purpose data structures and for caching purposes. Conversely, WeakMap and WeakSet are primarily suited for storing metadata or additional object-related data without impeding the object's potential garbage collection when no longer needed.Explore the differences between Map/Set vs WeakMap/WeakSet on GreatFrontEnd
One practical use case for the arrow function syntax in JavaScript is simplifying callback functions, particularly in scenarios where you need concise, inline function definitions. Here's an example:
Use Case: Mapping an Array
Suppose you have an array of numbers and you want to double each number using the map function.
// Traditional function syntaxconst numbers = [1, 2, 3, 4, 5];const doubledNumbers = numbers.map(function (number) {return number * 2;});console.log(doubledNumbers); // Output: [2, 4, 6, 8, 10]
Using arrow function syntax, you can achieve the same result more succinctly:
// Arrow function syntaxconst numbers = [1, 2, 3, 4, 5];const doubledNumbers = numbers.map((number) => number * 2);console.log(doubledNumbers); // Output: [2, 4, 6, 8, 10]
Explore a use case for the new arrow => function syntax on GreatFrontEnd
In asynchronous programming, a callback function is passed as an argument to another function and invoked when a task completes, such as fetching data or handling I/O operations. Here's a concise explanation:
function fetchData(callback) {setTimeout(() => {const data = { name: 'John', age: 30 };callback(data);}, 1000);}fetchData((data) => {console.log(data); // { name: 'John', age: 30 }});
Explore the concept of a callback function in asynchronous operations on GreatFrontEnd
Debouncing delays function execution until a specified time has passed since its last call, useful for tasks like search input handling.
function debounce(func, delay) {let timeoutId;return (...args) => {clearTimeout(timeoutId);timeoutId = setTimeout(() => func.apply(this, args), delay);};}
Throttling limits function execution to at most once within a specified interval, beneficial for tasks like handling frequent events 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 techniques optimize performance and manage event-driven behaviors effectively in JavaScript applications.
Explore the concept of debouncing and throttling on GreatFrontEnd
Destructuring assignment simplifies extracting values from arrays or properties from objects into separate variables:
// Array destructuringconst [a, b] = [1, 2];// Object destructurinconst { name, age } = { name: 'John', age: 30 };
This syntax uses square brackets for arrays and curly braces for objects, enabling concise variable assignment directly from data structures.
Explore the concept of destructuring assignment for objects and arrays on GreatFrontEnd
Hoisting moves function declarations to the top of their scope during compilation, allowing them to be called before their actual placement in the code. Function expressions and arrow functions, however, must be defined before they are called to avoid errors.
// Function declarationhoistedFunction(); // Works finefunction hoistedFunction() {console.log('This function is hoisted');}// Function expressionnonHoistedFunction(); // Throws an errorvar nonHoistedFunction = function () {console.log('This function is not hoisted');};
Explore the concept of hoisting with regards to functions on GreatFrontEnd
In ES2015, classes use extends to enable one class to inherit properties and methods from another. The super keyword accesses 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.
Here, Dog inherits from Animal, showcasing how classes streamline inheritance and method overriding in JavaScript.
Explore the concept of inheritance in ES2015 classes on GreatFrontEnd
Lexical scoping in JavaScript determines variable access based on its position in the source code. Nested functions can access variables from their outer scope.
function outerFunction() {let outerVariable = 'I am outside!';function innerFunction() {console.log(outerVariable); // 'I am outside!'}innerFunction();}outerFunction();
Here, innerFunction can access outerVariable due to lexical scoping rules.
Explore the concept of lexical scoping on GreatFrontEnd
Scope in JavaScript determines the visibility of variables and functions within different parts of the code. There are three main types: global scope, function scope, and block scope.
// Global scopevar globalVar = 'I am global';function myFunction() {// Function scopevar functionVar = 'I am in a function';if (true) {// Block scopelet blockVar = 'I am in a block';console.log(blockVar); // Accessible here}// console.log(blockVar); // Throws an error}console.log(globalVar); // Accessible here// console.log(functionVar); // Throws an error
Global scope variables are accessible throughout the code, while function scope variables are limited to the function they are declared in. Block scope, introduced in ES6, confines variables to the block they are declared within (e.g., within curly braces ).
Explore the concept of scope in JavaScript on GreatFrontEnd
The spread operator (...) in JavaScript expands elements of an iterable (like arrays or objects) into individual elements. It's used for copying arrays or objects, merging them, and passing array elements as function arguments.
// Copying an arrayconst arr1 = [1, 2, 3];const arr2 = [...arr1];// Merging arraysconst arr3 = [4, 5, 6];const mergedArray = [...arr1, ...arr3];// Copying an objectconst obj1 = { a: 1, b: 2 };const obj2 = { ...obj1 };// Merging objectsconst obj3 = { c: 3, d: 4 };const mergedObject = { ...obj1, ...obj3 };// Passing array elements as function argumentsconst sum = (x, y, z) => x + y + z;const numbers = [1, 2, 3];console.log(sum(...numbers)); // Output: 6
The spread operator simplifies tasks like copying, merging, and function argument handling by expanding iterable elements into individual components.
Explore the concept of the spread operator and its uses on GreatFrontEnd
this binding in event handlersIn JavaScript, the this keyword refers to the object executing the current code. In event handlers, this usually points to the element that triggered the event. However, its value can vary based on how the handler is defined and invoked. To ensure this refers correctly, methods like bind(), arrow functions, or explicit context assignment are used.
These approaches help maintain the intended context for this within event handling functions, ensuring predictable behavior across different event-triggering scenarios in JavaScript applications.
Explore the concept of this binding in event handlers on GreatFrontEnd
Classical Inheritance: In languages like Java and C++, classes inherit from other classes through a hierarchical structure. Instances are created from classes using constructors.
Prototypal Inheritance: In JavaScript, objects inherit directly from other objects. Objects serve as prototypes, and new objects are created based on existing ones.
Classical inheritance uses classes for instantiation, while prototypal inheritance leverages object linkage for property and behavior inheritance, highlighting JavaScript's unique approach to object-oriented programming.
Explore the difference between classical inheritance and prototypal inheritance on GreatFrontEnd
document.querySelector() and document.getElementById()document.querySelector() selects elements using CSS selectors and returns the first match.
const element = document.querySelector('.my-class');
document.getElementById() selects an element by its ID attribute and returns the element with that specific ID.
const elementById = document.getElementById('my-id');
While document.querySelector() offers flexibility with CSS selectors, document.getElementById() is straightforward for selecting elements by their unique IDs in the DOM.
Dot Notation: Concise and straightforward, it accesses object properties using valid identifiers.
const obj = { name: 'Alice', age: 30 };console.log(obj.name); // Alice
Bracket Notation: Flexible, it accesses properties using strings, suitable for names with special characters or dynamic properties.
const obj = { name: 'Alice', 'favorite color': 'blue' };console.log(obj['favorite color']); // blue
Dot notation is clear for standard properties, while bracket notation handles special cases like dynamic or non-standard property names effectively.
Accessible from anywhere in the code.
var globalVar = "I'm global"; // Global scope
Limited to the function where it's declared.
function myFunction() {var functionVar = "I'm in a function"; // Function scope}
Restricted to the block where let or const is used.
function myFunction() {if (true) {let blockVar = "I'm in a block"; // Block scopeconsole.log(blockVar); // Accessible here}// console.log(blockVar); // ReferenceError: blockVar is not defined}
These scopes define where variables can be accessed, from global access throughout the code to specific function or block-level access for better control and encapsulation of variables.
Explore the difference between global scope, function scope, and block scope on GreatFrontEnd
Duplicates top-level properties; nested objects remain referenced.
let obj1 = { a: 1, b: { c: 2 } };let shallowCopy = Object.assign({}, obj1);shallowCopy.b.c = 3;console.log(obj1.b.c); // 3
Duplicates all levels, creating independent nested objects.
let deepCopy = JSON.parse(JSON.stringify(obj1));deepCopy.b.c = 4;console.log(obj1.b.c); // 2
Shallow copies share references to nested objects, while deep copies create entirely new instances, ensuring independent modifications.
Explore the difference between shallow copy and deep copy on GreatFrontEnd
var, let, and constvar:**
console.log(myVar); // undefinedvar myVar = 'Hello';
let and const:**
console.log(myLet); // ReferenceError: Cannot access 'myLet' before initializationlet myLet = 'World';console.log(myConst); // ReferenceError: Cannot access 'myConst' before initializationconst myConst = '!';
const:**
const PI = 3.14;PI = 3.14159; // TypeError: Assignment to constant variable.
Explore the difference in hoisting between var, let, and const on GreatFrontEnd
Closures in JavaScript provide a mechanism to create private variables by encapsulating them within a function scope. Here's how closures can be used to achieve this:
function createCounter() {let count = 0;return {increment: () => ++count,decrement: () => --count,getCount: () => count,};}const counter = createCounter();console.log(counter.increment()); // 1console.log(counter.getCount()); // 1console.log(counter.count); // undefined
Explore how closures can be used to create private variables on GreatFrontEnd
Sets and Maps handle equality checks for objects?Sets and Maps in JavaScript determine the equality of objects based on reference equality, not by comparing their contents. This means objects are considered equal only if they point to the same memory location. For instance:
const set = new Set();const obj1 = { a: 1 };const obj2 = { a: 1 };set.add(obj1);set.add(obj2);console.log(set.size); // Output: 2
In this example, obj1 and obj2 are treated as separate entries in the Set because they are distinct objects, despite having identical properties. Therefore, Sets and Maps rely on object references to determine equality, not their internal values.
Explore how Sets and Maps handle equality checks for objects on GreatFrontEnd
To access the index of an element in an array during iteration, you can utilize methods like forEach, map, for...of with entries, or a traditional for loop. Here's an example using forEach:
const array = ['a', 'b', 'c'];array.forEach((element, index) => {console.log(index, element);});
Explore how to access the index of an element in an array during iteration on GreatFrontEnd
To determine the type of a variable in JavaScript, you use typeof followed by the variable name. It returns a string indicating the variable's type: "string", "number", "boolean", "object", "function", "undefined", or "symbol". For arrays, use Array.isArray(variableName), and for null, check variableName === null.
Explore how to check the data type of a variable on GreatFrontEnd
You've made it to the end of our extensive list of JavaScript interview questions and answers! We hope this guide has helped you gain the confidence and skills you need to ace your next JavaScript interview. Remember, practice is key, so keep coding and reviewing the concepts until they become second nature.

These 18 questions cover what front-end interviewers ask in JavaScript coding rounds, from junior screens to senior onsites. Every question includes a full working implementation you can study and adapt, plus a link to a practice page on GreatFrontEnd where you can solve it in our IDE with test cases.
If you're looking for additional JavaScript interview preparation materials, also check out:
Different interview rounds reach for different questions. Use this table to plan what to focus on first based on the role you're targeting.
| Level | What you'll be asked | Questions in this guide |
|---|---|---|
| Junior / new grad | Foundational JS, array methods, basic timing utilities | 1-6: Debounce, Throttle, Array.map, Array.filter, Array.reduce, Flatten |
| Mid-level | Object manipulation, real production patterns, common utilities | 7-13: Deep Equal, Deep Clone, Data Merging, classnames, Once, Memoize, Get |
| Senior / staff | Async patterns, OOP design, functional composition, DOM internals | 14-18: Event Emitter, Promise.all, Promise.allSettled, Curry, getElementsByClassName |
Junior questions still come up in senior rounds. The bar shifts to whether you can discuss edge cases (leading vs trailing edge, memory leaks, async ordering) rather than just produce working code.
Debouncing delays a function's execution until a certain amount of time has passed since the last call. Every new call resets the timer. The function ultimately runs once, after the burst of calls has stopped.
The classic use case is a search input: you only want to fire a request once the user pauses typing, not on every keystroke.
function debounce(fn, wait) {let timeoutId;return function (...args) {clearTimeout(timeoutId);timeoutId = setTimeout(() => fn.apply(this, args), wait);};}// Usageconst handleSearch = debounce((query) => {console.log('Search:', query);}, 300);document.getElementById('search').addEventListener('input', (e) => {handleSearch(e.target.value);});
The key details to mention if asked: forwarding this and args correctly so the wrapped function's call site behaves normally; remembering to expose a cancel() method for React unmount cleanup; and the difference between leading-edge and trailing-edge debounce (Lodash defaults to trailing only).
Practice implementing Debounce on GreatFrontEnd →
Throttling caps how often a function can run: at most once per time interval, regardless of how many times it's called. Calls during the cooldown are dropped (or in better implementations, coalesced into a trailing call).
Throttle is the right tool for scroll, mousemove, resize: events where you want regular updates during a continuous interaction.
function throttle(fn, limit) {let inThrottle = false;let lastArgs = null;return function (...args) {if (!inThrottle) {fn.apply(this, args);inThrottle = true;setTimeout(() => {inThrottle = false;if (lastArgs) {fn.apply(this, lastArgs);lastArgs = null;}}, limit);} else {lastArgs = args;}};}// Usageconst handleScroll = throttle(() => {console.log('Scroll position:', window.scrollY);}, 100);window.addEventListener('scroll', handleScroll);
For anything that paints to the screen, mention requestAnimationFrame throttling instead. rAF aligns with the browser's paint cycle and stops firing entirely in background tabs. setTimeout doesn't align with paint, and while modern browsers throttle it in background tabs (Chrome and Firefox cap it at roughly 1Hz), it keeps firing.
Practice implementing Throttle on GreatFrontEnd →
Array.prototype.mapmap returns a new array of the same length, with each element transformed by the callback. It's the most-used array method in modern JavaScript and a common ask for "implement a polyfill for X" interview rounds.
Array.prototype.myMap = function (callback, thisArg) {const result = new Array(this.length);for (let i = 0; i < this.length; i++) {if (i in this) {result[i] = callback.call(thisArg, this[i], i, this);}}return result;};// Usage[1, 2, 3].myMap((x) => x * 2); // [2, 4, 6]
Two details that separate junior from senior answers: handling sparse arrays correctly (the if (i in this) check skips holes, matching native behavior), and accepting the optional thisArg second parameter that controls the callback's this.
Practice implementing Array.prototype.map on GreatFrontEnd →
Array.prototype.filterfilter returns a new array containing only the elements for which the callback returns truthy. Like map, it preserves order and skips sparse-array holes.
Array.prototype.myFilter = function (callback, thisArg) {const result = [];for (let i = 0; i < this.length; i++) {if (i in this && callback.call(thisArg, this[i], i, this)) {result.push(this[i]);}}return result;};// Usage[1, 2, 3, 4].myFilter((x) => x % 2 === 0); // [2, 4]
A classic interview follow-up: "implement filter in terms of reduce". Worth knowing; it tests whether you understand how the array methods relate.
function filterViaReduce(arr, fn) {return arr.reduce((acc, x, i) => (fn(x, i, arr) ? [...acc, x] : acc), []);}
Practice implementing Array.prototype.filter on GreatFrontEnd →
Array.prototype.reducereduce applies a function against an accumulator and each element to reduce the array to a single value. It's the most general of the array methods; map and filter can both be expressed in terms of reduce.
Array.prototype.myReduce = function (callback, initialValue) {let accumulator;let startIndex;if (arguments.length >= 2) {accumulator = initialValue;startIndex = 0;} else {if (this.length === 0) {throw new TypeError('Reduce of empty array with no initial value');}accumulator = this[0];startIndex = 1;}for (let i = startIndex; i < this.length; i++) {if (i in this) {accumulator = callback(accumulator, this[i], i, this);}}return accumulator;};// Usage[1, 2, 3, 4].myReduce((sum, x) => sum + x, 0); // 10
Common interview gotchas to mention: the difference between providing and omitting the initial value (without it, the first element becomes the initial accumulator and iteration starts from index 1); the four parameters the callback receives (accumulator, current value, index, array); and the TypeError thrown when reducing an empty array with no initial value.
Practice implementing Array.prototype.reduce on GreatFrontEnd →
Flatten converts a nested array into a single-level array. Modern JavaScript provides Array.prototype.flat(depth) for this, but implementing it from scratch is a recurring interview ask.
function flatten(arr) {return arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? flatten(val) : val),[],);}// Usageflatten([1, [2, [3, [4, [5]]]]]); // [1, 2, 3, 4, 5]
For production code, use arr.flat(Infinity); it's more readable and handles edge cases. For the interview, the recursive reduce version above is the clean answer. A senior follow-up to be ready for: "what if the nesting is so deep that recursion overflows the stack?" Answer: use an iterative stack-based approach (push a copy of the array onto a stack, then pop and expand sub-arrays in place). Avoid arr.shift() for this; it's O(n) per call, which makes the whole traversal O(n²).
Practice implementing Flatten on GreatFrontEnd →
Deep equal recursively compares two values for structural equality. Unlike ===, which compares object references, deep equal walks both structures and compares values at every depth.
function deepEqual(a, b) {if (a === b) return true;if (a == null || b == null) return false;if (typeof a !== 'object' || typeof b !== 'object') return false;if (Array.isArray(a) !== Array.isArray(b)) return false;const keysA = Object.keys(a);const keysB = Object.keys(b);if (keysA.length !== keysB.length) return false;for (const key of keysA) {if (!keysB.includes(key) || !deepEqual(a[key], b[key])) return false;}return true;}// UsagedeepEqual({ a: 1, b: { c: 2 } }, { a: 1, b: { c: 2 } }); // truedeepEqual([1, [2, 3]], [1, [2, 3]]); // true
The basic version above handles the common case. Senior follow-ups: handling Date, RegExp, Map, Set (each needs a type-specific comparison), NaN (should be considered equal to itself, even though NaN === NaN is false), and circular references (track visited pairs in a WeakMap to avoid infinite recursion).
Practice implementing Deep Equal on GreatFrontEnd →
Deep clone creates a fully independent copy of a value, where modifying the clone never affects the original at any depth.
function deepClone(value) {if (value === null || typeof value !== 'object') return value;if (Array.isArray(value)) {return value.map(deepClone);}const result = {};for (const key of Object.keys(value)) {result[key] = deepClone(value[key]);}return result;}// Usageconst original = { a: 1, nested: { b: 2 } };const copy = deepClone(original);copy.nested.b = 99;console.log(original.nested.b); // 2 (unaffected)
For production code in modern environments, prefer the built-in structuredClone(value); it handles Date, RegExp, Map, Set, ArrayBuffer, typed arrays, and circular references correctly. It throws a DataCloneError on functions, Symbols, and most DOM nodes. It also silently strips custom prototypes, so a class instance comes back as a plain object with the own properties copied.
For the interview, the recursive version above is the standard answer. Be ready to discuss why JSON.parse(JSON.stringify(...)) is a common but buggy alternative: it drops undefined and functions, turns Date into a string, turns NaN and Infinity into null, and throws on circular references.
Practice implementing Deep Clone on GreatFrontEnd →
Merging combines multiple objects (or arrays) into one. JavaScript provides several built-in approaches; the right choice depends on whether you need shallow or deep merging.
// Shallow merge with spread (modern, idiomatic)const merged = { ...obj1, ...obj2 }; // obj2's keys overwrite obj1's// Shallow merge with Object.assign (older API, same result)const merged2 = Object.assign({}, obj1, obj2);// Deep merge: recursivefunction deepMerge(target, source) {if (typeof target !== 'object' || typeof source !== 'object') return source;const result = { ...target };for (const key of Object.keys(source)) {if (result[key] &&typeof result[key] === 'object' &&!Array.isArray(result[key]) &&typeof source[key] === 'object' &&!Array.isArray(source[key])) {result[key] = deepMerge(result[key], source[key]);} else {result[key] = source[key];}}return result;}// UsagedeepMerge({ a: 1, b: { x: 10, y: 20 } }, { b: { y: 30, z: 40 }, c: 3 });// { a: 1, b: { x: 10, y: 30, z: 40 }, c: 3 }
The interview gotcha is array merging: should [1, 2] merged with [3, 4] produce [1, 2, 3, 4] (concatenation) or [3, 4] (replacement)? There's no universally correct answer; ask the interviewer for the contract before coding.
Practice implementing Data Merging on GreatFrontEnd →
classnamesEvery React project (and most projects in other frameworks) needs a utility for conditionally combining CSS class names. The classnames package on npm is the de facto standard, and "implement classnames" is a frequent interview question.
function classnames(...args) {const classes = [];for (const arg of args) {if (!arg) continue;if (typeof arg === 'string' || typeof arg === 'number') {classes.push(arg);} else if (Array.isArray(arg)) {const inner = classnames(...arg);if (inner) classes.push(inner);} else if (typeof arg === 'object') {for (const key of Object.keys(arg)) {if (arg[key]) classes.push(key);}}}return classes.join(' ');}// Usageclassnames('btn', 'btn-primary'); // 'btn btn-primary'classnames('btn', { 'btn-disabled': isDisabled }); // 'btn' or 'btn btn-disabled'classnames('btn', ['btn-large', { active: isActive }]); // 'btn btn-large active'classnames(null, undefined, false, '', 0, 'btn'); // 'btn'
All falsy values (null, undefined, false, '', 0, NaN) are skipped, which matches the published classnames package; its top-of-loop if (!arg) continue filters them out uniformly. If the interviewer wants 0 treated as a valid class, swap the falsy guard for an explicit nullish/empty check.
Practice implementing classnames on GreatFrontEnd →
once wraps a function so it only ever runs the first time it's called. Subsequent calls return the cached first-call result without invoking the wrapped function.
function once(fn) {let called = false;let result;return function (...args) {if (!called) {called = true;result = fn.apply(this, args);}return result;};}// Usageconst init = once(() => {console.log('Initializing...');return { timestamp: Date.now() };});init(); // Logs "Initializing..." and returns { timestamp: ... }init(); // Returns the same object, doesn't log
A small question that covers a lot: closures (the called and result variables persist between calls), this/args forwarding, and why caching the result matters (so the second caller gets the same value, not undefined). Common follow-up: "make it so the wrapped function can be reset": add a .reset() method that clears called and result.
Practice implementing Once on GreatFrontEnd →
Memoize wraps an expensive function and caches its results by argument, so repeated calls with the same input return the cached value instead of recomputing.
function memoize(fn) {const cache = new Map();return function (...args) {const key = JSON.stringify(args);if (cache.has(key)) {return cache.get(key);}const result = fn.apply(this, args);cache.set(key, result);return result;};}// Usageconst slowSquare = (n) => {console.log('Computing', n);return n * n;};const fastSquare = memoize(slowSquare);fastSquare(5); // Logs "Computing 5", returns 25fastSquare(5); // Returns 25 from cache (no log)fastSquare(6); // Logs "Computing 6", returns 36
Two interview gotchas worth raising before the interviewer does:
JSON.stringify is a brittle key strategy: functions in arguments are dropped (replaced with null), circular references throw, and {a: 1, b: 2} and {b: 2, a: 1} produce different keys despite being structurally equivalent (modern engines preserve insertion order in stringification). For real production code, accept a custom resolver function (Lodash's API) or use a WeakMap keyed by the first argument when arguments are objects.Map with a max size for any function called with many distinct inputs.Practice implementing Memoize on GreatFrontEnd →
get reads a property at a nested path on an object, returning a fallback if any part of the path is missing. Before optional chaining landed in JavaScript, this was one of Lodash's most reached-for utilities.
function get(obj, path, defaultValue) {const keys = Array.isArray(path) ? path : path.split('.');let result = obj;for (const key of keys) {if (result == null) return defaultValue;result = result[key];}return result === undefined ? defaultValue : result;}// Usageconst user = { profile: { name: 'Alice', address: { city: 'NYC' } } };get(user, 'profile.name'); // 'Alice'get(user, 'profile.address.zip'); // undefinedget(user, 'profile.address.zip', 'unknown'); // 'unknown'
Modern JavaScript usually doesn't need this; optional chaining (user?.profile?.address?.city ?? 'unknown') is more readable and built into the language. The interview value is showing you understand both the historical reason for the function and the modern equivalent. A common follow-up: "what if the path includes array indices like users[0].name?" The split('.') approach above doesn't handle bracket notation; you'd need a regex split or a tokenizer for full Lodash compatibility.
Practice implementing Get on GreatFrontEnd →
An EventEmitter is a publish-subscribe primitive: listeners subscribe to named events, the emitter triggers all listeners for an event when emit is called. Node.js has a built-in EventEmitter; browsers have EventTarget. Implementing one tests your grasp of OOP, closures, and array manipulation.
class EventEmitter {constructor() {this.events = new Map();}on(event, listener) {if (!this.events.has(event)) this.events.set(event, []);this.events.get(event).push(listener);return this;}off(event, listener) {const listeners = this.events.get(event);if (!listeners) return this;this.events.set(event,listeners.filter((l) => l !== listener),);return this;}emit(event, ...args) {const listeners = this.events.get(event);if (!listeners || listeners.length === 0) return false;// Snapshot so listeners that unsubscribe themselves don't break iteration.[...listeners].forEach((listener) => listener.apply(this, args));return true;}}// Usageconst emitter = new EventEmitter();const handler = (data) => console.log('Got:', data);emitter.on('message', handler);emitter.emit('message', { text: 'hello' }); // Logs "Got: { text: 'hello' }"emitter.off('message', handler);emitter.emit('message', { text: 'world' }); // No log
The bug interviewers love to test is the listener-self-unsubscribe case: if a listener calls emitter.off(...) for itself inside emit, the array gets modified mid-iteration and the next listener is skipped. The [...listeners] snapshot above prevents it. Mention this without being asked.
Practice implementing Event Emitter on GreatFrontEnd →
Promise.allPromise.all takes an iterable of promises and returns a promise that fulfills with all results in input order, or rejects on the first rejection.
function promiseAll(promises) {return new Promise((resolve, reject) => {const results = new Array(promises.length);let remaining = promises.length;if (remaining === 0) {resolve(results);return;}promises.forEach((promise, index) => {Promise.resolve(promise).then((value) => {results[index] = value;remaining--;if (remaining === 0) resolve(results);},(reason) => reject(reason),);});});}// Usageconst results = await promiseAll([Promise.resolve(1),42,new Promise((r) => setTimeout(() => r('foo'), 100)),]);console.log(results); // [1, 42, 'foo']
A few specifics to mention even if not asked: Promise.all([]) resolves with [] (NOT rejects); non-promise values in the input array are wrapped via Promise.resolve and included in order; on first rejection, the other promises keep running but their results are discarded. JS promises themselves aren't cancellable; if you need to cancel the underlying work (a fetch, for example), pass an AbortController signal to it. For the full comparison with Promise.allSettled, see the next question.
Practice implementing Promise.all on GreatFrontEnd →
Promise.allSettledPromise.allSettled resolves once all input promises have settled, with an array of { status, value } or { status, reason } objects. Unlike Promise.all, it never rejects, even when individual inputs reject.
function promiseAllSettled(promises) {return new Promise((resolve) => {const results = new Array(promises.length);let remaining = promises.length;if (remaining === 0) {resolve(results);return;}promises.forEach((promise, index) => {Promise.resolve(promise).then((value) => {results[index] = { status: 'fulfilled', value };remaining--;if (remaining === 0) resolve(results);},(reason) => {results[index] = { status: 'rejected', reason };remaining--;if (remaining === 0) resolve(results);},);});});}// Usageconst results = await promiseAllSettled([Promise.resolve(1),Promise.reject(new Error('boom')),Promise.resolve(3),]);// [// { status: 'fulfilled', value: 1 },// { status: 'rejected', reason: Error('boom') },// { status: 'fulfilled', value: 3 }// ]
The when-to-use distinction matters: reach for Promise.all when partial success is invalid (transactional sequences, prefetches that gate a render). Reach for Promise.allSettled when partial results are still useful (dashboard widgets where one slow endpoint shouldn't blank the screen, bulk operations with per-row UX). The common mistake is wrapping Promise.all in try/catch to "handle" individual failures; that catches the first rejection but loses the rest of the results. Use allSettled instead.
Practice implementing Promise.allSettled on GreatFrontEnd →
Currying transforms a function of N arguments into a sequence of N functions of 1 argument each. The classic example is curry(add3) letting you call add3(1)(2)(3), add3(1, 2)(3), or add3(1)(2, 3) interchangeably.
function curry(fn) {return function curried(...args) {if (args.length >= fn.length) {return fn.apply(this, args);}return function (...nextArgs) {return curried.apply(this, [...args, ...nextArgs]);};};}// Usageconst add = (a, b, c) => a + b + c;const curriedAdd = curry(add);curriedAdd(1)(2)(3); // 6curriedAdd(1, 2)(3); // 6curriedAdd(1)(2, 3); // 6curriedAdd(1, 2, 3); // 6
The trick is using fn.length to know how many arguments the original function expects. The recursion in curried collects arguments across calls until the count is reached. Common follow-up: "implement curry that supports a placeholder for skipping arguments"; that's a separate variant (Curry II in our practice catalog) and a good test of whether you can extend a clean recursion to handle more state.
Practice implementing Curry on GreatFrontEnd →
getElementsByClassNamegetElementsByClassName returns a live HTMLCollection of elements that match one or more class names. The interview challenge is implementing it from scratch using DOM traversal, which is a real test of recursion and attribute matching.
function getElementsByClassName(root, classNames) {const targetClasses = classNames.trim().split(/\s+/);const results = [];function traverse(node) {if (node.nodeType !== 1) return; // Only Element nodes.const nodeClasses = (node.getAttribute('class') || '').trim().split(/\s+/);if (targetClasses.every((cls) => nodeClasses.includes(cls))) {results.push(node);}for (const child of node.children) {traverse(child);}}for (const child of root.children) {traverse(child);}return results;}// Usage// Given <div><span class="a"><i class="a b"/></span><i class="a"/></div>getElementsByClassName(document.body, 'a b'); // [<i class="a b">]
The key gotchas: the input class string can contain multiple space-separated class names, and the element must have ALL of them (not any); the comparison is on the parsed class list, so class="ab" is not a match for "a b"; the implementation should NOT include the root element itself, only its descendants. The native getElementsByClassName returns a LIVE collection that updates as the DOM changes; most interview implementations return a static array, which is acceptable as long as you mention the difference.
Practice implementing getElementsByClassName on GreatFrontEnd →
If you have a week before an interview, work through questions 1-6 (the junior set) twice: once writing them from a blank file, once reviewing the senior follow-ups. If you have a month, do all 18 in order. The questions are arranged so the patterns build on each other: closures appear in Debounce, Throttle, Once, and Memoize; recursion appears in Flatten, Deep Equal, Deep Clone, and getElementsByClassName; promise composition appears in Promise.all, Promise.allSettled, and is a common follow-up to Event Emitter (replacing callbacks with promises).
Practice each one on the linked GreatFrontEnd page; the IDE runs the same kinds of test cases real interviewers use, so edge cases surface before the interview does.
If you'd like to go beyond these 18 questions, check out our open-source GitHub repo featuring +190 JavaScript interview questions with detailed answers and test cases.

JavaScript interviews for experienced engineers demand more than just a basic understanding of syntax and concepts. They require a deep dive into advanced topics that demonstrate your ability to solve complex problems and architect robust solutions. Whether you're aiming to advance your career or secure a new role, mastering these 20 advanced JavaScript interview questions will not only enhance your technical prowess but also set you apart from others.
If you're looking for additional JavaScript interview preparation materials, also check out these resources:
function foo(){ }();. What needs to be changed to properly make it an IIFE?IIFE stands for Immediately Invoked Function Expressions. The JavaScript parser reads function foo(){ }(); as function foo(){ } and ();, where the former is a function declaration and the latter is an attempt at calling a function without a name. This results in a SyntaxError.
To fix this, wrap the function in parentheses: (function foo(){ })(). This turns it into a function expression, allowing it to be executed immediately.
In JavaScript, iterators and generators are powerful tools for managing sequences of data and controlling the flow of execution in a more flexible way.
Iterators are objects that define a sequence and provide a next() method to access the next value in the sequence. They are used to iterate over data structures like arrays, strings, and custom objects.
Creating a custom iterator for a range of numbers
In JavaScript, we can provide a default implementation for iterator by implementing [Symbol.iterator]() in any custom object.
class Range {constructor(start, end) {this.start = start;this.end = end;}[Symbol.iterator]() {let current = this.start;const end = this.end;return {next() {if (current <= end) {return { value: current++, done: false };} elsereturn { value: undefined, done: true };}},};}}const range = new Range(1, 3);for (const number of range) {console.log(number); // 1, 2, 3}
Generators are a special kind of function that can pause and resume their execution, allowing them to generate a sequence of values on-the-fly. They are commonly used to create iterators but have other applications as well.
Creating an iterator using a generator function
We can rewrite our Range example to use a generator function:
class Range {constructor(start, end) {this.start = start;this.end = end;}*[Symbol.iterator]() {let current = this.start;while (current <= this.end) {yield current++;}}}const range = new Range(1, 3);for (const number of range) {console.log(number); // 1, 2, 3}
Iterating over data streams
Generators are well-suited for iterating over data streams, such as fetching data from an API or reading files.
function* fetchDataInBatches(url, batchSize = 10) {let startIndex = 0;while (true) {const response = await fetch(`${url}?start=${startIndex}&limit=${batchSize}`);const data = await response.json();if (data.length === 0) break;yield data;startIndex += batchSize;}}const dataGenerator = fetchDataInBatches('https://api.example.com/data');for await (const batch of dataGenerator) {console.log(batch);}
Explore what iterators and generators are in JavaScript and what they are used for on GreatFrontEnd
Property flags and descriptors in JavaScript manage how object properties behave, allowing control over property access, modification, and inheritance.
Property flags are used to specify the behavior of a property on an object. Here are the available flags:
writable: Can the property be written to? Default is true.enumerable: Is the property enumerable? Default is true.configurable: Can the property be deleted or reconfigured? Default is true.When we create a property the usual way, all of them are true. But we also can change them anytime.
Property descriptors provide detailed information about a property, including its value and flags. Use Object.getOwnPropertyDescriptor() to retrieve and Object.defineProperty() to set them.
Example:
let user = { name: 'John Doe' };let descriptor = Object.getOwnPropertyDescriptor(user, 'name');console.log(descriptor); // {value: "John Doe", writable: true, enumerable: true, configurable: true}
writable: Controls if a property can be written to. If false, writing fails silently in non-strict mode and throws TypeError in strict mode.
const obj = {};Object.defineProperty(obj, 'name', { writable: false, value: 'John Doe' });console.log(obj.name); // John Doeobj.name = 'Jane Doe'; // TypeError in strict mode
enumerable: Controls if a property is visible in for...in loops.
const obj = {};Object.defineProperty(obj, 'name', {enumerable: false,value: 'John Doe',});for (const prop in obj) console.log(prop); // No output
configurable: Controls if a property can be deleted or reconfigured. If false, deleting or altering fails silently in non-strict mode and throws TypeError in strict mode.
const obj = {};Object.defineProperty(obj, 'name', {configurable: false,value: 'John Doe',});delete obj.name; // TypeError in strict mode
Explore what javascript object property flags and descriptors are on GreatFrontEnd
Polyfills are scripts that enable modern JavaScript features in older browsers that lack support, allowing developers to use the latest language features while maintaining compatibility.
Polyfills detect missing features and provide custom implementations using existing JavaScript. For example, Array.prototype.includes() is not supported in older browsers like Internet Explorer 11:
if (!Array.prototype.includes) {Array.prototype.includes = function (searchElement) {for (var i = 0; i < this.length; i++) {if (this[i] === searchElement) return true;}return false;};}
typeof, in, or window.core-js: Provides polyfills for many ECMAScript features.
import 'core-js/actual/array/flat-map';[1, 2].flatMap((it) => [it, it]); // => [1, 1, 2, 2]
Polyfill.io: Serves polyfills based on requested features and user agents.
<script src="https://polyfill.io/v3/polyfill.min.js"></script>
Polyfills ensure modern JavaScript features work across all browsers, enhancing compatibility and functionality.
Explore what JavaScript polyfills are for on GreatFrontEnd
Server-Sent Events (SSE) is a standard that allows servers to push updates to web clients over a single, long-lived HTTP connection. This enables real-time updates without the client constantly polling the server for new data.
EventSource object, providing the URL of the server-side script that generates the event stream.event, data, and id.EventSource object receives events and dispatches them as browser events, which can be handled using event listeners.EventSource automatically handles reconnection if the connection is lost, resuming the stream from the last received event ID.Last-Event-Id: The client sends the Last-Event-Id header when reconnecting, allowing the server to resume the stream.Client:
const eventSource = new EventSource('/sse');eventSource.onmessage = (event) => console.log('New message:', event.data);
Server (Node.js):
const http = require('http');http.createServer((req, res) => {if (req.url === '/sse') {// Set headers for SSEres.writeHead(200, {'Content-Type': 'text/event-stream','Cache-Control': 'no-cache',Connection: 'keep-alive',});// Function to send a messageconst sendMessage = (message) => {res.write(`data: ${message}\n\n`); // Messages are delimited with double line breaks.};// Send a message every 5 secondsconst intervalId = setInterval(() => {sendMessage(`Current time: ${new Date().toLocaleTimeString()}`);}, 5000);// Handle client disconnectreq.on('close', () => {clearInterval(intervalId);res.end();});} else {res.writeHead(404);res.end();}}).listen(8080, () => {console.log('SSE server running on port 8080');});
SSE provides an efficient and straightforward way to push updates from a server to a client in real-time. It is well-suited for applications requiring continuous data streams but not full bidirectional communication.
Explore what server-sent events are on GreatFrontEnd
JavaScript workers run scripts in background threads, offloading intensive tasks to keep the user interface responsive. There are three main types of workers in JavaScript: Web Workers / Dedicated Workers, Service Workers and Shared Workers.
postMessage() and onmessage.main.js:
// Check if the browser supports workersif (window.Worker) {// Create a new Workerconst myWorker = new Worker('worker.js');// Post a message to the workermyWorker.postMessage('Hello, Worker!');// Listen for messages from the workermyWorker.onmessage = function (event) {console.log('Message from Worker:', event.data);};// Error handlingmyWorker.onerror = function (error) {console.error('Error from Worker:', error);};}
worker.js:
// Listen for messages from the main scriptonmessage = function (event) {console.log('Message from Main Script:', event.data);// Perform a task (e.g., some computation)const result = event.data + ' - Processed by Worker';// Post the result back to the main scriptpostMessage(result);};
main.js:
if ('serviceWorker' in navigator) {navigator.serviceWorker.register('/service-worker.js').then((registration) => {console.log('Service Worker registered:', registration);}).catch((err) => {console.log('Service Worker registration failed:', err);});}
service-worker.js:
self.addEventListener('fetch', (event) => {event.respondWith(caches.match(event.request).then((response) => {return response || fetch(event.request);}),);});
Explore what workers in JavaScript are used for on GreatFrontEnd
"use strict";? What are the advantages and disadvantages to using it?"use strict"; is a directive from ECMAScript 5 (ES5) that enforces stricter parsing and error handling in JavaScript, making code more secure and less error-prone.
Global Scope: Add at the beginning of a JavaScript file.
'use strict';function add(a, b) {return a + b;}
Local Scope: Add at the beginning of a function.
function myFunction() {'use strict';// Strict mode only within this function}
arguments.caller.eval() to prevent variable declarations in the calling scope.// Without strict modefunction defineNumber() {count = 123;}defineNumber();console.log(count); // Logs: 123// With strict mode('use strict');function strictFunc() {strictVar = 123; // ReferenceError}strictFunc();console.log(strictVar); // ReferenceError
While "use strict"; is not mandatory in these contexts, it is still recommended for older code and broader compatibility.
Explore what "use strict" is and its advantages and disadvantages on GreatFrontEnd
To secure authentication and authorization in JavaScript applications, use HTTPS to encrypt data in transit and store sensitive data like tokens securely with localStorage or sessionStorage. Employ token-based authentication using JWTs, validating tokens server-side. Utilize libraries like OAuth for third-party authentication and enforce role-based access control (RBAC) for proper authorization.
Minimize direct DOM access by batching changes, using documentFragment, and leveraging virtual DOM libraries like React. Use requestAnimationFrame for animations and avoid layout thrashing by separating DOM reads and writes.
Explore how to optimize DOM manipulation for better performance on GreatFrontEnd
Minimize the number of requests, use caching, compress data, and leverage HTTP/2 and service workers. Combine CSS files, use Cache-Control headers for static assets, and enable Gzip compression to reduce data size.
Explore how to optimize network requests for better performance on GreatFrontEnd
Prevent clickjacking by using the X-Frame-Options HTTP header set to DENY or SAMEORIGIN to control iframe embedding. Additionally, use the Content-Security-Policy header with the frame-ancestors directive to specify allowed origins.
X-Frame-Options: DENYContent-Security-Policy: frame-ancestors 'self'
Explore how to prevent clickjacking attacks on GreatFrontEnd
Use the Constraint Validation API with properties like validity and validationMessage, and methods like checkValidity() and setCustomValidity(). For example:
const input = document.querySelector('input');if (input.checkValidity()) {console.log('Input is valid');} else {console.log(input.validationMessage);}
Explore how to validate form elements using the Constraint Validation API on GreatFrontEnd
In JavaScript, hoisting moves function declarations to the top of their scope, making them callable before their definition. Function expressions are not hoisted similarly; the variable is hoisted, but its assignment is not.
// Function declarationconsole.log(foo()); // Works finefunction foo() {return 'Hello';}// Function expressionconsole.log(bar()); // Throws TypeError: bar is not a functionvar bar = function () {return 'Hello';};
Explore how hoisting affects function declarations and expressions on GreatFrontEnd
JavaScript uses automatic garbage collection to reclaim memory from objects and variables no longer in use. The two main algorithms are mark-and-sweep and generational garbage collection.
Mark-and-Sweep
Generational Garbage Collection Modern engines divide objects into generations based on age and usage. Frequently accessed objects stay in younger generations, while less-used objects move to older generations, optimizing garbage collection by focusing on short-lived objects.
Different JavaScript engines may use different garbage collection strategies.
Explore how JavaScript garbage collection works on GreatFrontEnd
Mocks and stubs simulate real objects in testing. Stubs provide predefined responses to function calls, isolating the code being tested from external dependencies. Mocks are more complex, verifying interactions like whether a function was called and with what arguments. Stubs focus on isolating functionality, while mocks ensure correct interaction with dependencies.
Explore what mocks and stubs are and how they are used in testing on GreatFrontEnd
A proxy in JavaScript is an intermediary object that intercepts and customizes operations on another object, such as property access, assignment, and function invocation.
Example:
const myObject = {name: 'John',age: 42,};const handler = {get: function (target, prop) {console.log(`Accessed property "${prop}"`);return target[prop];},};const proxiedObject = new Proxy(myObject, handler);console.log(proxiedObject.name); // Logs: 'John'// Accessed property "name"console.log(proxiedObject.age); // Logs: 42// Accessed property "age"
Use cases:
Explore what proxies in JavaScript are used for on GreatFrontEnd
Using languages like TypeScript or CoffeeScript, which compile to JavaScript, has several pros and cons.
Advantages:
Disadvantages:
requestAnimationFrame for synchronized animations.will-change to elements that change frequently.Explore techniques for reducing reflows and repaints on GreatFrontEnd
Tools such as Chrome DevTools, Lighthouse, WebPageTest, and JSPerf are commonly used for this purpose. Chrome DevTools includes a Performance panel for profiling, Lighthouse provides performance audits, WebPageTest offers detailed performance testing, and JSPerf aids in comparing JavaScript snippet performance.
Explore tools to measure and analyze JavaScript performance on GreatFrontEnd
Web Workers enable running JavaScript in the background, independent of the main execution thread of a web application. This is beneficial for handling intensive computations without blocking the user interface. Web Workers are created using the Worker constructor and communication with them is facilitated through the postMessage and onmessage methods.
Explore what Web Workers are and how they can be used to improve performance on GreatFrontEnd
Preparing yourself to answer these questions in an interview setting will certainly help you stand out from the crowd. It's not just about knowing the answers; it's about understanding the underlying concepts and applying them effectively in real-world scenarios. Mastering these advanced JavaScript topics will not only boost your confidence during technical interviews but also equip you to build scalable and efficient web applications.

As the demand for skilled front-end developers rises in 2025, proficiency in React has become essential. This JavaScript library is widely used for building dynamic user interfaces, and mastering it requires a solid understanding of its core principles and advanced features.
To help you prepare for your upcoming interviews, we've compiled 30 essential React interview questions and answers that cover a range of topics from fundamental concepts to advanced techniques. Whether you're an experienced developer or new to React, this guide will equip you with the knowledge needed to excel in interviews.
If you're looking for more in-depth React interview preparation materials, also check out these resources:
React Node, Element, and Component are three fundamental concepts in React.
Node: A React Node is any renderable unit in React, like an element, string, number, or null.
Element: An element is a plain object that represents a DOM element or a component. It describes what you want to see on the screen. Elements are immutable and are used to create React components.
Component: A component is a reusable piece of UI that can contain one or more elements. Components can be either functional or class-based. They accept inputs called props and return React elements that describe what should appear on the screen.
React Fragments are a feature introduced in React 16.2 that allows you to group multiple children elements without adding extra nodes to the DOM. Fragments are useful when you need to return multiple elements from a component but don't want to wrap them in a parent element. They help keep the DOM structure clean and avoid unnecessary div wrappers.
Here's an example of using React Fragments:
function App() {return (<><Header /><Main /><Footer /></>);}
In this example, the <>...</> syntax is a shorthand for declaring a React Fragment. It allows you to group the Header, Main, and Footer components without adding an extra div to the DOM.
key prop in React?The key prop is a special attribute used in React to uniquely identify elements in a list. When rendering a list of elements, React uses the key prop to keep track of each element's identity and optimize the rendering process. The key prop helps React identify which items have changed, been added, or been removed, allowing it to update the DOM efficiently.
Using array indices as keys in React can lead to performance issues and unexpected behavior. When you use array indices as keys, React uses the index to identify elements and track changes. However, this approach can cause problems when the array is modified, as React may not be able to differentiate between elements with the same key.
For example, consider the following code:
function App() {const items = ['A', 'B', 'C'];return (<ul>{items.map((item, index) => (<li key={index}>{item}</li>))}</ul>);}
If you add or remove items from the items array, React may not update the DOM correctly because it relies on the index as the key. To avoid this issue, it's recommended to use unique IDs or keys that are stable across renders.
Controlled and Uncontrolled components are two common patterns used in React to manage form inputs and state.
Controlled Components: In a controlled component, form data is handled by React state and is updated via state changes. The input value is controlled by React, and any changes to the input are handled by React event handlers. Controlled components provide more control over form inputs and allow you to validate and manipulate the input data before updating the state.
Uncontrolled Components: In an uncontrolled component, form data is handled by the DOM itself, and React does not control the input value. The input value is managed by the DOM, and you can access the input value using a ref. Uncontrolled components are useful for integrating with third-party libraries or when you need to access the input value imperatively.
Example of controlled component:
function ControlledInput() {const [value, setValue] = React.useState('');return (<inputtype="text"value={value}onChange={(e) => setValue(e.target.value)}/>);}
Example of uncontrolled component:
function UncontrolledInput() {const inputRef = React.useRef();return <input type="text" ref={inputRef} />;}
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. Example:
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>);
Pure Components are a type of React component that extends React.PureComponent or uses the React.memo higher-order component. Pure Components are optimized for performance and implement a shouldComponentUpdate method that performs a shallow comparison of props and state to determine if the component should re-render. If the props and state of a Pure Component have not changed, React skips the re-rendering process, improving performance.
Pure Components are useful when you have components that render the same output given the same input and don't rely on external state or side effects. By using Pure Components, you can prevent unnecessary re-renders and optimize your React application.
createElement and cloneElement?createElement: Used to create a new React element by specifying its type (e.g., 'div', a React component), props, and children.React.createElement('div', { className: 'container' }, 'Hello World');
cloneElement: Used to clone an existing React element and optionally modify its props while keeping the original element's children and state.const element = <button className="btn">Click Me</button>;const clonedElement = React.cloneElement(element, { className: 'btn-primary' });
PropTypes is a library used in React to validate the props passed to a component. PropTypes help you define the types of props a component expects and provide warnings in the console if the props are of the wrong type. PropTypes are useful for documenting component APIs, catching bugs early, and ensuring that components receive the correct data.
Here's an example of using PropTypes:
import PropTypes from 'prop-types';const Greeting = ({ name }) => <h1>Hello, {name}!</h1>;Greeting.propTypes = {name: PropTypes.string.isRequired,};
In this example, the Greeting component expects a prop named name of type string. If the name prop is not provided or is not a string, a warning will be displayed in the console.
Stateless components do not manage internal state; they receive data via props and focus solely on rendering UI based on that data.
Example:
function StatelessComponent({ message }) {return <div>{message}</div>;}
Stateful components manage their own internal state and can update their UI based on user interactions or other events.
Example:
function StatefulComponent() {const [count, setCount] = React.useState(0);return (<div><p>{count}</p><button onClick={() => setCount(count + 1)}>Increment</button></div>);}
Hooks allow you to use state and other React features in functional components, eliminating the need for classes. They simplify code by reducing reliance on lifecycle methods, improve code readability, and make it easier to reuse stateful logic across components. Common hooks like useState and useEffect help manage state and side effects.
React hooks follow a set of rules to ensure they are used correctly:
Only call hooks at the top level: Hooks should only be called from the top level of a functional component or from custom hooks. They should not be called inside loops, conditions, or nested functions.
Only call hooks from React functions: Hooks should only be called from React components or custom hooks. They should not be called from regular JavaScript functions.
Use hooks in the same order: Hooks should be called in the same order on every render to ensure the component's state is consistent.
Don't call hooks conditionally: Hooks should not be called conditionally based on a condition. They should always be called in the same order on every render.
useEffect and useLayoutEffect in React?useEffect: Runs after the browser has painted the screen and is used for side effects that don't block the browser's painting process. It's asynchronous and runs after the render is committed to the DOM.
useLayoutEffect: Runs synchronously after the DOM has been updated but before the browser has painted the screen. It's used for side effects that require the DOM to be updated synchronously.
In most cases, you should use useEffect unless you need to perform a synchronous side effect that requires the DOM to be updated immediately.
Example:
import React, { useEffect, useLayoutEffect, useRef } from 'react';function Example() {const ref = useRef();useEffect(() => {console.log('useEffect: Runs after DOM paint');});useLayoutEffect(() => {console.log('useLayoutEffect: Runs before DOM paint');console.log('Element width:', ref.current.offsetWidth);});return <div ref={ref}>Hello</div>;}
useEffect affect?The dependency array of useEffect specifies the values that the effect depends on. When the values in the dependency array change, the effect is re-run. If the dependency array is empty, the effect runs only once after the initial render.
Example:
useEffect(() => {// Effect code}, [value1, value2]);
In this example, the effect will run whenever value1 or value2 changes. If the dependency array is omitted ([]), the effect will run only once after the initial render.
useRef hook in React and when should it be used?The useRef hook in React creates a mutable reference that persists across renders. It can be used to store references to DOM elements, manage focus, or store mutable values that don't trigger re-renders.
Example:
function TextInputWithFocusButton() {const inputRef = useRef(null);const handleClick = () => {inputRef.current.focus();};return (<><input ref={inputRef} type="text" /><button onClick={handleClick}>Focus Input</button></>);}
In this example, the inputRef is used to store a reference to the input element, and the handleClick function focuses the input when the button is clicked.
React recommends against mutating state directly because it can lead to unexpected behavior and bugs. When you mutate state directly, React may not detect the changes, causing the UI to become out of sync with the application's state. To ensure that React detects state changes correctly, you should always update state using the setState function or hooks like useState.
Reconciliation is the process by which React updates the DOM to match the virtual DOM after a component's state or props change. React compares the previous virtual DOM with the new virtual DOM and determines the minimum number of changes needed to update the DOM efficiently. Reconciliation is an essential part of React's performance optimization strategy and helps minimize the number of DOM manipulations required.
Hydration is the process by which React attaches event listeners and updates the DOM to match the virtual DOM on the client side. Hydration is necessary for server-side rendered React applications to ensure that the client-side rendered content is interactive and matches the server-rendered content. During hydration, React reconciles the server-rendered HTML with the client-side virtual DOM and updates the DOM to match the virtual DOM structure.
Higher-order components (HOCs) are functions that take a component as an argument and return a new component with enhanced functionality. HOCs are used to share code between components, add additional props or behavior to components, and abstract common logic into reusable functions.
Example:
const withLogger = (WrappedComponent) => {return (props) => {console.log('Component rendered:', WrappedComponent.name);return <WrappedComponent {...props} />;};};const EnhancedComponent = withLogger(MyComponent);
In this example, the withLogger HOC logs the name of the component every time it renders. The EnhancedComponent is a new component that includes the logging functionality.
Some common performance optimization techniques in React include:
Memoization: Use memoization techniques like useMemo and useCallback to cache expensive computations and prevent unnecessary re-renders.
Code Splitting: Split your code into smaller chunks and load them dynamically to reduce the initial bundle size and improve loading times.
Lazy Loading: Use lazy loading to load components or resources only when they are needed, reducing the initial load time of your application.
Virtualization: Implement virtualization techniques like windowing or infinite scrolling to render only the visible elements in long lists or tables, improving performance.
Server-Side Rendering: Use server-side rendering to pre-render your React components on the server and send the HTML to the client, reducing the time to first paint.
useReducer hook in React?The useReducer hook in React is used to manage complex state logic in functional components. It is an alternative to useState and allows you to update state based on the previous state and an action. useReducer is useful for managing state transitions that depend on the current state and require more complex logic than simple updates.
Example:
const initialState = { count: 0 };const reducer = (state, action) => {switch (action.type) {case 'increment':return { count: state.count + 1 };case 'decrement':return { count: state.count - 1 };default:return state;}};const Counter = () => {const [state, dispatch] = useReducer(reducer, initialState);return (<div>Count: {state.count}<button onClick={() => dispatch({ type: 'increment' })}>Increment</button><button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button></div>);};
In this example, the useReducer hook is used to manage the state of a counter component based on different actions.
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.
Server-side rendering (SSR) is a technique used to pre-render React components on the server and send the HTML to the client. SSR improves the performance of React applications by reducing the time to first paint and making the content accessible to search engines and users with slow internet connections.
Static site generation (SSG) is a technique used to pre-render static HTML pages at build time. SSG generates static HTML files for each page of a website, which can be served directly to users without the need for server-side rendering. SSG improves performance, reduces server load, and simplifies hosting and deployment.
Lazy loading is a technique used to load components or resources only when they are needed. Lazy loading helps reduce the initial load time of your application by deferring the loading of non-essential components until they are required. React provides a React.lazy function and Suspense component to implement lazy loading in your application.
useContext hook in React?The useContext hook in React is used to access the value of a context provider in a functional component. It allows you to consume context values without using a consumer component. useContext is useful for accessing global data or settings in your application without passing props down the component tree.
Example:
const ThemeContext = React.createContext('light');const ThemeProvider = ({ children }) => {return <ThemeContext.Provider value="dark">{children}</ThemeContext.Provider>;};const ThemeConsumer = () => {const theme = React.useContext(ThemeContext);return <div>Theme: {theme}</div>;};
In this example, the ThemeConsumer component uses the useContext hook to access the value of the ThemeContext provider.
useMemo hook in React?The useMemo hook in React is used to memoize expensive computations and cache the result to prevent unnecessary re-renders. useMemo takes a function and an array of dependencies and returns the memoized value. The memoized value is recalculated only when the dependencies change.
Example:
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
In this example, the computeExpensiveValue function is memoized, and the result is cached until the a or b dependencies change.
useCallback hook in React?The useCallback hook in React is used to memoize callback functions and prevent unnecessary re-renders of components that depend on those callbacks. useCallback takes a function and an array of dependencies and returns a memoized version of the function. The memoized function is only recalculated when the dependencies change.
Example:
const memoizedCallback = useCallback(() => {doSomething(a, b);}, [a, b]);
In this example, the doSomething function is memoized, and the memoized callback is cached until the a or b dependencies change.
useImperativeHandle hook in React?The useImperativeHandle hook in React is used to customize the instance value that is exposed to parent components when using React.forwardRef. It allows you to define which properties or methods of a child component's instance should be accessible to parent components when using a ref.
Example:
const ChildComponent = React.forwardRef((props, ref) => {const inputRef = useRef(null);useImperativeHandle(ref, () => ({focus: () => {inputRef.current.focus();},}));return <input ref={inputRef} />;});const ParentComponent = () => {const childRef = useRef(null);const handleClick = () => {childRef.current.focus();};return (<><ChildComponent ref={childRef} /><button onClick={handleClick}>Focus Input</button></>);};
In this example, the useImperativeHandle hook is used to expose the focus method of the inputRef to the parent component when using a ref.
If you'd like to explore more React interview problems beyond these 30, check out our Top ReactJS Interview Questions repository - a curated list of around 50 real-world questions sourced from actual interview experiences.

If you have used LinkedIn recently, you are sure to have seen posts from Jordan Cutler, who often shares bite-sized tips regarding software engineering career growth on the platform. Jordan is currently a Senior Frontend Engineer at Pinterest and runs the popular newsletter, "High Growth Engineer".
If you missed it, Jordan also contributed the popular blog article Top 5 CSS Mistakes made by Front End Engineers.
The GreatFrontEnd team recently had the pleasure of interviewing him.
Hey there 👋 and yeah! I'd love to. A few things I like to do are train my cats (one can do a high-five), play frisbee, and spend time with my girlfriend. My go-to comfort food would be french toast or pancakes. I love ALL breakfast food.
Even though I started my newsletter last year, it goes back to my time working at Gusto ~4 years ago.
Part of what led to me growing to Senior Engineer quickly was being so public about what I was learning.
I did demos, presentations, and learning posts in Slack sharing what I knew. That helped me become better and grow others.
When I wanted to learn frontend more, I made a Slack channel called #jordans-frontend-learnings. In it, I shared tiny snippets of frontend knowledge I was learning. Other people could join and learn, and since the channel was called #jordans-frontend-learnings, I didn't feel bad about spamming.
Eventually, over 50 people joined, and it became a community of people sharing their learnings, so we changed the name to #frontend-learning.
From starting that channel, a few things happened:
That 3rd point is so key to my newsletter and writing online.
I still remember exact posts I wrote in the #frontend-learning channel to this day. It's not the same as reading an article and moving on.
Below is a pyramid showing how much you retain from each learning method.
You retain 90% of what you teach others, compared to 5% from a lecture.

When I switched roles to Qualified, I thought, "What if I did something like what I did at Gusto, but broader? Post online rather than only internally?"
So I gave it a shot. This was my first LinkedIn post, 375 days ago:

That post led to more LinkedIn posts, which led to expanding to a newsletter.
Over a year later, after writing a newsletter each week, I'm still here, constantly learning.
The biggest mistake I've seen, especially as most people work remotely now, is dismissing the importance of being visible.
There's a common claim: "Your work should speak for itself".
I agree. But that's not how it works in reality. And people don't have the time for your work to speak for itself. You need to advocate and make the impact clear to others.
At this point, people think, "Ok, so I need to brag". That's also not true!
Every time you share about your work, it should give value to whoever you are sharing it with. Most of the time, this comes down to teaching and celebrating others. You can do this through sharing documentation, teaching via a presentation, or making a launch announcement and celebrating who helped make it happen.
For a full list of how you can be visible while giving value, check my article on becoming a go-to person.
First: Empathy. A mentor needs to relate to their mentee.
That's why the best mentors are 1-3 years ahead of you, not 10+ years. For example, I'd have difficulty mentoring someone graduating college because things have probably changed a lot.
Empathy allows you to create a strong bond with your mentee and prevents them from feeling judged. Your mentee will feel comfortable talking about anything with you.
Second: Experience.
A good mentor has a wide variety of experiences to draw from and remembers what worked, what didn't, and why. They help their mentees navigate challenges they experienced before and set them up for success.
Each cohort has about 25 engineers in who are on the cusp of Senior Engineer, but feel stuck. I teach the course I wish I had when I was at mid-level trying to get to Senior.
We cover 5 modules:
In each module, I cover the principles behind why the lessons work. Those principles stick with you as you go beyond the senior level.
Feedback-wise, it's been amazing! Across 2 cohorts, I received 33 ratings, averaging to 4.8/5 stars.
This is one of my favorite reviews:
This course was the missing link between my growth as an engineer and being promoted. I wish I had taken this course earlier in my career to have implemented the mindset and practices taught in this course. I believe I would have had more success if I had. This course is great because it doesn't focus on teaching you the answers but rather the right questions to ask yourself so that you can navigate your career – mindsets not prescriptions. Jordan is a relatable, enthusiastic, and kind teacher. He takes the time to understand his students and their questions and will provide you with 1:1 time during office hours or over Slack to make sure you're fully taking advantage of the material.
If you want a preview of the full course, I recently release the "Becoming a go-to person" module on Taro.
Since joining Pinterest, I feel like I'm more behind my peers than ever 😅. My peers get a lot done in a day. They're experienced, strong communicators, and excel technically.
To keep up with them, I create systems for myself. One system that's made a huge difference for me is planning my week and estimating how long I'll spend on each task. I also assign "important" and "urgent" scores to each task to help myself prioritize. This plan sets me up for success because I can start the day working on the biggest priority rather than playing email or Slack whack-a-mole.
At the end of the week, I reflect on how much I accomplished compared to my plan and adjust for the future. I'll also use my reflections as a "brag sheet" for my performance review.
Sometimes, I worry the planning and reflection take too much time.
But here's the math: 2 hours of planning and reflection = 5% of a 40-hour workweek.
So if that plan and reflection makes you at least 5% more efficient, it's worth it. For me, it saves me time by cutting out unimportant work. Plus, I know exactly what I need to work on at any time. It's more than worth it.
As Abraham Lincoln says: "Give me six hours to chop down a tree and I will spend the first four sharpening the axe".
Invest in yourself early in your career. The first 5 years of your career are the most important. After that, the expectations rise. You're expected to know a lot and grow others. You'll never stop growing, but putting the work in for those first 5 years will make the rest smoother when the expectations increase.
To put this into action, I recommend investing in courses, books, newsletters, and platforms like GreatFrontEnd. Structure is key, and these resources provide it.
So. Many. Benefits. I encourage everyone to learn in public and write online.
The top 3 benefits I experience are:
Start with 1 LinkedIn post and go from there. Get better every time and be consistent. Do what is sustainable for you.
Follow Jordan Cutler on LinkedIn, check out his newsletter and Maven course "Mid-level to Senior for high-growth engineers".

If you have used Next.js, you have probably heard of Server Actions as a new way to handle form submissions and data mutations in Next.js applications. Server Actions have both server-side and client-side aspects to them and Actions, the client-side APIs are landing in React 19! React Actions are not specific to Next.js or data fetching – they can be used with other server-side frameworks for any asynchronous operations.
In this post, we will elaborate on what React Actions are and how to use the new hooks like useActionState and useFormStatus to build form submission experiences the modern way.
Note: As of writing, React 19 has not been published and the API can be prone to updates, so you should always refer to the latest version of the documentation.
Before we dive deeper into React Actions, we should first understand the action property in native HTML forms. Before JavaScript was introduced, the common way to send the data to the server was via the action attribute on <form>s.
When we define a <form> element, we can also set an action attribute to a URI which will be used as the endpoint to send the data to the server. The action attribute is often combined with method attribute which can be set to HTTP methods like GET or PUT.
<form action="/user" method="POST"><input name="name" id="name" value="" /><div><button type="submit">Save</button></div></form>
When a user clicks on the "Save" button, the browser will make a HTTP request to the /user endpoint using the specified HTTP method. This is a very powerful pattern that does not rely on JavaScript, however there are downsides of this approach:
Submitting forms in React is straightforward. It can be done by utilizing the onSubmit prop and fetch API. We can show loading and error states through usage of the useState hook and onSubmit prop.
import { useState } from 'react';export default function UserForm() {const [isPending, setIsPending] = useState(false);const [error, setError] = useState(null);const handleSubmit = async (event) => {event.preventDefault();const data = new FormData(event.target);try {setError(false);setIsPending(true);await fetch('/user', {method: 'POST',body: JSON.stringify(data),});event.target.reset();} catch (err) {setError(err.message);} finally {setIsPending(false);}};return (<form onSubmit={handleSubmit}><input id="name" name="name" />{error && <p>{error}</p>}<button type="submit">{isPending ? 'Saving...' : 'Save'}</button></form>);}
Client-side form submissions and updates offer several advantages, particularly in terms of user experience and performance:
While client-side form submissions and updates provide numerous benefits, there are also potential problems and pitfalls that we should be aware of:
event.preventDefault otherwise the browser will do a full page refresh on submission of the form.Enter the era of new React APIs! With the introduction of Actions in React 19, we can harness the power of form actions on the client side as well.
Typically, HTML <form>s support URI strings for values to the action attribute. However, <form>'s in React 19 accept functions as valid values for the action prop! We can even pass in async functions to the action. When a string is passed to the action, the <form> will behave like native HTML forms, however if a function is passed, the form will be enhanced by React.
<form action={actionFunction}>
Let's convert the form above to use React 19's Actions:
import { useState } from 'react';export default function UserForm() {const [isPending, setIsPending] = useState(false);const [error, setError] = useState(null);async function createUserAction(formData) {setError(false);setIsPending(true);try {await fetch('/user', {method: 'POST',body: JSON.stringify({ name: formData.get('name') }),});alert('User has been created successfully');} catch (err) {setError(err.message);} finally {setIsPending(false);}}return (<form action={createUserAction}><input id="name" name="name" />{error && <p>{error}</p>}<button type="submit">{isPending ? 'Saving...' : 'Save'}</button></form>);}
React does a few special things under the hood when we pass a function to action:
event.preventDefault(), React does this automatically if a function is passed to actionFormData as a parameter, so we do not need to construct form data ourselves via new FormData(event.target)<form> will be reset upon action successThat's useful, but do we still have to maintain the pending and error state variables ourselves? Not at all, React also has a solution for them. These issues are addressed by the introduction of new hooks – useActionState and useFormStatus. Let's look at these two new hooks.
useActionState hookThe useActionState hook helps make the common cases easier for Actions. useActionState hook accepts multiple parameters:
actionFn: A function which will be used as action for the form. actionFn accepts two parameters: previousState and formDatainitialState: Value to be used as the initial state. It is ignored after the action is first invokedpermalink (optional): A string containing the unique page URI that this form modifiesThe useActionState hook returns an array containing two values:
formState: A value which will be derived from return value of action function. Defaults to initialStateformAction: Reference to action function which was passed to the <form>'s actionconst [state, formAction] = useActionState(actionFn, initialState);
Let's rewrite our earlier form example using the useActionState hook:
import { useActionState } from 'react';async function createUserAction(prevState, formData) {try {await fetch('/user', {method: 'POST',body: JSON.stringify({ name: formData.get('name') }),});} catch (err) {return {success: false,message: err.message,};}return {success: true,message: 'User created successfully!',};}export default function UserForm() {const [formState, formAction] = useActionState(createUserAction, null);return (<form action={formAction}><input id="name" name="name" />{formState?.success === true && (<p className="success">{formState?.message}</p>)}{formState?.success === false && (<p className="error">{formState?.message}</p>)}<button type="submit">Save</button></form>);}
A little better! We no longer need a state just for the error message, it is now part of the action state. Astute readers will notice that this new example does not handle the pending/loading states. That's where the useFormStatus hook comes in.
useFormStatus hookThe useFormStatus hook provides status information of the last form submission, which can be used by components to render pending states (e.g. loading indicators, disabling buttons and inputs).
It does not accept any parameters and returns a status object with the following properties:
pending: A boolean value that indicates whether the parent <form> is pending submissiondata: A FormData object containing data of the parent <form>. It is null if there is no submission or no parent <form>method: A string value of either get or post. This tells us whether the form is getting submitted using GET or POSTaction: A reference to the action prop on the parent <form>. It is null if there is no parent <form> or if a string URI value provided to the action propconst { pending, data, method, action } = useFormStatus();
Using the useFormStatus hook comes with a caveat – useFormStatus() will only return status information for a parent <form>. It will not return status information for any <form> rendered in that same component or children components. Hence the useFormStatus hook must be called from a component that is rendered inside a <form>.
Let's rewrite our example to use useFormStatus for handling pending states:
import { useActionState } from 'react';import { useFormStatus } from 'react-dom';async function createUserAction(prevState, formData) {try {await fetch('/user', {method: 'POST',body: JSON.stringify({ name: formData.get('name') }),});} catch (err) {return {success: false,message: err.message,};}return {success: true,message: 'User created successfully!',};}// In order for `useFormStatus` to work we have to extract the button// into a separate component so the <form> is now a parent component.function SubmitButton() {const { pending } = useFormStatus();return <button type="submit">{pending ? 'Saving...' : 'Save'}</button>;}export default function UserForm() {const [formState, formAction] = useActionState(createUserAction, null);return (<form action={formAction}><div><input id="name" name="name" /></div>{formState?.success === true && (<p className="success">{formState?.message}</p>)}{formState?.success === false && (<p className="error">{formState?.message}</p>)}<SubmitButton /></form>);}
In order for useFormStatus hook to work, we have to extract the <button> into a separate component so the <form> is now a parent component.
By using the new useActionState and useFormStatus hooks:
pending status. We can utilize the pending field from the return value of useFormStatus. There is no need to do prop drilling or use context for passing the pending state.success and error fields from the action function and be used to display success and error messages.<form> action succeeds, React will automatically reset the form for uncontrolled components. If you need to reset the <form> manually, a new requestFormReset React DOM API is available.action or formAction props of <form>, <input>, and <button> elements, the HTTP method will be POST regardless of the value of the method prop.action prop can be overridden by a formAction prop on a <button> or <input> component as these support the formAction prop.The new React Actions, along with the useActionState and useFormStatus hooks provide apps with a new way to write form submissions in React efficiently and can easily manage the form's pending, success, and error states.
Say goodbye to form boilerplate code!
We also maintain an open-source Top ReactJS Interview Questions GitHub repository - featuring 50 authentic, community-contributed questions that help you master both new React 19 features and timeless React fundamentals.

Other than front end performance techniques, image performance is also crucial in developing a web application, especially if your website contains a lot of them. This article contains multiple ways we can do to make image loading more efficient and thus, reduce the time needed to display.
Content delivery network (CDN) saves a copy of our web content on many servers around the world. This is to allow content to be delivered to users from their nearest server.
Having optimal image formats, such as WebP or AVIF, can reduce sizes while maintaining quality. These formats use advanced compression techniques, making web pages load faster.
<picture> to provide multiple sources in different formats, letting the browser choose the most compatible and efficient one.To have responsive images, where images adjust their own size to the current display size, we can provide multiple sources for the image, allowing the browser to select the most appropriate version.
<img> to list these image sources of different sizes to match various display sizes.Adaptive images involve detecting network speed and serving different image qualities accordingly. Users with slower connections will receive lower-quality images as compared to those with faster connections.
Images that are offscreen, or not in the viewport, are not loaded until they are needed. This means that the images are only loaded when they are about to become visible / nearing the viewport.
With lazy loading, similar to offscreen images, images and iframes are loaded only as they approach the user's viewport. This can significantly reduce initial page load times and save bandwidth.
loading="lazy" to <img> and <iframe>.Progressive JPEGs load in layers, improving in clarity and detail with each layer, so that users see a low-quality version of the image almost immediately, which progressively improves until the image fully loads. This enhances the user experience by providing visual content faster.
Preloading allows us to specify which images should be loaded early, even before the browser encounters the image tags. This is particularly useful for images that are crucial to the user experience but might be discovered late by the browser, such as later sections of the page or carousels.
<link rel="preload"> in <head> to specify these images, setting as="image".Compression reduces file sizes by getting rid of unnecessary data, speeding up loading times. Effective compression balances size and quality, allowing images to load quickly without an obvious loss in quality.

Enhancing image performance requires the combination of various techniques. By applying the above techniques, we can significantly improve the loading speed and efficiency of our web applications. Ultimately, staying updated of the latest trends in image optimization and web standards is essential for remaining competitive in web development.

After months of working hard on this, we are thrilled to finally announce the beta launch of GreatFrontEnd Projects! 🥳️
At GreatFrontEnd, we strive to create a platform for front end engineers to learn, grow and connect with one another.
Through scouring countless forums frequented by front end engineers and speaking to our users, we've discovered a common need for a platform where you can develop well-crafted real-world projects – be it to learn something new through hands-on practice, or to build up your portfolio of projects.
We wanted to build the very best version of such a platform while listening to your needs. We believe we are close to achieving this, and we're excited for you to experience it firsthand.
GreatFrontEnd Projects is a platform for front end engineers to build real-world projects.
In essence, we provide you with tons of real-world project challenges that you can build.
For each challenge, you'll have access to everything you need to start coding right away – including professional designs, API specs, starter code and image files.

Just click "Start project", open up your IDE and start coding!
After you're done, host your project on any service and submit your GitHub repo and site url. This will be published for other users to give you code reviews and feedback.

We will pull your code from GitHub directly onto the platform for instant code reviews by the community, allowing you to receive feedback for your work and gain experience points ("reputation") to track your progress.

By using the platform, you can:
Budget-savvy users will also be happy to know that we are predominantly free, with 80% of our challenges accessible for no cost. We believe in providing the basics for free, including multi-page apps and breakpoint management. You won't be charged for essential features like taking screenshots.
Our premium charges only apply to advanced features that go beyond the basics, offering an extra boost to your learning and development. Find out more →
Whether you're a complete beginner hoping to learn front end from scratch, or a senior engineer hoping to learn more modern stacks, our platform was designed to assist you in your learning goals.
We offer a range of starter level challenges that are specifically designed for beginners. You can begin with these and gradually progress to more advanced challenges as you gain confidence and experience.

As you embark on each challenge, you'll have access to a wealth of resources to support your learning journey. These include detailed guides, solutions, references from fellow users, and community forums. These resources are carefully curated to assist you in understanding and mastering the concepts behind each challenge.
While other platforms might leave you relying solely on community feedback, our premium plan provides you with practical development guides and solutions written by experienced senior engineers from top tech companies. You'll learn best practices and supercharge your learning by referencing professionally-written code derived from years of experience.
Complete beginners will also be happy to find guides to help you get started with the very basics, such as starting up your IDE and code repository, or building UI with figma.

Every challenge on our platform is detailed with the skills that you would be able to learn after building them. Starting from the simplest challenges, you may find yourself learning basic HTML and CSS. As you move along, you start to learn more advanced skills, such as using UI frameworks like React or Svelte.

We also offer a skills roadmap on our platform, which serves as a step-by-step guide to acquire all the fundamental skills required for front-end development, from the very basics to advanced topics.

For each skill, it provides you with a list of curated resources, as well as a recommended order of projects to build.
After completing a project, you'll have the opportunity to receive feedback and code reviews from the community. This feedback is invaluable for your growth as it helps you identify areas for improvement and refine your coding skills.
We make code reviews easy by displaying your code directly on our platform, eliminating the need for community members to go elsewhere to review your work. This convenience encourages more feedback and collaboration, which means you can expect to receive more feedback for your work.

Our platform includes an advanced gamification system that encourages you to track your progress and take on more challenges. Every productive action you undertake towards building projects or learning new skills will be rewarded with reputation points, continuously motivating you to stay engaged and make steady progress in your learning journey.

Our projects were designed with real world project specs meant for professional software engineers. This includes project specs and user stories written by professional product managers, and fully specified UI UX designs by high-end designers.
This means that whatever you build from day 1 will resemble the kind of work you would be expected to do in a full-time front end engineer job. Moreover, you can be sure that every project you spend time building will form a professional application that can be reused for future projects, or used as part of your portfolio. Learn while building something useful!

If you're looking to build your portfolio – we've built our platform to ensure that you are well taken care of.
For developers seeking to build their portfolios or embark on side projects, our platform allows you to build stunning portfolio projects that were professionally designed by high-end designers. Design is hard – and you'd be able to focus solely on the technical execution.

Furthermore, unlike other challenge platforms, you'd be able to easily construct personalized portfolio projects instead of building the same thing as everyone else.
Each project within our platform is made up of reusable components which adhere to the same design system, making them inherently modular and compatible with one another. This means you can seamlessly combine components from various projects to construct unique and customized applications for your portfolio. These components cover a diverse range of applications, including Marketing, E-Commerce, Web Apps, Games, and even Portfolios, which means you'd be able to compose a wide variety of apps from them.

Additionally, we offer Component Tracks, which are collections of projects that form component libraries or design systems. This can leave a strong impression on potential employers and recruiters, showcasing your expertise and versatility in building a variety of components for common use cases, which is much more impressive than building individual projects.


If you enjoy dedicating your spare time to creating side projects, our platform will be beneficial for you as well. Each challenge you complete contributes to a growing collection of professionally designed, reusable components, allowing you to seamlessly integrate them into any of your personal side projects.
We provide all of the essentials for free. You will be able to complete 80% of our challenges and even some advanced features that other platforms make premium, such as multi-page apps, breakpoint management and screenshot taking.
Here are the advanced features you can enjoy as a Premium member:
Each guide and solution was written by big tech senior engineers with best practices derived from years of experience, allowing you to learn techniques and patterns early on in your learning, setting up for a strong foundation.
Learning how to use design tools like Figma is an important skill for any professional front end developer. Moreover, using the design file helps you in building a more precise solution using design details like font sizes, spacing and colors, eliminating the time that would otherwise be spent on guesswork.
Without knowing the domain well, it's hard to know which projects you should build in order to train different aspects of a skill. Our skills roadmap solves that problem by providing a structured roadmap of projects to build to train all the core skills required for front end engineers, all the way from beginner to advanced. While the free plan lets you access only the foundational skills in the skills roadmap, you will get full access to all nodes in the skills roadmap once you purchase any premium plan. This helps you learn skills efficiently without the guesswork.
Our component tracks are a unique feature where each track is a collection of projects that form a component library or even design system. By building entire component tracks, you showcase your abilities and versatility in building a variety of components for common use cases, which is much more impressive than building individual projects. Moreover, one of our component tracks is a design system, which means you get to build the underlying design system behind all of the projects on our platform, serving as a good foundation for your toolkit of reusable components.
Some of our most impressive projects were designed to teach you (and allow you to showcase) complex and / or modern techniques like full stack or artificial intelligence skills. These are the projects you'd want to reference when building your portfolio to stand out from the crowd of applications.
With these premium features, you'll save considerable time and effort towards building accurate designs and becoming a highly skilled front-end developer.
Refer to our pricing plan here for a free vs premium comparison table →
With our Beta launch underway, we're eager to collect insights from our beta testers to enhance our platform further. Should you wish to report a bug or propose new features, feel free to reach out via email at feedback@greatfrontend.com or share your thoughts through our feedback widget ("Chat with us!" on the side of the page).
This was a significant milestone for such a huge project spanning several months. We would like to express our gratitude to the team that helped make this happen, including:

A good website is not only just about aesthetic user interface, optimizing its front end performance is equally as important, and in certain domains like e-commerce, checkout conversion performance is highly dependent on website performance.
This article presents a collection of underutilized yet effective strategies that you can use to improve your website's speed and user experience. These are useful concepts to know for front end system interviews as well as for your day-to-day work!
List virtualization is an optimization technique where only the currently visible items in a long list or large dataset are rendered. This method dynamically loads and unloads elements based on the scroll position.
react-virtualized.Both bundle and code splitting are optimization techniques in web development that involve dividing a large codebase into smaller chunks, loading only the necessary chunks at any point of time. This can significantly enhance the performance and efficiency of applications, especially those with extensive codebases or complex dependencies.
import() for dynamic imports at these points.By implementing dynamic imports, we import code only on interaction, visibility, or navigation. Similar to that, lazy loading is also a popular design pattern that delays the initialization of an object until when it is actually needed by users. These can help to improve efficiency, especially at times where costly resources are not always utilized.
import() calls.Optimal loading sequence prioritizes the loading of essential resources, like CSS, fonts, and above-the-fold content. This method carefully orders the loading process, so critical elements are rendered first, enhancing perceived performance. By doing this, non-essential items are deferred, largely boosting efficiency and user satisfaction, especially during page initialization.
<link rel="preload"> for these resources in the HTML head.Through the prefetching technique, resources are loaded in the background before they are requested by users. This strategy aims to reduce perceived latency and improve responsiveness by fetching resources ahead of time, based on user behavior patterns or predictive algorithms.
<link rel="prefetch"> to instruct the browser to load these in idle time.Preloading is where specific resources are identified and loaded early in the page's life cycle, even before the browser requires them. This ensures that critical assets such as scripts, stylesheets, and images are readily available by the time they're needed.
Unlike prefetching, which depends on future navigation, preloading focuses on the current page, strategically accelerating the availability of high-priority resources that are crucial for the immediate next steps. This is particularly useful for resources that are essential for the initial view or interactive features of a page, ensuring a smoother and faster user experience.
<link rel="preload"> for these resources, specifying the type with as.Compression is a method that reduces file sizes for faster user delivery by eliminating redundant data. This not only quickens load times for web pages and apps but also cuts down on bandwidth and associated costs, similar to how tree shaking, which will be explained below, removes unused code to streamline bundles.
Tree shaking is an optimization technique used to eliminate unused code from the final bundle before deployment. By analyzing the import and export statements in a module structure, static analysis tools can determine which modules and lines of code are not being utilized and remove them.

Optimizing front end performance is important in providing a fast, efficient, and enjoyable user experience. Techniques mentioned above are often powerful yet often underutilized. By carefully implementing these strategies, we can ensure that our applications perform optimally, keeping users engaged and satisfied.

This is a guest post by Jordan Cutler, Senior Frontend Engineer at Pinterest and author of the High Growth Engineer Newsletter.
I've seen a lot of CSS.
Unfortunately, there aren't great resources on doing it right.
In my experience as a Senior Frontend Engineer, I see 5 common mistakes that I'll go over in this article and how to avoid them.
Recognizing and avoiding these mistakes will help you write CSS that:
Let's dive in!
width and height properties incorrectlyOne of the most common mistakes comes at the cost of responsiveness.
It's the overuse of width and height.
Luckily, there are easy fixes to this.
In general:
max-width alongside widthheight for min-heightUsing width and height can be ok in certain scenarios. If you are using them, you should know what you're doing and be cautious.
Some examples where width and height make more sense:
overflow: auto on their parent.HTML is responsive by default. Our CSS is what often breaks the responsiveness. When we add styles, we need to keep our page responsive.
Using width and height is dangerous because they are restrictive.
It says: "You must be this size no matter what".
For example: If we use width: 900px on an element, what happens if we are on mobile?
It would overflow off the screen.

If you do need to use a fixed width value, make it flexible. My preferred way for doing that is adding max-width: 100% .

A common example I see in the real world is defining a fixed width value on <input> elements.
Here's what it looks like when an <input> is 400px inside a container that was shrunk to 250px.

Once we apply max-width: 100% the issue goes away.

Similarly, we run into the same issue with height.
If we define a fixed height: 250px and our content size is greater than 250px, we see this happen:

However, the fix is easy. We can use min-height: 250px instead.
Now, our container will always be at least 250px, but can become larger if it needs to fit the content inside.

CSS is often considered difficult to maintain.
A large reason for this is mixing responsibilities in CSS.
In the same way that you don't want a function to be doing 10 different things, we can apply that principle to our CSS.
How? By dividing each element we apply CSS to into either a "layout element" or a "content element".
Here are examples of content elements:
Essentially, they are isolated items that hold content.
In comparison, "layout elements" define the layout those "content elements" should be placed in.
They define the structure via flexbox, grid, gap, and margin.
Layout elements are all the elements you don't see.
Here's an example:
Let's look at the Tailwind landing page and see how it is structured by adding a border: 1px solid red to everything.

You can already start to see where the "content" elements are vs the "layout elements". The content elements are the buttons, text inputs, paragraph text, icon buttons in the navbar, etc.
Let's focus on the bottom row with the "Get started" button:

The "Get started" button and "Quick search" input are wrapped in a <div> which is the layout element.
It defines display: flex, margin-top, and justify-content: center.
With that, it created the skeleton where content elements can just be plopped into the correct location.
In your CSS, you should aim to use this pattern too.
A common mistake would be applying margin-top on one or both of these buttons, rather than letting the layout element do it.
Question: In the below image, would you use padding, margin, or gap to add space between these tags?

Hopefully, your answer is gap.
Often, I see something like:
<style>.tag {margin-left: 8px;}.tag:first-child {margin-left: 0;}</style><div class="tag">All</div><div class="tag">Music</div><div class="tag">JavaScript</div>...<div class="tag">Sales</div>
While this "works", it breaks separation of responsibilities.
The .tag should just be concerned with rendering itself and its content.
Instead, we can do:
<style>.tagRow {display: flex;gap: 8px;}.tag {// The CSS that controls the tag appearance}</style><div class="tagRow"><div class="tag">All</div><div class="tag">Music</div><div class="tag">JavaScript</div>...<div class="tag">Sales</div></div>
This defines a wrapper around the .tags. Any element inside that tag wrapper will receive a space around them that is 8px wide. The .tag doesn't know anything about and should not bother with the context it's in. It's just a tag being placed in a pre-defined layout.
Think of padding as bubble wrap inside a package. It's part of the package, but not the contents.
Padding is just whitespace that is part of the element itself and prevents the content from being too tightly packed.
Padding gets applied to "content elements".
Think of margin as a restraining order. You have to be a minimum distance away.
I don't like to apply margin to "content elements".
Instead, I create a wrapping element that applies the margin.
For example, let's say we have a button at the bottom of a form like this:
<form><input /><input />...<button type="submit">Submit</button></form>
If we want to add a minimum amount of space above the button and any other content, I'd create a wrapper div around the button to apply that margin.
<form><input /><input />...<div class="submitButtonWrapper"><button type="submit">Submit</button></div></form><style>.submitButtonWrapper {margin-top: 16px;}</style>
Technically, you could add the margin to the submit <button> directly. I don't prefer this though.
In practice, your buttons will usually be abstracted away in a component library that usually prevents style overrides, for good reason.
By creating this wrapper element, the separation of responsibilities is clearer.
One more note for a good use of margin: Typography elements.
Headings and paragraphs are good uses of margin, since there will usually be varying amounts of spacing between these and they usually aren't wrapped in a container where gap makes sense.
Let's say we have 2 <div> elements and some CSS like this:
<style>div {width: 100px;height: 100px;}.blue {z-index: 5000;background-color: blue;}.red {z-index: 4000;background-color: red;// Just to get both divs on top of each othermargin-top: -100px;}</style><div class="blue"></div><div class="red"></div>
Do you know which one will be displayed on top?
…
The answer is… red.
Why?
If you answered blue, you might have thought it was because the z-index of the .blue div was higher.
While that is true, z-index isn't doing anything here.
It's not doing anything because z-index only works in certain "layout modes".
Because it isn't doing anything, the element that appears in the DOM later is rendered at the top. In this case, since the .red div element comes later, it will appear above the other.
The default layout mode for most elements is called "flow".
z-index isn't implemented in the "flow" layout mode.
The MDN docs call this out in the position: static area which is the default for elements.

But it is implemented in "positioned" layout mode.

To enter positioned layout for an element, we can apply position: relative, position: absolute, position: fixed, or position: sticky
In this case, the easiest way to get z-index to work is to use position: relative.
<style>div {width: 100px;height: 100px;}.blue {position: relative;z-index: 5000;background-color: blue;}.red {position: relative;z-index: 4000;background-color: red;// Just to get both divs on top of each othermargin-top: -100px;}</style><div class="blue"></div><div class="red"></div>
Since both elements are in the "positioned" layout mode, z-index will work and the .blue div will be displayed on top.
If you've instinctively used gap only when you add display: flex or display: grid, you probably knew this without realizing it.
Why don't you just add gap without adding display: flex ?
Because gap isn't implemented in the default layout mode: flow.
But it is in the "flex" layout mode! Which you get when you do display: flex.
top, left, right, bottom only works in "positioned" layout mode.grid-template-areas only works in "grid" layout mode.The final common mistake is overusing only display: grid or only display: flex.
Although many display: flex cases can use display: grid and vice-versa, each of these excel in their own areas.
CSS Grid is great for 2-dimensional layouts. I often see good CSS grid use cases when building a page-level layout structure.
For example, Una Kravetz shares how to build the classic holy grail layout using CSS Grid.

.parent {display: grid;grid-template: auto 1fr auto / auto 1fr auto;}header {grid-column: 1 / 4;}.left-side {grid-column: 1 / 2;}main {grid-column: 2 / 3;}.right-side {grid-column: 3 / 4;}footer {grid-column: 1 / 4;}
Flexbox is great for stacking items side by side or on top of each other in 1 dimension.
A great example of this is something like nav items that should be equally spaced apart.
Here is an example on Tailwind's home page.

Here's one more example on Airbnb.

In general, I use Flexbox more than CSS Grid, but they both have their use cases and its important to use the correct one for your use case.
To learn more about Flexbox and CSS Grid on a fundamental level, I recommend Josh Comeau's Interactive Flexbox Guide and his Interactive CSS Grid Guide.

These are the top 5 CSS mistakes I've seen in my experience, but I'm sure there are others we can all learn from.
If you have other mistakes you see the most, feel free to drop me a DM on LinkedIn.
You can also check out my newsletter, High Growth Engineer, where I share how to grow faster as a software engineer to 50k+ engineers weekly.
See you there!

Open source design systems by tech companies provide a wealth of knowledge and insights into best practices, innovative solutions, and collaborative processes between designers and developers. Here are some of our favorite by popular tech companies:

Gestalt by Pinterest is a design system designed to enhance UI consistency and quality. Its set of components and rules are tailored to fit Pinterest's specific look and how it works. It's also great at creating easy-to-use interfaces, paying special attention to making sure everyone can use them easily, no matter their abilities.
By the numbers (accurate as of 26th Feb 2024):

Material Design by Google is a system for design that helps in developing consistent, visually pleasing, and effective UI for various platforms and devices. It includes grid-based layouts, responsive animations, transitions, and many more. All these are made by using a minimalist design approach.
By the numbers (accurate as of 26th Feb 2024):

Next, Blueprint by Palantir is a collection of reusable components, interactive documentation, and accessibility features for the web, built on React. Its emphasis on growing easily and being interactive makes it especially good for big company apps in fields that need to handle a lot of complex information.
By the numbers (accurate as of 26th Feb 2024):

Polaris, created by Shopify, is a design system that assures a consistent user experience on its platform. It has detailed guidelines on various components that follow Shopify's design standards. This allows for it to have easy-to-use and readily available shopping experiences for both sellers and buyers.
By the numbers (accurate as of 26th Feb 2024):

Lightning by Salesforce provides ready-to-use HTML and CSS UI elements that can be used to develop Salesforce experiences. It explains the visual design values and attributes that ensure branding and UI consistency at scale.
By the numbers (accurate as of 26th Feb 2024):

Primer by GitHub contains guidelines, principles, and patterns for designing UI at GitHub, with the aim of ensuring consistency and usability across GitHub's platforms. It includes design guidelines, components, and tools that reflect GitHub's specific needs for collaborative coding environments, which is particularly important to ensure clarity and efficiency in complex interfaces.
By the numbers (accurate as of 26th Feb 2024):

Spectrum, Adobe's design system, is engineered to enhance user experience across its diverse platforms. By focusing on being flexible and creative, it can become really good at being easy to use and looking great, setting a high bar for how user interfaces should look.
By the numbers (accurate as of 26th Feb 2024):

Carbon by IBM is an open source design system for products and digital experiences, based on the IBM Design Language. With its comprehensive set of design guidelines and development components, Carbon is designed to facilitate the creation of intuitive and efficient user interfaces, particularly for complex enterprise solutions requiring scalability and integration.
By the numbers (accurate as of 26th Feb 2024):

Ring UI by JetBrains is a collection of UI components for web-based products built inside JetBrains or third-party plugins for JetBrains’ products. It's designed to help developers build consistent, responsive, and attractive interfaces quickly. Ring UI emphasizes productivity and user experience, reflecting JetBrains' expertise in development tools.
By the numbers (accurate as of 26th Feb 2024):

In addition, Base Web by Uber is a React component library that offers a robust suite of components out of the box. It offers an extreme level of customization through the Overrides API and configurable Themes. Its scalability and wide-ranging component library make it appropriate for various web applications, such as large-scale enterprise systems or basic web projects.
By the numbers (accurate as of 26th Feb 2024):

Atlassian Design System offers direction, elements, and materials for creating interfaces that match Atlassian's products like Jira and Confluence. It highlights teamwork, variation, and a unique visual design to support Atlassian's dedication to team productivity software.
By the numbers (accurate as of 26th Feb 2024):

Backpack by Skyscanner consists of design tools, components, and guidelines that focus on providing a positive user experience on the Skyscanner website. Promoting modularity, scalability, and accessibility helps designers and developers quickly develop cohesive and user-friendly interfaces.
By the numbers (accurate as of 26th Feb 2024):

Fluent by Microsoft is a collection of UX frameworks designed for developing web and mobile applications with the same appearance and function as Microsoft products.It emphasizes on motion and material-inspired interfaces for smooth and intuitive user experiences on Microsoft platforms and devices.
By the numbers (accurate as of 26th Feb 2024):

Finally, Protocol by Mozilla is a design system for Mozilla and Firefox websites that provides a common design language, reusable code components, and high level guidelines for content and accessibility. It prioritizes web standards and open-source principles, making it a versatile choice for web projects aiming for clarity and user-friendliness.
By the numbers (accurate as of 26th Feb 2024):

Design systems from top tech companies that everyone can use are a huge help for both designers and developers. They help make digital interfaces consistent, easy to use, and innovative. Each system has its own special features and ideas, showing how varied and vibrant the tech world is today.

One of the best things about React is the rich ecosystem of libraries and tools that helps developers build apps quickly. Here, we list some enduring and widely favored React ecosystem libraries.

Next.js, a full-stack React framework, is utilized by some of the biggest companies globally to build top-notch web applications. It offers automatic optimizations, data fetching, routing, server functions, API endpoints, and more, solidifying its position as a robust full-stack React framework.
By the numbers (accurate as of 26th Feb 2024):

Remix is a full stack React framework with a focus on web fundamentals and modern web app UX, enabling developers to build better websites with faster and smoother user experiences. It allows data loading, code splitting, and UI transitions to be handled by the URL segments, resulting in snappy page loads and instant transitions. Furthermore, it supports HTML forms, handles both the server and client side, has built-in error handling, and supports route error boundaries. Its abundance of features allow it to stand-out as one of the most impactful React libraries.
By the numbers (accurate as of 26th Feb 2024):

Additionally, React Query is a library that provides declarative, always-up-to-date auto-managed queries and mutations for data fetching in React applications. It handles caching, background updates, stale data, and many more with zero-configuration. It simplifies data fetching logic, improves developer and user experiences, supports complex workflows, and works with any backend.
By the numbers (accurate as of 26th Feb 2024):

Docusaurus is a react-based static website generator. It includes customization, localization, versioning, search, and dark mode features to improve user experience and aid in documentation creation.
By the numbers (accurate as of 26th Feb 2024):

React Hook Form allows for better form state management and validation as it is a library for building forms in React with less code and better performance. It offers a feature-complete API, a constraint-based validation system, a small package size, minimal re-renders, easy adoption, and consistent validation strategies.
By the numbers (accurate as of 26th Feb 2024):

React Router is instrumental in implementing declarative routing within React applications. Its approach simplifies the creation of dynamic, single-page applications, allowing developers to manage navigation and view transitions with ease. The library's popularity and utility in the React ecosystem are evident from its GitHub stars, underscoring its role in shaping modern web application development.
By the numbers (accurate as of 26th Feb 2024):

Redux / React Redux is a predictable state management for JavaScript apps in a consistent, predictable, and testable way. It enables powerful features like undo/redo, state persistence, and more. One of its features, Redux DevTools, can debug and trace state changes, with time-travel and error reporting capabilities. Most importantly, Redux works with any UI layer and has a large ecosystem of addons to fit different needs.
By the numbers (accurate as of 26th Feb 2024):

Meanwhile, Framer Motion is an open source, production-ready animation and gesture library for React on the web. Possessing straightforward syntax, robust capabilities, and optimized performance make it one of the most valuable libraries in the React ecosystem.
By the numbers (accurate as of 26th Feb 2024):

React Testing Library is one of the best choices for testing React components that emphasizes user experience, providing easy-to-use APIs for different frameworks such as Angular, Vue, and React. It also includes extensions for Cypress and React Native, which further displays its versatility and effectiveness.
By the numbers (accurate as of 26th Feb 2024):

React Email consists of high-quality, unstyled components for creating beautiful emails using React. The components are responsive, accessible, customizable, and compatible with most email clients. They also back server-side rendering and CSS inclining, making it a valuable tool in the React ecosystem for creating impactful email campaigns.
By the numbers (accurate as of 26th Feb 2024):
Last but not least, React Use is a large library of React hooks, ported from libreact, that provide various functionalities for React components. This shows how it can be a valuable resource for developers seeking to enhance their applications with custom hooks, as reflected in its GitHub stars.
By the numbers (accurate as of 26th Feb 2024):

The React ecosystem consists of countless libraries that streamline web development and tackle many different challenges. Each of the libraries mentioned have a crucial role in improving the quality and efficiency of web apps development. With strong community support, the React ecosystem is a dynamic and invaluable resource for developers.
Want to better understand how these libraries connect to real React concepts? Explore our Top ReactJS Interview Questions GitHub repo - featuring 50 frequently asked questions that help you apply theory to practice.

UI component libraries improve developer efficiency by enabling consistent design and code reuse. As the top JavaScript frameworks, React offers a wide range of UI component libraries. This article will delve into some of the most popular and robust options available.

MUI, previously known as Material UI, provides React developers with a range of free UI tools including a diverse component library, customizable themes, and production-ready components, all in line with Google's Material Design principles. This makes it an excellent option for creating visually appealing and powerful web applications.
By the numbers (accurate as of 22nd Feb 2024):

Ant Design uses CSS-in-JS technology to provide dynamic and mixed theme ability, and also improves the performance of the application by using component level CSS-in-JS solution. It is commonly used in the development of complex and large scale enterprise applications.
By the numbers (accurate as of 22nd Feb 2024):

Shadcn UI consists of beautifully designed components built using Radix UI and Tailwind CSS, which are accessible, customizable, and open source. They cover a wide variety of use cases such as dashboard, tasks, forms, music, and authentication.
By the numbers (accurate as of 22nd Feb 2024):

Next, Chakra UI is a simple, modular and accessible component library for building React applications. It offers accessible, themeable, composable and light/dark UI components, as well as developer experience and community support, making it an excellent choice for developers who prioritize inclusivity and flexibility in their web applications. Its emphasis on composition and ease of styling allows for rapid development of attractive and accessible web interfaces.
By the numbers (accurate as of 22nd Feb 2024):

Mantine is a React components library that offers more than 100 customizable components and 50 hooks for building accessible web applications faster. It supports visual customizations with props, styles overriding, and flexible theming with colors, fonts, shadows, and more – providing developers with a versatile toolkit for building responsive and visually appealing web applications.
By the numbers (accurate as of 22nd Feb 2024):

Additionally, React Bootstrap is a library that replaces the Bootstrap JavaScript with React components, without unneeded dependencies like jQuery. As one of the original React libraries, React-Bootstrap has grown alongside React, making it a great option.
By the numbers (accurate as of 22nd Feb 2024):

Next UI is a beautiful, fast and modern React UI library that provides a plugin to customize default themes, a fully-typed API, and accessibility support. Its focus on design and user experience, along with a developer-friendly attitude, makes it a top pick for web developers.
By the numbers (accurate as of 22nd Feb 2024):

Semantic UI is the official React integration for Semantic UI, a development framework that helps create beautiful, responsive layouts using human-friendly HTML. Semantic UI React is jQuery free, declarative, and has a rich set of components and subcomponents. It also supports augmentation, shorthand props, and auto controlled state.
By the numbers (accurate as of 22nd Feb 2024):

Last but not least, PrimeReact includes advanced components like Data Tables, Trees, and more – offering a vast collection of widgets to build rich user interfaces. Its emphasis on theme customization and a wide array of components makes it suitable for applications demanding a high level of visual customization and functionality.
By the numbers (accurate as of 22nd Feb 2024):

Untitled UI React is new addition to open-source Tailwind CSS libraries. Launched in July 2025, it's the world's largest collection of open-source React components built with Tailwind CSS v4.1, TypeScript, and React Aria.
It's consistent, professionally designed, and includes pretty much everything you need to design and develop modern, beautiful, responsive interfaces and websites in one neatly organized package. Untitled UI React is based on Untitled UI Figma, the world's most popular Figma UI kit, so design and code are always in sync if your team uses Figma for prototyping.
Installation is easy and this library comes with a custom CLI tool and starter kits for Next.js, Bolt.new, Vite, and more. It's ideal for teams already using Tailwind CSS who want production-ready, modern, accessible React components out of the box that don't rely on external dependencies.
By the numbers (accurate as of 30th Jul 2025):

React UI component libraries in 2024 offer a variety of choices to meet different development needs and design preferences. These libraries cater to visual appeal, performance, and inclusivity. Their popularity, shown by GitHub stars and npm downloads, highlights their importance in the React ecosystem. As web development trends rapidly evolve, these libraries will likely continue evolving and taking an important part in web applications.
To complement your understanding of React UI libraries, visit our Top ReactJS Interview Questions repository - a handpicked collection of 50 React interview questions that cover component design, state management, and real-world implementation patterns.

One of the most effective ways to grow as a developer is by studying real-world projects. In this article, we've curated a list of extensive Next.js projects for you to dive into and dissect. By exploring the architecture and codebase of these large-scale web applications, you'll gain invaluable insights into best practices, project structure, and advanced techniques.
Whether you're a beginner looking to understand the fundamentals or an experienced developer aiming to refine your skills, these projects serve as valuable resources to elevate your proficiency and tackle complex challenges with confidence.

Supabase is an open-source alternative to Firebase that offers a full PostgreSQL database, real-time functionality, simplified authentication, seamless storage integration, and additional features. It empowers developers to build scalable and secure web applications while maintaining compatibility with existing tools and extensions.
Beyond the basics, Supabase offers additional features like embedding vectors (great for geospatial data), real time subscriptions, edge functions (serverless compute), migration guides, project management tools, a command-line interface (CLI), and integrations with other services.
By the numbers (accurate as of 15th Feb 2024):

Cal.com is an open-source scheduling tool that allows users to control their own data, workflow, and appearance. It is a successor of Calendly that is self-hosted or hosted by Cal.com, Inc. and can be deployed on the user's own domain.
What's great about Cal.com is that it integrates with various services such as Google Calendar, Zoom, Daily.co, HubSpot, and more. It also supports customization, white-labeling, and API access. It has a built-in app store where users can add or remove integrations as they wish.
By the numbers (accurate as of 15th Feb 2024):

Infisical is an open-source secret management platform that teams use to centralize their secrets like API keys, database credentials, and configurations. It offers a user-friendly dashboard, client SDKs, CLI, API, native integrations, Kubernetes operator, agent, self-hosting, secret versioning, role-based access controls, secret scanning, and more. The wide range of features offers ample opportunities for learning and experimentation within the Next.js ecosystem.
By the numbers (accurate as of 15th Feb 2024):

Dub.co is an open-source link management tool for modern marketing teams to create, share, and track short links. One most important feature is you can host Dub.co on your own server for more control over your data and design as an opportunity to experiment with Next.js.
By the numbers (accurate as of 15th Feb 2024):

Compared to alternatives, Twenty is great for the fact that it offers full control, freedom, and the ability to contribute, self-host, and fork, breaking away from vendor lock-in and allowing users to shape the open future of CRM. This is while also prioritizing data accessibility and visualization from various sources without retrofitting, and providing an effortlessly intuitive interface inspired by Notion.
By the numbers (accurate as of 15th Feb 2024):

Inbox Zero is an open-source email app that helps users reach inbox zero fast with AI assistance. Its unique feature is that it involves AI to help users manage their email subscriptions, automate their replies, block cold emails, and analyze their inbox.
What's more, the AI lets users instruct the app in plain English to reply, forward, or archive emails based on certain rules. Users can also use planning mode to review the AI's suggestions before applying them.
By the numbers (accurate as of 15th Feb 2024):

Rallly is a web application that allows users to schedule group meetings with friends, colleagues and teams by creating meeting polls based on participants' availability. With the convenience of not having to sign up/login, users only have to simply enter their name and email to join a poll. Hence, this allows Rallly to have multiple advantages over alternatives, including its simplicity, privacy and customisation.
By the numbers (accurate as of 15th Feb 2024):

Formbricks is a free and open source survey platform that allows users to gather feedback from various channels and integrate with other tools. Users can create surveys with a no-code editor, launch and target surveys to specific user groups, invite team members to collaborate, and leverage Formbricks Insight Platform or build their own data analysis capabilities.
By the numbers (accurate as of 15th Feb 2024):

Civitai is an open-source platform where people can share, collaborate, and learn from each other's stable diffusion models, which are techniques to customize AI generations. Users can also leave comments and feedback on each other's models to facilitate collaboration and knowledge sharing.
By the numbers (accurate as of 15th Feb 2024):

Plane is an open source project management tool that helps you track your issues, epics, and product roadmaps in the simplest way possible. As a platform used by over a thousand companies across countries, it offers a minimalist and intuitive interface, a powerful query language, a flexible workflow system, and integrations with popular tools like GitHub, Slack, and Figma.
By the numbers (accurate as of 15th Feb 2024):

Last but not least, Daily.dev is a professional network for developers to learn, collaborate, and grow together. Its feature involves customizing your feed, bookmarking articles, syncing across devices, and joining a community of developers.
Its web app utilizes the brand new incremental static generation feature of Next.js to deliver pages fast, which makes it interesting to explore. This is used to deliver the latest programming news from top tech publications on any topic you want so that you can stay updated on the latest trends, learn new skills, and discover new opportunities in the tech industry.
By the numbers (accurate as of 15th Feb 2024):

This collection of large-scale Next.js projects offers a rich learning environment for those seeking to deepen their understanding and hone their skills. By dissecting and exploring these real-world applications, we can gain valuable insights into advanced techniques, project structures, and best practices. Whether you're looking to master the fundamentals or refine your expertise, immersing yourself in these projects provides a hands-on opportunity to elevate your proficiency and tackle complex challenges with confidence. So, dive in, explore, and embark on a journey of continuous learning and growth within the Next.js ecosystem!

As developers, we often wonder what kind of web page gives good user experience. The problem arises when we try to quantify it. Enter 'Core Web Vitals' – a set of key metrics provided by Google that constitute unified guidance for quality signals. It measures real-world user experience for loading performance, interactivity, and visual stability of the page.
In this short article, we will summarize the most important things you should know about these metrics and provide you with links to learn more should you desire.

The first metric to consider is the Largest Contentful Paint (LCP). LCP is a metric that measures the loading performance of a page. Specifically, it marks the point in the page load timeline from a user's perspective when the main/largest content (such as images or videos) has likely loaded.
LCP is represented in seconds, where anything under 2.5 seconds is great. It is extremely crucial as it represents how quickly users can access the main content of a web page. The faster it is, the better user engagement, satisfaction, and retention. To monitor, platforms such as Google's PageSpeed Insights or Lighthouse can be used.
LCP is affected by several factors: Server response times, network latency, render-blocking resources, and the complexity of the web page's layout. Optimizing these factors can help improve LCP and overall loading performance.
Examples of such optimization techniques include:

First Input Delay (FID) is also an important metric that assesses the responsiveness of a web page. Different from LCP, FID evaluates the interactivity of a page by quantifying the time taken between users' first action (such as clicking a button or tapping on a link) and how long the browser takes to respond to that.
A low FID (under 100 milliseconds) indicates that the page is highly interactive and responsive to user input, contributing to a positive user experience. Similar to LCP, FID is measured using performance monitoring tools like Google's PageSpeed Insights, Lighthouse, or web analytics platforms.
As FID is affected by code execution time, main thread activity and the presence of long tasks that block the browser's responsiveness, the following methods will help in improving it:

Lastly, Cumulative Layout Shift (CLS) measures visual stability by quantifying how much visible content moves around on the screen while loading and the total of all individual layout movement scores (ranging from 0 to 1) that happens while the page remains active. Layout shifts happen when visible elements on a page unexpectedly move to a different position, causing content to reorganize and disrupt user experience. A low CLS (under 0.1) means that the page's layout remains stable and consistent with minimum disruption for users.
CLS is affected by the following factors:
Hence, to reduce CLS, developers can try the following techniques:

Understanding and optimizing Core Web Vitals metrics are essential for developers looking to improve user experience and website performance. Multiple research shows that only about 40% of web pages pass these metrics. By focusing on them, developers can easily quantify the scores and performance of their web pages, giving users a great experience while visiting the website.

Tailwind CSS' "write only atomic classes in your HTML approach" has long been regarded as a controversial and radical approach to styling since its inception. Over the years, the industry has started to see its benefits and Tailwind has since gained immense popularity.
In this article, we explore the diverse range of free UI components and libraries that leverage Tailwind CSS, empowering front end developers to build modern and aesthetically pleasing user interfaces without compromising on efficiency or flexibility.
Whether you're a seasoned developer or just venturing into the realm of Tailwind-powered design, this article is your compass to discover the most valuable and freely available UI component libraries that can elevate your websites to new heights.

Sailboat UI is a modern UI component library specifically designed for Tailwind CSS. It offers over 150 open source components, and uses Alpine.js for some interactive components. However, the important part of the examples is the Tailwind classes used, so you can easily integrate it with your favorite headless UI library in a framework of your choice.
What is great about Sailboat is that it offers a few variants of each component. For example, Accordions are offered in multiple styles like simple, bordered, with background, etc, so you can choose a variant that suits your theme and brand.
By the numbers (accurate as of 18th Jan 2024):

HyperUI is a collection of free copy-pastable Tailwind CSS components that has been around since 2021. It is very similar to Tailwind UI in that both contain components and sections for application, marketing, and e-commerce websites. Since it's pure copy pasting of HTML/CSS, there's nothing to install and you can integrate it with any headless UI library.
By the numbers (accurate as of 18th Jan 2024):

The most outstanding thing about Preline is its vast library of components and sections. At over 60 components and 170 sections, it is the largest free set of Tailwind component examples out there. The examples are all very well-built and look very polished. Last but not least, all the examples have full support for dark mode.
By the numbers (accurate as of 18th Jan 2024):

daisyUI is one of the first Tailwind-based UI component libraries in existence. The library takes a unique approach where it provides a Tailwind plugin that injects its own higher-level classes that are composed of Tailwind utility classes. This somewhat defeats the purpose of Tailwind's atomic classes approach as developers are using pre-built CSS classes, similar to Bootstrap. Nevertheless, with 28.5k stars, it's a wildly popular library that boasts a vast library of over 50 components with multiple variants and classes.
By the numbers (accurate as of 18th Jan 2024):

While many libraries are aimed at more general usage of marketing and e-commerce, Tremor is focused on dashboard and data visualization components. Beyond the standard UI components of accordion, button, forms, Tremor includes multiple visualization components – area chart, bar chart, donut chart, line chart, scatter chart, you name it, they have it.
By the numbers (accurate as of 18th Jan 2024):

Despite the name, NextUI has nothing to do with Next.js, the popular React metaframework. Since v2.0, Next UI is built on top of Tailwind CSS and React Aria and is one of the fastest growing UI libraries based on Tailwind. Like Tremor, to use Next UI, import React components from the npm package @nextui-org/react and use the components within your application.
By the numbers (accurate as of 18th Jan 2024):

If there was one library that took the world by storm in 2023, it'd be shadcn/ui, which is built on top of Tailwind CSS and Radix UI. While many other Tailwind UI libraries make developers copy the HTML/CSS or install the library as an npm dependency, shadcn takes an approach where you copy and paste any required component code into your projects; you have full ownership and control over the code. This approach has both merits and drawbacks. While you have full control over the components, the obvious downside is that you won't receive updates automatically and have to remember to sync any changes. This approach is best used by teams who have strong front end developers who have a need for customizing component appearance as well as the capability to maintain the components in the long term.
By the numbers (accurate as of 18th Jan 2024):

Park UI is a relatively new Tailwind library but is a strong contender to shadcn/ui. Park UI's usage approach is similar to shadcn/ui,
Where shadcn/ui is coupled with Tailwind and Radix UI, Park UI is way more versatile. It has first class support for both Tailwind CSS and Panda CSS (another atomic CSS library that uses a JavaScript-first approach) and also integrates with React, Vue, and Solid via Ark UI, a headless UI library that has first party implementations with React, Vue, and Solid.
By the numbers (accurate as of 18th Jan 2024):

Pines UI is a UI library built on top of Tailwind CSS and Alpine.js and offers multiple variants for each component. With black as its primary color, the default appearance of the components highly resemble shadcn/ui and Park UI.
By the numbers (accurate as of 18th Jan 2024):

Last but not least, we have Aceternity UI. Built by the amazing Manu Arora, Aceternity UI is unlike the typical common UI component libraries introduced above. It is a collection of bespoke and carefully crafted UI sections built on top of Tailwind CSS and Framer Motion.
Some of our favorite examples include the Hero Parallax, 3D Card Effect, and the Wavy Background. It's an extremely fast way to elevate your website's design and add a "wow" factor! We recommend using it along another UI component library of your choice.
By the numbers (accurate as of 18th Jan 2024):

Untitled UI React is a new addition to open source Tailwind CSS libraries. Launched in July 2025, it's the world's largest collection of open-source React components built with Tailwind CSS v4.1, TypeScript, and React Aria.
It's consistent, professionally designed, and includes pretty much everything you need to design and develop modern, beautiful, responsive interfaces and websites in one neatly organized package. Untitled UI React is based on Untitled UI Figma, the world's most popular Figma UI kit, so design and code are always in sync if your team uses Figma for prototyping.
Installation is easy and this library comes with a custom CLI tool and starter kits for Next.js, Bolt.new, Vite, and more. It's ideal for teams already using Tailwind CSS who want production-ready, modern, accessible React components out of the box that don't rely on external dependencies.
By the numbers (accurate as of 30th Jul 2025):


Now that we know how Meta writes JavaScript and CSS, let's see how they're used to build UI components.
Naturally the company that created React will build all their applications using React. You won't find Angular, Vue, or Svelte in Meta's codebase and Meta is all-in on React. By focusing on a single UI framework, tooling, knowledge and expertise can be shared and transferred more easily. Moreover, any issues faced by Meta engineers can be solved in-house, by consulting the React team. Longer term larger issues can be addressed in React's roadmap.
To easily build and test React components, Meta engineers can write example files for these components to demonstrate how the component looks and behaves when using a certain combination of props, similar to stories in Storybook.
All React components in the codebase that have example files are easily searchable within an internal tool and can be interacted with right within the browser. It is super handy for discovering common components and seeing how they are being used.
Unit testing of component interaction and behavior can be done using Jest and React Testing Library. Snapshot tests and screenshot tests can also be conveniently generated from the component examples.
Each product Meta builds has its own design system with a React implementation of the UI components. Many UI components (e.g. buttons, dropdowns, modals, etc) have the same underlying logic, functionality, and accessibility features built in and that layer can be shared between UI components belonging to different design systems. Each design system only has to care about customizing the appearance. This shared logic and accessibility layer is known as headless UI components. Naturally, Meta has built its own headless components to be reused across product design systems.
Companies looking to accelerate their UI components development should pick up one of the popular headless UI component libraries. For React, there's Radix UI, React Aria, and Aria Kit.
Meta also has internal tools to gain insights on how big a component's file size is (bytes) and analytics, e.g. which modules are depending on the component (importing the component) and how many times they are being used in the codebase. This is useful for tracking progress when deprecating components.
In a monorepo, it becomes too easy to import components from anywhere in the codebase. However, this is bad when a component is still under development and not ready to be used widely or when a component is meant to be deprecated. To prevent unwarranted usage of components, lint rules can be used to flag usages of the components outside of directories they are not allowed to be used in.
An emerging technology trend / workflow is “Design to code”, meaning you turn designs from your designer into code that you can use directly within your app. Since Figma is the industry standard for designing digital products these days, a forward-looking styling approach will be one that's integrated with Figma and can support exporting of UI markup and styles from Figma designs. Companies like Builder.io and Locofy offer such solutions.
Over the years, new UI libraries offering different reactive approaches have emerged (e.g. Qwik and Solid). React may no longer be the most performant library, but in terms of the overall ecosystem, learning resources, financial backing, availability of developers proficient in it, React is still one of the safest choices. Moreover, React is still constantly innovating and improving, with projects like React Server Components and an optimizing compiler. However, it is more important to decide on a company-wide blessed framework that the team is comfortable with, than to choose the most trendy UI framework.
For component examples and visual testing, Chromatic (by the maintainers of Storybook) and Percy are the leading choices available in the market.

Writing CSS at scale, particularly in large and complex projects, can present several challenges. Here are some common problems associated with scaling CSS:
CSS uses a global namespace, which causes many problems at scale when many developers are building into the same web application. Since the 2010s, Meta has been writing CSS using an approach called CSS modules, which solves some of the problems with CSS at scale – global namespace, including styles only on pages that use them, dead code elimination (only including the selectors that are still being referenced), and some others. Meta's CSS modules implementation is not open sourced but bundlers like webpack add support for something similar.
In 2014, Christopher Chedeau gave an insightful talk about writing CSS within your JS. In 2018, Meta started their rewrite of facebook.com and announced a new way of writing CSS in JS, which is called StyleX. StyleX's API resembles CSS-in-JS libraries like Aphrodite and Glamor, and has these key features:
The team responsible for rebuilding facebook.com gave a talk about StyleX during React Conf 2019 and also at React Finland. The process of open sourcing StyleX has been a long one and as of Nov 2023, StyleX is finally open sourced. A caveat about StyleX is that it needs extra configuration to work in UI frameworks that use custom file formats like Vue and Svelte.
In the open source ecosystem, Tailwind CSS is the rage. Like StyleX, Tailwind also uses atomic CSS approach so they also share a similar benefit of a CSS stylesheet file that plateaus. However, since Tailwind CSS is just a set of atomic classes, you can use it within both JavaScript and HTML and even server-side Ruby templates. Out-of-the-box, the default Tailwind config provides a set of predefined classes (similar to design tokens) whereas StyleX does not.
Panda CSS is a new atomic CSS-in-JS library by the creators of Chakra UI and it is the middleground of StyleX (library calls are compiled away, type-safe) and Tailwind (inline style declarations, provides some default styling tokens).
The advent of React Server Components means that a whole category of styling libraries that relied on context for theming and runtime injection without any static stylesheet extraction will cease to work. Thankfully Tailwind and Panda CSS generate styles at build time and are fully-compatible with React Server Components.
If you don't mind seeing multiple classes in your HTML or building only with HTML and CSS (or non-JavaScript templating approach), Tailwind CSS will be my recommendation. If you prefer a JavaScript-first approach for styling and type-safety is important to you, use Panda CSS. Panda CSS is similar to StyleX but has a larger community maintaining it.
Just like how developers can use the latest language features of JavaScript by using Babel, developers can also use the latest CSS features by using a CSS preprocessor like PostCSS to automate routine CSS operations, utilize future CSS syntax, and enhance CSS's functionality.
Using Tailwind and Panda removes the need for writing CSS directly, so you might not actually need to use PostCSS in your applications at all. By the way, Tailwind and Panda use PostCSS under-the-hood so you will still be using PostCSS, just not directly.
Because Meta uses StyleX for authoring styles, linting is done in JavaScript, which means ESLint and Flow. StyleX's type-safety features allow components to restrict styling props to be of only certain allowed properties (e.g. margin) and only allowed values (e.g. multiples of 4).
Teams using Tailwind CSS should install the official Tailwind CSS Intellisense plugin which offers autocomplete, linting and previews for a class' underlying styles.
If you're writing plain CSS or CSS modules, stylelint is the recommended linter.
Design tokens are essentially variables used to store design decisions such as colors, fonts, border radii, spacings, animations, and more. These tokens can be simple values like a number, color hex code, or more complex information like a typography scale (font size, line height, letter spacing).
By using design tokens, teams can ensure consistency across their product. As the design system evolves, changes made to the tokens automatically propagate throughout the product, ensuring uniformity and reducing the risk of inconsistencies.
On the web, design tokens can be implemented using CSS variables, or an object in JavaScript. Meta uses CSS variables for color tokens so that dark mode is essentially another set of color tokens. For spacing, border radii and other kinds of numerical values, tokens are not being exposed to developers because most design choices have been abstracted behind UI components built by the product's design system team.
In my opinion, UI components don't capture every design requirement and it's useful to make design tokens available for product engineers to use. Your company's product design team would probably already have selected design tokens and used them in their design system.
If you are using Tailwind or Panda CSS, the default configuration includes tokens for spacing, colors, typography, etc but they can be configured for custom design tokens. If you are not using Tailwind or Panda CSS and don't have a design system but want a similar default set of tokens, Open Props offers a good set of design tokens in the form of CSS variables.
An Atomic stylesheet is the recommended approach for styling on the web because the stylesheet size does not grow proportionately to the number of features/developers/products. Tailwind CSS and Panda CSS are modern libraries to achieve atomic stylesheet generation in a futureproof fashion that's compatible with React Server Components and a server-first component era.

As web development continued to evolve, the demand for more advanced and modern features led to the development of ECMAScript 6 (ES6), also known as ECMAScript 2015. Released in June 2015, ES6 introduced a range of new features, including arrow functions, classes, template literals, and destructuring assignments, among others.
Today, JavaScript is a ubiquitous language used not only for client-side web development but also for server-side development (Node.js) and mobile app development (React Native). The language continues to evolve, with ongoing efforts to enhance performance, introduce new features, and address the needs of modern web development.
After the release of ES2015 / ES6, the ECMAScript specification transitioned to a yearly release cycle, allowing for a more agile development process and quicker incorporation of new features. Subsequent releases, such as ECMAScript 2016 (ES7), ECMAScript 2017 (ES8), and so on, brought additional improvements and features to the language.
Using the latest JavaScript language features in web development is crucial for several reasons. First and foremost, it ensures compatibility with modern browsers and leverages performance improvements introduced in newer language versions. This not only enhances the user experience but also aligns development efforts with the evolving standards of the JavaScript ecosystem. Furthermore, modern features contribute to enhanced productivity, readability, and maintainability of code. Syntax improvements, abstractions, and functionalities like arrow functions, async/await, and class syntax make code more concise, organized, and easier to understand. By staying current, developers gain access to a broader ecosystem of APIs, libraries, and community support, fostering a better development experience and future-proofing their code. Keep your developers happy by enabling the latest technologies and language features.
A combination of JavaScript compilers and polyfills allow developers to use the latest ECMAScript features. JavaScript compilers like Babel transpile newer language syntax into an older version while polyfills like core-js provide implementations for newer JavaScript APIs and they work together so that older browsers that do not support the features can still run the code. Google uses their homegrown Google Closure Compiler while Meta uses a combination of Babel and Flow.
TypeScript, which offers type checking on top of enabling modern language features, is both a compiler and type checker, so you might not need Babel at all. Read on to find out more.
Type-safety is extremely important in large companies and large codebase. Type-safety helps to eliminate an entire class of bugs during development that will not make it into production.
Meta places such a high importance in type-safety that they have developed type-safe versions of / type checkers for every dynamic-typed language they use:
Other big tech companies like Microsoft have developed TypeScript and Google has developed Dart, which further highlights the importance of using type-safe languages when developing at scale. These languages / type checkers are open sourced and available for all to use.
Beyond catching and preventing type errors, type-safe languages are also easier to read and maintain. In VS Code, you can hover over a symbol to know its type. After all, code is read much more than it is written! The benefits of increased readability and clarity outweighs the learning curve in the long term. IDEs can also improve the developer experience by providing better autocompletion suggestions, and showing type errors inline.
Although Flow is open sourced, the Flow team has publicly stated that open source support is not a priority. In the open source ecosystem, there are other alternatives for writing type-safe web applications like ReScript and Reason, which also have their roots in Meta, but they have lost traction in recent years.
There are multiple ways to write and format code, and in a large codebase built by a large team, inconsistent coding styles across different files and developers make the code difficult to read and maintain. This lack of standardization can also hinder collaboration and slow down development, as more time is spent understanding and debugging the code instead of adding new features or improvements.
Linting is the automated process of analyzing source code to detect errors, enforce coding conventions, and identify potential issues. Linters, or linting tools, play a crucial role in improving code quality by catching syntax errors, promoting consistent coding styles, and enforcing best practices. They contribute to error prevention, enhance code readability, and establish a uniform coding style across a project.
Meta uses ESLint with eslint-plugin-flowtype for linting JavaScript. If you're using TypeScript, the recommendation would be ESLint with typescript-eslint. Configuring ESLint rules individually can be a chore, so it is recommended to use popular open source ESLint configs like eslint-config-airbnb as a starting point. Meta's internal ESLint config can be found at eslint-config-fbjs.
It is recommended to add ESLint rules around sorting imports, sorting object keys alphabetically, sorting React component props alphabetically, etc. This reduces the chances of merge conflicts when developers edit the same file. Christoph Nakazawa, ex-manager on Meta's JavaScript infrastructure and React Native team published @nkzw/eslint-config which has a high degree of overlap with Meta's ESLint configuration.
Coding style is another aspect that can be automated and benefits from consistency. Prettier is the de facto choice for formatting. In 2018, Christopher Chedeau ran a format-athon at Meta where he rallied around 20 engineers across the company to format the codebase with Prettier.
Linting should ideally be executed during development so that errors and warnings are reflected within the IDE instantly. The fast feedback loop increases productivity as the developer can fix the issues on-the-spot while the context is still fresh. Autofix lint issues and format the code on save by adding to VS Code's settings.json:
{"editor.formatOnSave": true,"editor.codeActionsOnSave": {"source.fixAll.eslint": true}}
A JavaScript runtime is an environment where JavaScript code is executed. JavaScript was originally designed to be run in the browser, but these days JavaScript can also be run on the server using Node.js, which is the most popular server-side JavaScript runtime built on Google Chrome's V8 engine.
Meta uses Hermes for server-side rendering of React, although Hermes was originally designed to run React Native apps on mobile platforms.
There isn't much of a choice to debate about here. In the wild, most companies use Node.js and it has gotten really good in recent years. Deno and Bun are rising stars in the JavaScript runtime space and both offer TypeScript support out-of-the-box. Deno has a high focus on security by restricting access to sensitive runtime APIs by default, while Bun is extremely fast and includes a package manager, bundler, and test runner.
Deno and Bun are still considered new so the risk is yours to bear if you decide to go with them.
While the JavaScript language is rapidly evolving, in the last decade, there were huge gaps where the language left in terms of providing solutions to common product needs like functional utilities, datetime formatting and manipulation, etc. As a result, a rich ecosystem of utilities and libraries that cater to various development needs have emerged:
Engineering leadership or a front end infrastructure team should make a decision on which libraries to use for various purposes. This prevents different libraries serving the same purpose being shipped in the same app and users end up paying the cost of downloading the JavaScript twice. Resources should also be committed to supporting internal developers facing issues with them and performing periodic codebase-wide upgrading of the library versions being used.
Finally, the last piece of JavaScript development to discuss is package management. Package management in JavaScript refers to the process of managing, distributing, and installing JavaScript libraries and tools within a project. A package manager is a tool that simplifies these tasks by automating the installation, versioning, and dependency resolution of external code packages.
Back in 2016, Meta created Yarn as an improvement over npm, it introduced the concepts of lockfiles, fast installs, global offline caches, workspaces, etc. A key feature of a scalable package manager is locking down the version dependencies and an offline mirror. You don't want to be blocked from deployment if the npm registry goes down. At Meta, node_modules are installed via Yarn v1, scanned for vulnerabilities, and checked into the monorepo.
These days, npm has mostly caught up with Yarn in terms of feature parity and Yarn has also undergone many changes since its initial launch. As of writing, Yarn is now v4, introduces Yarn Plug'n'Play, and is no longer maintained by Meta. Personally, my goto package manager these days is pnpm because it has the most features, has an amazing development experience, and is frequently updated.
TypeScript by Microsoft is now the de facto way to write type-safe web applications. Community support (learning resources, library type declarations) and developer tooling support (linters, IDE integration) for TypeScript is also extremely strong; new JavaScript runtimes like Deno and Bun also support TypeScript out-of-the-box. Even Google uses TypeScript as the primary language for Angular development, a UI framework created by them.
You will benefit from choosing TypeScript, even if the size of your codebase is small.


Meta (previously Facebook) is known for its social media platforms and used by a huge number of users and services. Meta's websites include facebook.com, instagram.com, messenger.com, whatsapp.com, threads.net, meta.com, and more and are used by billions of global users monthly.
Developing these websites consisting of thousands of pages by thousands of engineers is no simple feat. In this article, we will provide some insights of how big companies like Meta and Google handle front end development on such a large scale, the tools, methods, and strategies they use and how growing companies who face the same problems can adopt some of the approaches using alternatives in the open source ecosystem.
Aside from Meta's huge web user base, Meta is also an interesting company to look at because between 2015 to 2020, Meta was the forerunner of modern front end development with the creation of popular open source front end technologies like React, React Native, Flux, Jest, GraphQL, Docusaurus, Relay, Draft.js, Yarn, and more. It's fair to say that Meta had a significant impact on the modern front end ecosystem.
Disclaimer: I am no longer affiliated with Meta and this article does not represent Meta. Opinions and experiences are solely my own. I worked at Meta as a Front End Engineer from 2017 to early 2023 and things might have changed since then.
I am an ex-Meta Staff Engineer who led engineering teams to build meta.com and oculus.com. I was actively involved in the development of Meta's open source projects by creating Docusaurus 2, maintaining (and deprecating) Flux, and making small contributions to Lexical and Relay. Outside of front end development, I have also written multiple technical interview resources like Blind 75, Tech Interview Handbook, and Front End Interview Handbook, which have amassed over 100,000 GitHub stars.
As a company grows, the number of features and number of engineers developing those features increase. Look at your company's front end code base and see:
If there are too many to count, congratulations, you have just identified tech debt. These problems start to occur because:
As a result of the above, teams solve the problems for themselves, because it's much faster to solve their own problem than to solve it for everyone. What can also happen is that someone tries to solve it for everyone, but the solution isn't sufficient and the next person comes along to “improve” the solution for their own use case without understanding the use cases of others and ends up making things worse for everyone, often in subtle unnoticeable ways like performance degradations.
Duplicated code, different approaches to achieving a similar outcome, unusable existing solutions all contribute to tech debt. At scale and over a long period of time, tech debt that is seemingly small can compound and lead to disastrous business consequences like bugs, downtimes, and developer burn out. Thankfully, even small improvements will also lead to large benefits at scale when lots of products and people benefit from it.
Effective front end teams at scale score well on the following metrics:

It is important to know the nature of the organization you are in. Different types of organizations have different focuses and you should adapt your approach to the nature of the company and product. Tech debt and engineering scalability is not as important in certain companies for good reasons.
Be also aware of premature optimizations. Sometimes what you think is a problem might just be a minor inconvenience that is not actually a critical problem to solve right now. Without thorough understanding of the problem, solving a problem prematurely and having to revert it later costs more time overall.
As a Front End Engineer who mostly worked in product teams I'm not all that familiar with deployment infrastructure and the amazing product infrastructure work, so some parts will be glossed over.

A common frustration when preparing for front end interviews is the sheer volume of questions to practice. There are over 400 front end interview questions available on GreatFrontEnd. How do you choose the best questions to practice to get the highest return of investment on your time?
Front end interview questions often revolve around a combination of fundamental JavaScript concepts, problem solving skills, layout building skills, and data structures and algorithms. To maximize your efficiency, we recommend practicing the most commonly-asked questions, along with those that touch multiple skills, so that you get all-rounded practice.
In this list we will cover the most important User Interface questions to practice. Let's get started!
Building a Todo list is a practical exercise that evaluates a front end engineer's ability to work with user interfaces, handle state, and perform basic CRUD (Create, Read, Update, Delete) operations. In the most basic form, candidates are asked to create a simple todo list where users can:
It is a common interview question because it reflects real-world scenarios in front end development, where developers often create user interfaces to add new entries to a list, manipulate the list, and remove entries from the list.
Building a well-implemented Todo list demonstrates proficiency in DOM manipulation, event handling, building accessible forms, event delegation, and state management. This is a question that can also have multiple potential follow-up questions:
The ease of successfully implementing the follow-up questions is highly dependent on the quality of the basic implementation, so it's important to get the basic implementation right.
Practice implementing a Todo List on GreatFrontEnd
Tabs are a common UI pattern in web development, making this question practical and applicable to a front end engineer's daily tasks. Typically, candidates are asked to create a tabs UI component with the following requirements:
For such questions, candidates might be asked to implement without using any external libraries or frameworks. Building a well-implemented Tabs component demonstrates proficiency in DOM manipulation, event handling, accessibility, event delegation, state management, and designing component abstractions. This is a question that can also have multiple potential follow-up questions:
Practice implementing a Tabs component on GreatFrontEnd
A "Tic-tac-toe" front-end interview question typically involves creating a web-based version of the classic Tic-tac-toe game using HTML, CSS, and JavaScript. Typically, candidates are asked to create a tabs UI component with the following requirements:
Implementing Tic-tac-toe assesses a candidate's understanding of DOM manipulation, event handling, building a grid-based layout, implementing non-trivial game logic, data structures and algorithms, and state management. Potential follow-up questions include:
Practice implementing a Tic-tac-toe component on GreatFrontEnd
An "Image Carousel" front end interview question typically involves creating a web-based image carousel or slider. The goal is to implement a user interface that allows users to view a sequence of images, navigate between them. More specifically:
For such questions, candidates might be asked to implement without using any external libraries or frameworks. Implementing an image carousel assesses a candidate's understanding of DOM manipulation, event handling, building a slider layout, CSS transitions, and more. There are many ways to implement an image carousel, each with its own pros and cons. Unsurprisingly, this question has tons of possible follow-up questions:
Image carousel interview question, along with its follow-ups, can even evolve into a front end system design question as there are multiple performance optimizations to talk about, with image optimizations being key. It's a great front end interview question to practice.
Read about how to design an Image Carousel on GreatFrontEnd
An "Autocomplete" front end interview question typically involves creating an interactive autocomplete feature for a text input field. The goal is to implement a user interface that suggests and displays possible matches or completions as the user types into the input field.
Here's a simplified example of an Autocomplete front end interview question has the following requirements:
Implementing an autocomplete assesses a candidate's understanding of DOM manipulation, event handling, asynchronous programming (fetching suggestions from a server), familiarity with CSS positioning, form handling, accessibility, performance, and the list goes on. It is the single most important question you can practice as it literally touches on every possible front end interview topic. Like the image carousel question, there are also many possible follow-up questions:
With the amount of things to talk about, this can also evolve into a front end system design question to have further discussions about what other APIs an autocomplete component can include, performance improvements, etc.
Read about how to design an Autocomplete on GreatFrontEnd

It goes without saying that there are more possible interview questions to practice, but many other interview questions build on top of the skills evaluated for the above questions. If you are short on time, practicing the above questions first will give your the highest return on investment and is highly recommended.
All the best for your interviews!

A common frustration when preparing for front end interviews is the sheer volume of questions to practice. There are over 400 front end interview questions available on GreatFrontEnd. How do you choose the best questions to practice to get the highest return of investment on your time?
Front end interview questions often revolve around a combination of fundamental JavaScript concepts, problem solving skills, layout building skills, and data structures and algorithms. To maximize your efficiency, we recommend practicing the most commonly-asked questions, along with those that touch multiple skills, so that you get all-rounded practice.
In this list we will cover the most important JavaScript questions to practice. Let's go!
Debouncing is a crucial technique used to manage repetitive or frequent events, particularly in the context of user input, such as keyboard typing or resizing a browser window. The primary goal of debouncing is to improve performance and efficiency by reducing the number of times a particular function or event handler is triggered; the handler is only triggered when the input has stopped changing.
import { debounce } from 'lodash';// Example usage for a search input fieldconst searchInput = document.getElementById('search-input');const debouncedSearch = debounce(() => {// Perform the search operation hereconsole.log('Searching for:', searchInput.value);}, 300);searchInput.addEventListener('input', debouncedSearch);
In this example, the debounce function creates a wrapper function that delays the execution of the provided callback until a certain amount of time has passed since the last input event. This helps optimize performance by preventing unnecessary computations or network requests during rapid user input. Adjust the delay parameter based on the specific requirements of your application.
Debounce is a frequently asked question in front end interviews, especially by big tech companies because it assesses the candidate's understanding of asynchronous programming, closures, and the this callback. The basic version of debounce isn't too hard to implement, so you might be asked to implement additional functionality that's available on Lodash's _.debounce:
Or you could also be asked to implement the Throttle function, which is the sister function of Debounce; it's important to understand the differences between them. Throttle is a common follow-up question to Debounce and vice versa.
Practice implementing a Debounce function on GreatFrontEnd
Promise.all()Promise.all() is an important feature in JavaScript for simplifying code needed to handle multiple asynchronous operations concurrently, especially if there are dependencies between them. It takes an array of promises and returns a new promise that resolves to an array of the results when all of the input promises have resolved, or rejects if any of the input promises reject.
Proficiency with Promise.all() is an indicator of a front end engineer's ability to handle complex asynchronous workflows efficiently and manage errors effectively, making it highly relevant to their daily tasks.
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) => {// This callback is only ran when all promises in the input array are resolved.console.log('All responses:', responses);}).catch((error) => {// Handle any errors from any promiseconsole.error('Error:', error);});
In this example, Promise.all() is used to fetch data from three different URLs concurrently, and the .then() block is executed only when all three promises have resolved. If any of the promises rejects, the .catch() block is triggered.
It is a good question to practice for front end interviews because candidates are often evaluated on their mastery of asynchronous programming and whether they know how to implement polyfills. Promise.all() has many sister functions like Promise.race(), Promise.any(), which can also be asked in interviews, so you kill many birds with one stone by practicing Promise.all().
Practice implementing Promise.all() on GreatFrontEnd
In JavaScript, a deep clone function is used to create a new object or array that is entirely independent of the original object/array. This means that not only the top-level structure is cloned, but all nested objects and arrays within the original structure are also duplicated. In other words, changes made to the cloned object do not affect the original, and vice versa.
// Original user.const user = {name: 'John',age: 42,};// Create a deep clone of the user.const clonedUser = deepClone(user);// Modify the cloned user without affecting the original.clonedUser.name = 'Jane';// Output the original and cloned user. The original is not affected.console.log('Original User Data:', user); // { name: 'John', age: 42 }console.log('Cloned User Data:', clonedUser); // { name: Jane', age: 42 }
While not as frequently asked in interviews as some other questions, deep cloning showcases one's understanding of recursion and the various data types in JavaScript – how to recursively traverse an arbitrary object in JavaScript and process each encountered type.
Practice implementing the Deep Clone function on GreatFrontEnd
An Event Emitter class in JavaScript is a mechanism that allows objects to subscribe to and listen for events, and to emit events when certain actions or conditions occur. It facilitates the implementation of the observer pattern, where an object (the event emitter) maintains a list of dependents (observers) that are notified of changes or events. In fact, EventEmitter is part of Node.js' API.
// Example usageconst eventEmitter = new EventEmitter();// Subscribe to an eventeventEmitter.on('customEvent', (data) => {console.log('Event emitted with data:', data);});// Emit the eventeventEmitter.emit('customEvent', { message: 'Hello, world!' });
Implementing an EventEmitter class involves knowledge of object-oriented programming, closures, this callback, and data structures and algorithms fundamentals. Possible follow-up questions include an alternative unsubscribing API.
Practice implementing an Event Emitter on GreatFrontEnd
Array.prototype.filter() is a built-in method in JavaScript that allows you to create a new array containing elements that satisfy a certain condition. It iterates over each element in the array and applies a callback function to determine whether the element should be included in the filtered array.
// Example: Filtering out even numbers from an arrayconst numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];const oddNumbers = numbers.filter(function (number) {return number % 2 !== 0;});console.log(oddNumbers); // Output: [1, 3, 5, 7, 9]
Array.prototype.filter() is commonly-asked questions in front end interviews by big tech companies, along with its sister functions, Array.prototype.map(), Array.prototype.reduce(), and Array.prototype.concat(). Modern front end development favors functional programming style APIs like Array.prototype.filter() and it is also an opportunity for candidates to demonstrate their knowledge of prototypes and polyfills. It seems easy on the surface, but there's much more to it:
this value?Nailing these edge cases is paramount for acing front end interviews.
Practice implementing the Array.prototype.filter() function on GreatFrontEnd
If you're short on time, we recommend practicing these 5 questions, which takes around 3 hours.
What do you think of these questions? Do you think there are other important questions to cover?

Front End Engineering is a nuanced field within Software Engineering and not all companies place equal emphasis on nor have the need for. Choosing the right company is a pivotal decision that can shape the trajectory of a Front End Engineer's career.
A careful evaluation of potential employers to ensure alignment with one's skills, aspirations, and work preferences. From the nature of business to technological stack to growth opportunities, the considerations are diverse and significant. In this post, we highlight key factors and essential questions that Front End Engineers should explore when evaluating companies. By delving into these factors, Front End Engineers can make informed decisions that not only resonate with their expertise but also contribute to a fulfilling and successful career.
Here are some of the factors you should be considering when joining a company as a Front End Engineer:
Let's dive into each factor.
How important is the front end web product to the success of the company? Is web the primary medium in which users consume the product or is web just used for internal dashboards?
If front end web is important to the company, the company will likely want to hire the best front end engineering talent and compensate them well. As a Front End Engineer, it's also important to join such companies because if/when recession strikes, the less important departments will be the first to be let go. Ideally, you'd want to be in the important departments, be it recession or not.
Web engineering is important to companies like Figma where the core product is within the browser or a native desktop app. On the other hand, web is not as important to a ridehailing company where users are using native mobile apps to book rides.
What are the primary revenue streams for the business? What is the breakdown of revenue per platform?
It goes without saying that companies will allocate more resources to revenue-generating platforms.
Other questions to ask:
What technology stack is the company on? Does the company bother to keep up with modern front end technologies or are the company's products still on jQuery and Bootstrap 3 with no intention to migrate?
You don't want to be still working on legacy technologies and not improve your skills at your new job. A stack that includes modern front-end technologies can offer more relevant experience and contribute to your professional growth.
Good companies should strive to keep their technologies updated as it improves the product quality and helps their employees stay relevant with industry needs.
What are scalability and performance challenges the company has faced? Describe the technical challenges faced by the front end teams.
If the technical challenges/bottlenecks are mainly on the back end, then back end is likely more important to the company than front end in the long run. Consequently, there will be more seniority composition of engineers will bias towards the back end and limited growth for front end engineers due to lack of complex front end work available.
What is your approach to front end testing and quality assurance?
If front end products are not tested thoroughly, the company is either small, or the front end products are not as important.
Other questions to ask:
How are engineering teams organized? Are there both front end engineers and back end engineers in the same team or are they in separate teams? How much back end engineering does a front end engineer have to do?
Both ways of team organization can work, but you'd want to avoid a situation where you are the only front end engineer on a large team primarily made up of back end engineers where there are few senior to learn front end from. You might also be a victim of "bait and switch", where after you join a team, there isn't that much front end work to do and you end up spending most of your time doing back end work. That's not what you're hired to do in the first place, some might like it, but most don't.
Is there a front end infrastructure team that takes care of common front end engineering needs across the company such as design system components, linting, build, performance, package upgrades, etc?
If a medium/large-sized company does not have a dedicated front end infrastructure team, there is a good chance that the company does not have too many front end products to warrant the need for a front end infrastructure team. Another reason is that front end engineering quality is not important to the company.
Other questions to ask:
How much does leadership value front end engineering? How important is brand identity and design to the company?
The former can be hard to find out as an outsider but you can roughly tell from the quality and aesthetics of the products. This is an important question to find out because leadership decides where to allocate resources to and the more important departments have a higher priority.
What is the background of the leaders (especially the technical ones)?
Leaders that were more product and design focused will tend to prioritize front end engineering quality.
Other questions to ask:
Is there a dedicated front end interview process or is the process the same as for general software engineers (back end heavy)?
A dedicated front end interview process allows companies to assess candidates more thoroughly and accurately for front end engineering roles. It ensures that the evaluation is aligned with the specific skills and challenges associated with front end development, contributing to effective hiring decisions and the overall success of the engineering team.
If there isn't a dedicated front end interview process, it could be a sign that leadership does not understand front end engineering well and there's no front end engineers in leadership roles. A front end interview process that does not test the right skills can result in non-optimal hires which brings down the company's quality of front end engineering.
Other questions to ask:
Does the company pay front end engineers the same as back end / full stack / generalist software engineers?
If a company pays different specializations of software engineering differently, it's an indication that some specializations are not as important as the rest, aka. Second class citizens. Many companies like Meta and Google pay Front End Engineers as much as Software Engineers. Don't settle for less.
Even if companies pay the same salary for Front End Engineers, the offer salary shouldn't be the only factor you should consider. You should also consider the longer term aspects such as career progression, how feasible it is possible to grow to senior, staff levels and beyond.
How does the career ladder for a front end engineer differ from a back end engineer? How high is the career ceiling for a front end engineer at the company? What level can you attain by focusing on front end development? What is the composition of backgrounds of the high-level engineers at the company?
The career ladder for a front end engineer and a back end engineer can vary between companies, and the specific titles and levels may differ. The career ceiling for a front end engineer can be quite high in companies that have complex client products (e.g. Figma, Google Docs, VS Code, etc) or value the importance of user experience and interface design.
In many other companies, the career ceiling for front end engineers is much lower. For such companies, front end engineers have to look beyond front end work and contribute strategically to the organization's goals in order to rise up the ranks.
Companies which have high career ceilings for Front End Engineers (without them having to go into management) include Google, Meta, Microsoft, Airbnb, and Figma.
As mentioned earlier, look out for where the technical challenges lie as the company grows. Complex front end technical challenges will warrant the need for senior and beyond front end engineers which is good for a career in front end engineering.
How many other front end engineers are there at the company? Who are the other front end engineers at the company? Are they experienced and people I can learn from to level up my front end engineering skills? Are the other front end engineers also passionate about modern front end technologies and frequently share knowledge with each other?
Understanding the experience level of fellow front end engineers is crucial for evaluating potential learning and mentorship opportunities. Working alongside experienced peers facilitates skill development, as you can glean insights from their expertise and real-world projects.
It's also important to know if there's a culture for sharing about modern front end technologies; A team that actively shares knowledge and embraces modern technologies suggests a dynamic and forward-thinking environment.
Other questions to ask:
Does the company have any open source projects? Does the company give back to open source by submitting upstream projects? Does the company have an engineering or design blog and their posts of high quality?
Having a community presence contributes to the positive brand image of the company and demonstrates a commitment to transparency, collaboration, and giving back to the community. An engineering blog fosters a culture of knowledge sharing within the company and contributes to the professional development of team members.
A company actively involved in open source contributions is likely to attract developers who are passionate about open source and community collaboration. It can be a deciding factor for top talent when choosing where to work.
Being able to contribute to open source (be it the company's open source projects or projects used by the company)as part of your work helps to build up personal brand and credibility which is useful to show future employers.
Other questions to ask:
Are the company's products visually appealing? Is there a cohesive brand identity and product interface? Do the products look like they are part of a design system?
While Front End Engineers don't necessarily have to design, they have to work closely with designers and implement what the designers come up with. A design system provides a structured approach to design and development, promoting consistency, efficiency, and collaboration across teams. It is an essential tool for creating and maintaining a unified and user-friendly interface in software products. Building products without a proper design system will lead to inefficiencies and inconsistencies down the road. A lack of a proper design system or design department could also be an indication that design and front end engineering is not valued by the company.
Other questions to ask:
Evaluating a company holistically, from its tech stack to its commitment to innovation and the quality of its team, empowers you to make informed choices that align with your professional aspirations.
By considering the factors outlined in this guide, you pave the way for a rewarding and fulfilling career, where your skills thrive, and your contributions leave a lasting impact on the digital experiences you help create.
You are not just choosing a workplace; you are selecting a path that leads to your own advancement, innovation, and success as a Front End Engineer.

This is part 2 of our top companies series where we gathered the top tech companies for a fulfilling career as a Front End Engineer. Companies are selected based on the following criteria:
This post covers the list of the top medium-sized tech companies for front end engineers. For each company, we give a rating out of 5 on the following areas:
Airbnb revolutionized the travel and hospitality industry with its online marketplace for lodging and experiences. The Airbnb platform is a prime example of good design meets good engineering with its user-friendly and aesthetically pleasing interface that is used globally and supports nearly 100 languages.
Airbnb has made significant contributions to the open source community. Some of their notable open source projects include:
Between 2015 to 2020, Airbnb is one of the most innovative companies in the front end space. Airbnb was one of the earliest companies to implement isomorphic/universal rendering on the web with the invention of Rendr, a library to render Backbone.js apps on the server. They also use server-driven UIs to dynamically build pages for web and mobile. Airbnb's front end team also spoke at React Conf 2019 about the process behind their design system.
Noteworthy front end engineers from Airbnb include Harrison Shoff, known for his work on Airbnb's style guide and visx. Leland Richardson, Jordan Harband, Spike Brehm, Josh Perez, were previously from Airbnb and were instrumental in shaping Airbnb's strong front end engineering culture and establishing the company as a leader in the front end ecosystem.
On the design front, Airbnb's Design department has a beautifully-designed blog and frequently shares their knowledge and resources with the designer community.
Airbnb's front end interview process mostly focused on front end coding with just one round of algorithmic coding. However, Airbnb places a high emphasis on cultural fit as the full loop consists of two rounds of behavioral interviews where most companies only have one.
As of writing, Airbnb has scaled back on their community contributions, possibly due to the global movement towards cost cutting. We no longer see as much front end innovation coming from Airbnb as compared to before. Some of Airbnb's famous libraries like Enzyme and React Dates have been moved out of the Airbnb GitHub organization and don't look maintained. Airbnb's design blog has been paused since 2022 and Airbnb's engineering and design social media accounts have been closed.
It's important to note that at its core, the Airbnb platform is a CRUD application and the challenges lie in scaling the platform for new verticals; the products themselves are not technically sophisticated client applications like a collaborative editor or design tools. However, if you are a product engineer who enjoys translating beautiful designs into reality and bringing them into the hands of people globally, Airbnb is still a cool and great place to work.
Rating: Projects (3/5), Talent (4/5), Design (5/5), Compensation (4/5), Outlook (3.5/5)
A Canadian powerhouse, Shopify empowers businesses with its e-commerce platform, providing business owners with tools to create online stores. As these websites allow a high degree of customization by users while optimizing for performance and SEO, the Shopify platform requires complex, top quality front end engineering.
Shopify is no stranger to open source and although most of their projects are targeting the Ruby ecosystem, there are a few notable front end projects from them:
In recent years, Shopify hired many well-known developers from Google like Ilya Grigorik, Jason Miller (creator of Preact), Jake Archibald, and Surma. Ryan Florence and Michael Jackson of React Router fame ended up at Shopify thanks to the Remix acquisition.
Shopify is an attractive workplace for front end engineers due to its innovative and varied product range, which offers opportunities for diverse and challenging work. The company is pushing the boundaries of e-commerce web development which demands cutting-edge development to enhance user experiences, providing developers a platform to work on impactful and widely-used applications. Shopify's embrace of the latest technologies and commitment to continuous improvement in the field also contribute to a dynamic and growth-oriented work environment. This, combined with a supportive culture and opportunities for professional development, makes Shopify a rewarding place for front end engineers.
Rating: Projects (4/5), Talent (4/5), Design (4.5/5), Compensation (3.5/5), Outlook (4/5)
Figma has become a game-changer in the design software industry, offering a cloud-based interface design tool that enables collaborative work. Figma's apps (Figma and Figjam) are possibly some of the most complex applications to exist as it is extremely challenging to build a performant design tool in the browser that scales for large design files and also offers real-time collaboration.
While Figma's direct contributions to open source are limited, its impact on the design and development community is substantial. Figma has won over the design market and has begun to focus their efforts on improving the design-to-code workflow with the introduction of Dev Mode and Storybook integration, making the product easier to use for developers.
Figma’s focus on user interface and experience design aligns well with the core competencies of front end engineering. Furthermore, the company's innovative approach and commitment to cutting-edge technology provide a dynamic environment for professional growth and learning in the field of front end development. There's a good chance that engineers who work at Figma are passionate about design and creating great user experiences; it's always great to be surrounded by like-minded people.
Rating: Projects (4/5), Talent (4.5/5), Design (5/5), Compensation (4/5), Outlook (4/5)
Stripe provides payment processing solutions for e-commerce, characterized by their user-friendly and secure interfaces. Their front end work mainly involves creating straightforward yet secure checkouts and payment SDKs, dashboards for businesses to manage their account, prebuilt UI elements for developers who want to create their own checkout flows, and developer platform, which is a world-class example of great developer documentation.
Stripe does not have too many contributions to open source but front end engineers at Stripe have been instrumental in advancing UI/UX in financial technology. Some of their projects include Markdoc, a powerful, flexible, Markdown-based authoring framework and Sorbet, a type checker for Ruby.
Stripe is a great company for front end engineers and designers mainly due to its reputation for building elegant, user-friendly products. The company is at the forefront of financial technology, offering developers the chance to work on innovative, high-impact projects that shape the way businesses handle online transactions. Stripe's emphasis on clean, efficient code and cutting-edge technology aligns with the interests of developers who are keen on pushing the boundaries of web development. Additionally, Stripe's collaborative work culture and focus on professional growth make it a great environment for a front end engineering career.
Rating: Projects (3.5/5), Talent (5/5), Design (5/5), Compensation (4.5/5), Outlook (4/5)
Vercel is a cloud platform focused on enhancing the development and deployment experience for front end teams. Vercel's platform is designed to make building and deploying as straightforward as possible, emphasizing ease of use, universality, and accessibility. There's seamless integration with major JavaScript frameworks, like Next.js, Remix, Nuxt, etc so that deploying sites built using these frameworks becomes a breeze. On top of hosting, Vercel also provides logs, A/B testing, storage, analytics, performance tracking, and more.
It's particularly known for creating Next.js, an open source web framework based on React. Next.js is currently the most popular meta framework for building React applications and innovating at breakneck speed especially after members of the React core team left Meta to join Vercel. Besides Next.js, Vercel is also behind projects like SWR, Turborepo, Turbopack, and SWC.
The Vercel team can be said to be the Avengers of the web development ecosystem, a congregation of top front end engineering talents. Guillermo Rauch, CEO and founder of Vercel, is a well-known figure in the development community for his work on Next.js and Socket.io. Jared Palmer, a key figure in the JavaScript community joined Vercel after they acquired his startup Turborepo. Tobias Koppers, the creator of webpack, joined Vercel to work on the evolution of webpack, Turbopack. As mentioned above, some React core team members like Sebastian Markbåge, Andrew Clark, and Dominic Gannaway, joined Vercel to continue working on React and other projects. The list goes on.
Vercel has taken Meta's place of being the industry leader in pushing the boundaries of front end innovation. Besides front end hosting, Vercel is also investing in AI with the creation of the Vercel AI SDK and v0, a tool for generative UI design.
Rating: Projects (4.5/5), Talent (5/5), Design (4.5/5), Compensation (4.5/5), Outlook (4/5)

When you're on the hunt for your next front end engineering role, it's not just about picking any company that'll have you. Companies differ in terms of domains, size, customers, etc. The importance of front end development to a company depends on the company's core offering. Naturally, a company that has a core product that is primarily used on the web will prioritize their front end engineering department, while for companies whose flagship products are mobile apps or hardware, front end development might take a backseat.
Whether it's working on cutting-edge technologies, being surrounded by inspiring colleagues, or contributing to projects you're passionate about, the right company can elevate your career from ordinary to extraordinary. Choosing the right company can make all the difference between leading a fulfilling career and hitting arbitrary career ceilings.
In this post, we have gathered the top tech companies for a fulfilling career as a Front End Engineer. Companies are selected based on the following criteria:
This post covers the list of the top big tech companies for front end engineers. For each company, we give a rating out of 5 on the following areas:
Let's get the obvious company out of the way. Meta (previously Facebook), is a major player in social networking, digital marketing, and has recently ventured into the realm of augmented and virtual reality. The complexity of their front end work is showcased in the Facebook and Instagram platforms, which are known for their visually rich interface and smooth user experience. Unbeknownst to most, the most complex (and possibly most important) web application at Meta is their Ads Manager application as it has to display and manage thousands of rows of ad campaigns and also allow advertisers to create ad creatives in various formats for different platforms. The ads manager and business suite surfaces are worked on by thousands of engineers across the company.
When the Facebook.com redesign was released in 2020, it was considered the gold standard for web applications as it popularized the concepts of SSR, lazy loading, prefetching on navigation, and data-driven dependencies. Today, most new applications at Meta are built using this JavaScript-centric tech stack.
If I were to name a company that has the most impact on the open source front end ecosystem, it has to be Meta. Meta revolutionized how UI was built with the invention of React and React Native, introduced new data fetching approaches with GraphQL, built Jest, a feature-rich test runner that displaced then-frontrunner Mocha, made building documentation websites easy with Docusaurus, and Yarn, a package manager that was a huge improvement over npm at the time of its release. Meta has open sourced technology in almost every aspect of front end development most of which achieved widespread adoption at one point in time, which speaks volumes about the standard of Meta's front end engineers and importance of front end development to the company. Because much of Meta's front end technologies are open source and widely-adopted, new developers who had prior experience with these technologies will have an easier time when onboarding.
Meta is continuing to lead innovation in the front end space with the development of React Server Components, Server actions, React forget, and cross-platform React components so that users can share components between web, mobile, and VR environments.
Notable figures in front end development from Meta includes Jordan Walke, the creator of React and Reason; Joe Savona, who is currently working on the React Forget compiler; Dan Abramov, Sebastian Markbåge, Lauren Tan, Andrew Clark, Dominic Gannaway, Brian Vaughn, Sophie Alpert, Rick Hanlon, who have worked / are still working on React, Christopher Chedeau of Prettier and CSS-in-JS fame; and Christoph Nakazawa, who worked on React Native, Jest, and Metro.
Meta's front end interview process is one that heavily favors Front End Engineers as it is focused on practical front end domain knowledge without excessive emphasis on algorithmic knowledge.
While Meta is a great place for Front End Engineers, it can be stressful working there. The company is known for its fast-paced and results-driven work environment that rewards high performers, which can contribute to tight deadlines and stress. Employees may also face public scrutiny due to the company's high-profile nature and history with privacy concerns and misinformation. The demanding nature of the work and high expectations, coupled with potential challenges to work-life balance, are factors individuals should consider. On the engineering side of things, Meta is stuck using Flow for writing type-safe JavaScript, which is almost no longer used outside of Meta. However, there are some similarities with TypeScript, so the learning curve should not be too huge.
Rating: Projects (4/5), Talent (5/5), Design (4/5), Compensation (4.5/5), Outlook (3/5)
Google, a global technology leader, is best known for its search engine. However, the company's reach extends far into areas such as video streaming, cloud computing, AI, consumer hardware, and software solutions. Some of their products that showcase intricate front end development include YouTube, the world's top video streaming website; Google Maps, with its interactive maps and street views; Gmail, a widely-used email service featuring a sophisticated interface; and Google Docs, an online word processor offering real-time collaboration.
Google's commitment to the front end open source ecosystem is evident in projects like:
Google is home to many prominent figures in the front end community because they regularly create great guides and content for web development like web.dev and YouTube channels like Chrome for Developers. Paul Irish is known for his work on Chrome DevTools and other web development resources; Addy Osmani, Software Engineer on Google Chrome and author of numerous technical books; Minko Gechev, tech lead and manager for Angular.
Google's front end interview process has a good balance of algorithmic knowledge and domain knowledge. There are pure algorithmic rounds and front end coding questions will also require a good grasp of software design and good data structure choice.
Working at Google, while offering numerous benefits, comes with potential downsides. The large organizational structure of the company may pose challenges in communication and decision-making, and frequent reorganizations can create uncertainty for employees. Projects are often canceled and I have lost count of the number of instant messaging applications that were built, killed, and rebuilt. Despite these downsides, Google's cutting-edge projects, great salary, career growth opportunities, and focus on employee well-being continue to attract top talent.
Rating: Projects (4.5/5), Talent (4.5/5), Design (4/5), Compensation (4/5), Outlook (4/5)
Microsoft, a household name in software, offers a range of products from operating systems to cloud services and business solutions. Besides Bing and Edge, the Microsoft 365 suite brings traditional workplace offerings like Word, Excel, and PowerPoint to the web which are complex web applications that require a high level of front end to create.
For a long time, Microsoft was focused on desktop applications but in recent years, Microsoft has increased investment in developing the web ecosystem and have established themselves as one of the most important companies to the future of front end development with the following projects:
Moreover, Microsoft is heavily investing in AI. They're the largest investors in OpenAI and OpenAI utilizes Azure as its primary cloud provider to train and run its large-scale AI models. Microsoft has been integrating OpenAI's technologies into its products, like GitHub Copilot, Microsoft 365 products and Bing search engine, among others. The partnership with OpenAI gives it a competitive edge in the AI race, especially against other tech giants like Google and Amazon. By closely working with and funding one of the most advanced AI research labs, Microsoft positions itself at the forefront of AI innovation.
In a company as large as Microsoft, bureaucratic processes are often in place. This can mean slower decision making, more layers of management, and a need to navigate through a complex corporate structure to get things done. For some, this can feel restrictive and stifle creativity.
Rating: Projects (4/5), Talent (4/5), Design (3.5/5), Compensation (3.5/5), Outlook (4/5)
The only Chinese technology company on the list, ByteDance is most famous for its social media app TikTok, which is a highly addictive platform that prioritizes mobile-first short-form videos. While TikTok content is primarily consumed on mobile platforms, ByteDance has a large suite of enterprise offerings for the web, like Lark and BytePlus.
ByteDance is actually pretty active in the open source community, though their projects are less known compared to those from Western big tech companies. One of ByteDance's most famous open source projects is rspack, a Rust port of webpack. Other notable projects include IconPark, a high quality, highly customizable icon set and Xigua player, a modern video player for the web.
ByteDance is home to many talented individuals like Zack Jackson (Creator of module federation) and Dexter Yang (Author of Spellbook of Modern Webdev). Anthony Fu, one of the most prolific open source leaders in recent years, used to work at ByteDance.
In terms of the hiring process, ByteDance's front end interview process is one of the toughest to crack due to their wide pool of questions that tests the candidates' breadth and depth in the domain. Candidates can expect to answer tough trivia questions about their favorite JavaScript framework and the front end domain. All of algorithms, JavaScript utilities, UI coding, and system design questions can also be asked.
ByteDance is very similar to Meta in many ways and I often tell people they're the Meta of China – their focus on social products, performance-driven culture that disproportionately rewards high performers, mindset which encourages moving fast and taking ownership, open sourcing innovative technologies, etc.
However, being a Chinese company, might have a corporate culture that differs significantly from Western norms. Engineers from different cultural backgrounds might find it challenging to adapt to these differences in work style, communication, and management practices. TikTok is constantly under scrutiny in the United States due to national security concerns related to its Chinese ownership and there's no knowing if or when the app will be banned in the US, which will hurt the business greatly.
Rating: Projects (4/5), Talent (4/5), Design (4/5), Compensation (4/5), Outlook (4/5)
Netflix, the leading entertainment streaming service, is renowned for its personalized user interfaces and seamless streaming experience across devices. Netflix is one of the best places for front end engineers not because of Netflix.com; it is their suite of complex web-based studio technologies that makes working there exciting.
Netflix's primary contribution to the open source community is Falcor, a JavaScript library for efficient data fetching. Notable front end developers from Netflix include Jafar Husain, known for his work on reactive programming and JavaScript frameworks. Jafar has also worked at Meta on GraphQL.
Netflix's company culture, often compared to a professional sports team, is centered around performance, collaboration, and a high degree of freedom and responsibility. This culture is detailed in their "Netflix Culture: Seeking Excellence" document and is widely recognized in the business world for its unique approach. Netflix is known for paying top of the market salaries for Software Engineers and mostly in base salary. Employees at Netflix have the flexibility to decide how much of their compensation is in cash or stock options. On the other hand, the dream team culture can lead to workplace competitiveness and increased stress as employees may feel they must constantly prove their worth to remain with the company.
Rating: Projects (3.5/5), Talent (4/5), Design (4/5), Compensation (5/5), Outlook (4/5)

Originally posted on https://niteshseram.in.
Handling large datasets is a common challenge in frontend applications. As the amount of data grows, it can lead to performance issues, such as slow loading times and unresponsive user interfaces. In this blog, we will explore different methods to effectively handle large datasets in React applications. We will discuss techniques like pagination, infinite scroll, and windowing. By implementing these strategies, we can ensure that our frontend application remains fast and efficient, even when dealing with large amounts of data.
Before we dive into the different methods of handling large datasets, let's first understand the performance problems associated with them. When an application tries to render or manipulate a large amount of data in a list, it can cause significant performance issues. This is because rendering a large number of DOM elements can be time-consuming and resource-intensive.
To illustrate this, let's create a sample React application that renders a list of 10,000 records. By examining the performance of this sample application, we can better understand the challenges of handling large datasets.
To get started, create a new React application using the create-react-app command in your terminal:
npx create-react-app large-dataset-app
Once installed, open the App.js file in the src directory and replace the existing code with the following:
const data = new Array(10000).fill().map((_, index) => ({id: index,name: `Temp Name ${index}`,email: `Temp Email ${index}`,}));function App() {return (<div>{data.map((item) => (<div key={item.id}><h3>{item.name}</h3><p>{item.email}</p></div>))}</div>);}export default App;
In this code, we generate an array of 10,000 objects, where each object represents a record in our dataset. We then use the map function to render each item in the array as a <div> element. Each <div> contains the name and email of the corresponding item.
Now, start the React application by running the following command in your terminal:
npm start
Open your browser and navigate to http://localhost:3000. You will notice that it takes some time for the page to load, and scrolling through the list may also be slow. This is because rendering 10,000 DOM elements at once can cause performance issues.
One way to handle large datasets is by implementing pagination. Pagination allows you to render data in pages, rather than all at once. By controlling the amount of data shown on the page, you can reduce the stress on the DOM tree and improve performance.
There are several UI libraries in React that provide pagination components, such as react-paginate. However, if you prefer not to use a UI library, you can implement pagination manually.
To illustrate this, let's modify our sample application to include pagination. First, install the react-paginate library by running the following command:
npm i react-paginate
Next, open the App.js file and replace the existing code with the following:
import { useState } from 'react';import ReactPaginate from 'react-paginate';const data = new Array(10000).fill().map((_, index) => ({id: index,name: `Temp Name ${index}`,email: `Temp Email ${index}`,}));function App() {const [currentPage, setCurrentPage] = useState(0);const itemsPerPage = 10;const pageCount = Math.ceil(data.length / itemsPerPage);const offset = currentPage * itemsPerPage;const currentData = data.slice(offset, offset + itemsPerPage);const handlePageChange = (selectedPage) => {setCurrentPage(selectedPage.selected);};return (<div>{currentData.map((item) => (<div key={item.id}><h3>{item.name}</h3><p>{item.email}</p></div>))}<ReactPaginatepreviousLabel={'Previous'}nextLabel={'Next'}breakLabel={'...'}pageCount={pageCount}marginPagesDisplayed={2}pageRangeDisplayed={5}onPageChange={handlePageChange}containerClassName={'pagination'}activeClassName={'active'}/></div>);}export default App;
In this code, we use the useState hook to manage the current page state. We calculate the number of pages based on the total number of records and the desired number of items per page. We then use the slice method to get the current data to be displayed on the page.
The ReactPaginate component renders a pagination UI with previous and next buttons, as well as page numbers. The onPageChange event handler updates the current page state when the user clicks on a page number.
Now, when you run the application, you will see that the data is rendered in pages, with only a subset of records shown at a time. This helps to improve the performance of the application by reducing the number of rendered DOM elements.
Another approach to handling large datasets is through the infinite scroll. Infinite scroll involves loading data incrementally as the user scrolls down the page. Initially, only a subset of data is loaded, and more data is appended as the user reaches the end of the list.
There are various ways to implement infinite scroll in React, and one popular library for this purpose is react-infinite-scroll-component. To use this library, install it by running the following command:
npm i react-infinite-scroll-component
Next, open the App.js file and replace the existing code with the following:
import { useState } from 'react';import InfiniteScroll from 'react-infinite-scroll-component';const data = new Array(10000).fill().map((_, index) => ({id: index,name: `Temp Name ${index}`,email: `Temp Email ${index}`,}));function App() {const [items, setItems] = useState(data.slice(0, 20));const fetchMoreData = () => {setTimeout(() => {setItems((prevItems) => [...prevItems,...data.slice(prevItems.length, prevItems.length + 20),]);}, 1500);};return (<InfiniteScrolldataLength={items.length}next={fetchMoreData}hasMore={items.length < data.length}loader={<h4>Loading...</h4>}>{items.map((item) => (<div key={item.id}><h3>{item.name}</h3><p>{item.email}</p></div>))}</InfiniteScroll>);}export default App;
In this code, we use the useState hook to manage the item's state. Initially, we load the first 20 items from our dataset. The fetchMoreData function is called when the user scrolls to the end of the list. It appends the next 20 items to the existing items using the spread operator.
The InfiniteScroll component from react-infinite-scroll-component wraps the list of items. It takes the current length of the items as the dataLength prop, the fetchMoreData function as the next prop, and a boolean value to indicate whether there is more data to be loaded.
When you run the application, you will notice that the data is loaded incrementally as you scroll down the page. This approach improves the user experience by providing a seamless scrolling experience while efficiently loading and rendering the data.
Another technique for handling large datasets is windowing. Windowing involves rendering only the visible portion of a list to the screen, rather than rendering all the items at once. This helps to reduce the number of DOM elements and improves performance.
One popular library for windowing in React is react-window. It provides a set of components for efficiently rendering large lists. To use react-window, install it by running the following command:
npm i react-window
Next, open the App.js file and replace the existing code with the following:
import { FixedSizeList as List } from 'react-window';const data = new Array(10000).fill().map((_, index) => ({id: index,name: `Temp Name ${index}`,email: `Temp Email ${index}`,}));const Row = ({ index, style }) => (<div style={style}><h3>{data[index].name}</h3><p>{data[index].email}</p></div>);function App() {return (<List height={400} itemCount={data.length} itemSize={80} width={300}>{Row}</List>);}export default App;
In this code, we define a Row component that renders each item in the list. The FixedSizeList component from react-window is used to render the list. It takes the height and width of the list, the total number of items, and the size of each item as props.
When you run the application, you will see that only a portion of the list is rendered at a time, based on the height of the list. As you scroll through the list, the windowing technique efficiently renders only the visible items, resulting in improved performance.
You might be wondering what’s the difference between what the react-infinite-scroll-component and react-window do. The difference is that in react-infinite-scroll-component load data incrementally as the user scrolls. It dynamically adds more items to the list as needed, creating an illusion of infinite content. react-window, on the other hand, renders only a subset of the list items that are currently visible in the viewport, reusing DOM elements as the user scrolls.
Due to its simpler API and automatic handling of scrolling, react-infinite-scroll-component may be easier to set up and use for basic infinite scrolling needs. However, it may not perform as well with extremely large data sets or complex list items since it keeps all rendered elements in the DOM. In contrast, react-window's windowing technique ensures that only the visible items are rendered, resulting in improved performance and reduced memory footprint for large lists.
Handling large datasets in frontend applications can be challenging, but there are various techniques available to address this issue. By implementing pagination, infinite scroll, windowing, or using specialized libraries like react-virtualized or react-window, you can effectively manage large amounts of data while maintaining optimal performance.
In this blog, we explored different methods of handling large datasets in React applications. We discussed pagination as a way to render data in pages, infinite scroll for loading data on demand, windowing for efficiently rendering large lists, and libraries like react-window that provide additional features for handling large datasets.
Remember to consider the specific requirements and constraints of your application when choosing a method for handling large datasets. Each approach has its advantages and trade-offs, so it's important to evaluate which technique best suits your use case.
By implementing these strategies, you can ensure that your frontend applications remain fast, responsive, and user-friendly, even when dealing with large amounts of data.