Landing a full-stack developer role that requires React and Node.js means facing a broad range of technical questions — from low-level JavaScript mechanics to system design and deployment strategies. This guide covers 45 interview questions across React, Node.js, and full-stack topics, with concise, practical answers that reflect what interviewers actually want to hear. Whether you are preparing for a mid-level or senior position, these questions represent the real-world knowledge you need to demonstrate on the day.
React: Hooks
1. What is the difference between useState and useReducer, and when should you choose one over the other?
useState is ideal for managing simple, independent pieces of state such as a toggle or a form field value. useReducer is better suited when state logic is complex, involves multiple sub-values, or when the next state depends on the previous one — it brings Redux-like structure into a component without an external library.
2. What is the useEffect hook and how does its dependency array control execution?
useEffect runs side effects after render. With an empty dependency array it runs once after the initial mount, similar to componentDidMount. When dependencies are listed, it re-runs whenever those values change. Omitting the array entirely causes it to run after every render, which is rarely what you want.
3. What is the difference between useMemo and useCallback?
useMemo memoizes the result of a computation and returns a cached value, preventing expensive recalculations on every render. useCallback memoizes the function reference itself, which is useful when passing callbacks to child components that rely on referential equality to avoid unnecessary re-renders.
4. How does useRef differ from useState, and what are its primary use cases?
useRef returns a mutable object whose .current property persists across renders without triggering a re-render when changed. It is commonly used to directly access DOM nodes, store previous state values, or hold mutable values like timer IDs that should not drive the UI.
5. What is the useContext hook and what problem does it solve?
useContext allows a component to subscribe to a React context without wrapping it in a Consumer component. It solves prop-drilling by making shared values — such as a theme, locale, or authenticated user — available to any component in the tree without passing them down through intermediate components.
6. Can you call hooks conditionally inside a component? Why or why not?
No. React relies on the order in which hooks are called to associate state and effects with the correct component instance. Calling hooks inside conditionals, loops, or nested functions breaks this ordering guarantee and leads to bugs or runtime errors. The Rules of Hooks enforce top-level, unconditional calls.
React: Virtual DOM and Component Lifecycle
7. How does the React Virtual DOM work and why does it improve performance?
React maintains a lightweight in-memory representation of the actual DOM called the Virtual DOM. When state or props change, React creates a new Virtual DOM tree, diffs it against the previous one using a reconciliation algorithm, and applies only the minimal set of changes to the real DOM. This batching and diffing avoids costly direct DOM manipulation on every update.
8. What is reconciliation in React?
Reconciliation is the process by which React determines what changed between two renders and updates the DOM accordingly. React uses heuristics — such as comparing element types and using key props — to make this O(n) rather than O(n³). When element types differ, React unmounts the old tree and mounts a new one; when they match, it updates only the changed attributes or children.
9. What are React keys and why are they important in lists?
Keys are unique identifiers that help React identify which items in a list have changed, been added, or removed. Without stable keys, React may re-render or reorder components incorrectly, leading to bugs with component state. Keys should be unique among siblings and ideally stable — avoid using array indices when the list can be reordered.
10. How do class component lifecycle methods map to hooks in functional components?
componentDidMount maps to useEffect with an empty dependency array. componentDidUpdate maps to useEffect with specific dependencies listed. componentWillUnmount maps to the cleanup function returned from useEffect. getDerivedStateFromProps logic is typically replaced with useMemo or direct computation during render.
11. What is React.StrictMode and what does it do in development?
StrictMode is a development-only wrapper that helps identify potential problems by intentionally double-invoking render methods and lifecycle hooks to surface side effects. It also warns about deprecated APIs and unexpected behaviors. It has no impact on the production build.
React: State Management
12. What is the Context API and when is it appropriate to use instead of Redux?
The Context API is built into React and allows state to be shared across a component tree without prop-drilling. It is appropriate for low-frequency updates — such as theme, locale, or current user — that do not change often. Redux is better for complex applications with high-frequency state changes, advanced debugging needs, middleware requirements, or shared state across many disconnected components.
13. Explain the Redux data flow.
In Redux, the UI dispatches an action — a plain object describing what happened. Reducers are pure functions that take the current state and the action and return a new state. The store holds the single source of truth. Connected components subscribe to the store and re-render when the relevant slice of state changes. Middleware like Redux Thunk or Redux Saga intercepts actions for asynchronous logic.
14. What is Redux Toolkit and how does it simplify Redux?
Redux Toolkit is the officially recommended way to write Redux logic. It reduces boilerplate by providing createSlice, which generates action creators and reducers together, and configureStore, which sets up the store with sensible defaults including Redux DevTools and Thunk middleware. It also includes createAsyncThunk for handling async operations cleanly.
15. What are the trade-offs of using the Context API for global state at scale?
Context re-renders every consumer whenever the context value changes, which can cause performance problems if large parts of the component tree consume a frequently-updated context. Splitting contexts by concern, memoizing values with useMemo, and using useReducer inside the provider can mitigate this, but at scale Redux or Zustand often provide better performance and tooling.
React: Performance Optimization
16. How does React.memo work and when should you use it?
React.memo is a higher-order component that memoizes the rendered output of a functional component and skips re-rendering if its props have not changed (by shallow comparison). Use it when a component is expensive to render and its parent re-renders frequently with unchanged props. Avoid overusing it, as the comparison itself has a cost.
17. What is code splitting and how do you implement it in React?
Code splitting breaks the JavaScript bundle into smaller chunks that are loaded on demand rather than all at once. In React, React.lazy and Suspense enable component-level code splitting. Combined with dynamic import() syntax, a component’s bundle is only fetched when it is first rendered, reducing initial load time significantly.
18. What are error boundaries and how do you implement one?
Error boundaries are class components that catch JavaScript errors in their child component tree during rendering, in lifecycle methods, and in constructors, preventing the whole app from crashing. They implement getDerivedStateFromError to update state and componentDidCatch to log the error. Functional components cannot be error boundaries natively, though libraries like react-error-boundary provide a hook-friendly wrapper.
19. What is the difference between controlled and uncontrolled components in React forms?
In a controlled component, form data is managed by React state — the input’s value is set by state and every change calls setState, making React the single source of truth. In an uncontrolled component, the DOM handles the form data and you read values using a ref when needed. Controlled components offer more predictability and easier validation; uncontrolled components are simpler for basic use cases or when integrating with non-React code.
20. How does React Router work, and what is the difference between BrowserRouter and HashRouter?
React Router provides declarative routing by matching the current URL to a component tree. BrowserRouter uses the HTML5 History API, producing clean URLs like /about, and requires server-side configuration to serve the app for all routes. HashRouter uses the URL hash (#/about), which works without server configuration but produces less clean URLs. BrowserRouter is preferred for production applications served from a real server.
Node.js: Event Loop and Async Patterns
21. Explain the Node.js event loop and its phases.
The Node.js event loop is a single-threaded loop that processes callbacks from different queues in a defined order: timers (setTimeout/setInterval callbacks), pending I/O callbacks, idle/prepare, poll (incoming I/O), check (setImmediate callbacks), and close callbacks. Between each phase, Node.js checks the microtask queues (Promise callbacks and process.nextTick), which have higher priority and are drained before moving to the next phase.
22. What is the difference between process.nextTick and setImmediate?
process.nextTick callbacks are executed at the end of the current operation, before the event loop continues to its next phase — they have the highest priority among async callbacks. setImmediate callbacks are executed in the check phase, after the poll phase completes. Use process.nextTick sparingly because a recursive call can starve the event loop; setImmediate is safer for deferring work without blocking I/O.
23. What are the key differences between callbacks, Promises, and async/await?
Callbacks are the original Node.js async pattern but lead to deeply nested “callback hell” and make error handling inconsistent. Promises flatten the chain with .then/.catch and allow easier composition with Promise.all. async/await is syntactic sugar over Promises that makes async code read synchronously, improving readability and enabling standard try/catch error handling. All three ultimately rely on the same event loop mechanics.
24. What does non-blocking I/O mean in the context of Node.js?
Non-blocking I/O means that when Node.js initiates an I/O operation — such as a database query or file read — it does not wait for the result before handling other requests. Instead, it registers a callback and continues processing. When the I/O completes, the callback is queued and executed by the event loop. This allows a single thread to handle many concurrent connections without the overhead of thread-per-request models.
25. How would you handle unhandled Promise rejections in a Node.js application?
You can attach a global listener using process.on(‘unhandledRejection’, (reason, promise) => { … }) to log and handle rejections that were not caught. In modern Node.js versions (15+), unhandled rejections crash the process by default, which is appropriate for production. The best practice is to always attach .catch() to Promises or wrap await calls in try/catch and use a centralized error handler in your Express app.
Node.js: Express and Middleware
26. What is Express middleware and how does the middleware pipeline work?
Express middleware are functions that have access to the request object, response object, and the next function in the pipeline. They execute in the order they are registered using app.use() and can modify req/res, end the request-response cycle, or call next() to pass control to the next middleware. Common uses include authentication, logging, body parsing, CORS headers, and error handling.
27. How do you implement centralized error handling in Express?
In Express, an error-handling middleware is defined with four parameters: (err, req, res, next). It must be registered after all other middleware and routes. When any middleware or route handler calls next(err) or throws in an async handler, Express forwards the error to this centralized handler, where you can log it, format a response, and respond with the appropriate HTTP status code.
28. How do you structure a large Express application?
A scalable Express application typically separates concerns into routers (routes/), controllers (controllers/), service/business logic (services/), data access (repositories/ or models/), and middleware (middleware/). The main app.js wires everything together. This separation makes the codebase testable, maintainable, and allows independent development of each layer.
29. What is JWT authentication and how do you implement it in a Node.js API?
JSON Web Tokens are self-contained tokens signed with a secret or private key that encode a payload such as a user ID and role. On login, the server signs and returns a JWT; the client stores it (typically in memory or an HttpOnly cookie) and sends it in the Authorization header on subsequent requests. A middleware verifies the token’s signature using jsonwebtoken’s verify() function before allowing access to protected routes.
30. What are Node.js streams and when are they useful?
Streams are objects that let you read data from a source or write data to a destination in chunks rather than loading everything into memory at once. They are essential for processing large files, HTTP responses, or database result sets where loading the full dataset would exhaust memory. Node.js provides Readable, Writable, Duplex, and Transform streams, and they implement the EventEmitter interface.
31. What is clustering in Node.js and why is it important?
Node.js runs on a single thread, meaning a standard process uses only one CPU core. The built-in cluster module allows you to fork multiple worker processes that share the same server port, each running on a separate core. This makes full use of multi-core machines, improving throughput and resilience — if a worker crashes, the master can spawn a replacement. PM2 provides a managed clustering solution in production.
Full Stack: REST APIs and HTTP
32. What are the HTTP methods used in REST APIs and what do they represent?
GET retrieves a resource and is idempotent and safe. POST creates a new resource or submits data. PUT replaces a resource entirely. PATCH partially updates a resource. DELETE removes a resource. OPTIONS is used in CORS preflight requests. Proper use of these verbs makes APIs self-describing and allows caching layers and proxies to behave correctly.
33. What is CORS and how do you configure it in an Express API?
Cross-Origin Resource Sharing is a browser security mechanism that restricts HTTP requests from one origin to another unless the server explicitly allows it via response headers. In Express, the cors npm package sets the Access-Control-Allow-Origin header and handles OPTIONS preflight requests. You should configure it to allow only known client origins rather than using the wildcard * in production, especially when credentials are involved.
34. What is the difference between REST and GraphQL?
REST uses multiple fixed endpoints where each URL represents a resource, and the shape of the response is determined by the server. GraphQL exposes a single endpoint where clients specify exactly what data they need in a typed query, eliminating over-fetching and under-fetching. REST is simpler to cache and widely understood; GraphQL suits complex, interconnected data models and clients with varying data needs such as mobile apps.
35. How do you version a REST API?
Common strategies include URL versioning (/api/v1/users), request header versioning (Accept: application/vnd.api+json; version=1), and query parameter versioning (?version=1). URL versioning is the most explicit and easiest to document. Regardless of strategy, maintain backward compatibility within a version and provide a deprecation timeline with clear migration guidance when retiring old versions.
36. What HTTP status codes should a well-designed API return?
200 OK for successful GET/PUT/PATCH. 201 Created for successful POST. 204 No Content for successful DELETE. 400 Bad Request for invalid input. 401 Unauthorized when authentication is missing. 403 Forbidden when the user is authenticated but lacks permission. 404 Not Found for missing resources. 409 Conflict for duplicate resources. 422 Unprocessable Entity for validation errors. 500 Internal Server Error for unhandled server exceptions.
Full Stack: Database Integration
37. What is the difference between MongoDB and PostgreSQL, and how do you choose?
PostgreSQL is a relational database with a fixed schema, strong ACID guarantees, and powerful query capabilities including joins, transactions, and aggregations — ideal for structured data with well-defined relationships. MongoDB is a document database with a flexible schema suited for hierarchical or polymorphic data and rapid iteration. Choose PostgreSQL when data integrity and complex queries matter; MongoDB when flexibility and horizontal scaling of document-like data are priorities.
38. What is an ORM and what are the trade-offs of using one?
An Object-Relational Mapper maps database tables to application objects, allowing you to write queries in your language rather than SQL. Sequelize and Prisma are popular choices for Node.js with PostgreSQL. ORMs speed up development, provide migrations, and reduce SQL injection risk, but they can generate inefficient queries, abstract away important database behavior, and add complexity for highly optimized database-heavy applications.
39. How do you prevent SQL injection in a Node.js application?
Always use parameterized queries or prepared statements rather than concatenating user input into SQL strings. ORMs and query builders like Knex.js handle parameterization automatically. If using raw queries with the pg driver, pass values as the second argument to query() rather than interpolating them. Input validation and allowlisting column/table names also reduce the attack surface.
40. What is database connection pooling and why does it matter?
Creating a new database connection for every request is expensive. Connection pooling maintains a set of open connections that are reused across requests, dramatically reducing latency and load. Libraries like pg-pool for PostgreSQL or Mongoose for MongoDB manage pools automatically. Configure pool size based on the database server’s connection limits and the application’s concurrency requirements.
Full Stack: Deployment, Environment Variables, and Testing
41. How do you manage environment variables in a Node.js application?
Sensitive configuration — database URLs, API keys, JWT secrets — should never be hardcoded or committed to version control. Use a .env file locally with the dotenv package and load it at startup. In production, inject environment variables via the hosting platform (Heroku config vars, AWS Parameter Store, Kubernetes secrets). Always validate required variables on startup and fail fast if any are missing.
42. What is the difference between development, staging, and production environments?
Development runs locally with hot-reloading, verbose logging, and debug tools enabled. Staging mirrors production as closely as possible and is used for final testing before release — it should use production-like data volumes and infrastructure. Production serves real users and prioritizes stability, performance, and security, with error logging forwarded to a service like Sentry and sensitive debug information suppressed.
43. How do you write unit tests for React components with React Testing Library?
React Testing Library encourages testing components the way users interact with them — querying by accessible roles, labels, and text rather than internal implementation details. Wrap renders in a provider if the component needs context or a router. Use userEvent to simulate clicks and typing, then assert against the DOM state with expect queries. Mock API calls with MSW or jest.fn() to isolate the component under test.
44. How do you test an Express route with Jest?
Use the supertest library to make HTTP requests against your Express app in tests without starting a real server. Import the app (not the listening server), pass it to supertest, and chain HTTP method calls with assertions on status codes and response bodies. Mock database calls or service functions with jest.mock() to keep tests fast and isolated from external dependencies.
45. What is the difference between unit tests, integration tests, and end-to-end tests in a full-stack context?
Unit tests verify a single function or component in isolation with all dependencies mocked. Integration tests verify that multiple units work together correctly — for example, an Express route with its controller and database layer, or a React component with its API calls. End-to-end tests drive a real browser through complete user flows using tools like Playwright or Cypress. A healthy test suite has many fast unit tests, a moderate number of integration tests, and a small number of critical end-to-end tests.
Tips for Full Stack Interview Preparation
Technical knowledge is necessary but not sufficient. Interviewers evaluate how you think through problems, communicate trade-offs, and apply concepts to real scenarios. The following practices will put you in a strong position:
Build and deploy a real project. Nothing prepares you better than building a full-stack application end-to-end — React frontend, Node.js/Express API, a real database, authentication, and deployment on a platform like Railway, Render, or AWS. This gives you concrete experience to reference in every answer.
Understand the why, not just the what. For every concept, be able to explain why it exists and what problem it solves. Why does React have the Virtual DOM? Why is the Node.js event loop single-threaded? Interviewers at senior levels care about your mental model, not your ability to recite definitions.
Practice system design for full-stack features. Be ready to design common features from scratch: user authentication with JWTs and refresh tokens, real-time notifications with WebSockets, file uploads with S3, paginated data fetching. Walk through the React component structure, the API contract, the database schema, and any infrastructure considerations.
Know your tools deeply, not broadly. It is better to understand React, Express, and PostgreSQL well than to have surface-level familiarity with a dozen frameworks. Know the internals, the footguns, and the performance characteristics of the tools on your resume.
Review your past code critically. Before interviews, revisit projects you have built and identify what you would do differently today. This demonstrates growth and gives you specific, credible examples of technical decision-making to discuss.