React Developer Interview Questions and Answers
Preparing for a React Developer interview can feel overwhelming, but having concrete examples and frameworks ready transforms nervousness into confidence. This guide walks you through the types of react developer interview questions you’ll encounter, provides realistic sample answers you can adapt, and gives you the strategies to stand out.
Common React Developer Interview Questions
What is the Virtual DOM and why does React use it?
Why they ask: This tests whether you understand one of React’s core performance advantages. It’s foundational knowledge that separates developers who’ve used React from those who truly understand why React works.
Sample answer:
“The Virtual DOM is essentially a lightweight JavaScript representation of the actual DOM. When you make changes to your component’s state or props, React creates a new Virtual DOM tree and compares it to the previous one—a process called ‘diffing.’ React then updates only the parts of the real DOM that actually changed, rather than re-rendering everything.
In my last project, we had a dashboard with dozens of components updating simultaneously. By using the Virtual DOM, we avoided constant full-page re-renders that would’ve caused noticeable lag. Instead, React batched updates and only touched the DOM elements that needed changing. It’s like updating a specific paragraph in a document instead of rewriting the whole page.”
Personalization tip: Mention a specific scenario where you noticed performance improve after understanding this concept, or a bug you debugged thanks to understanding the Virtual DOM.
Explain the difference between state and props
Why they ask: This is fundamental to React architecture. Your answer reveals whether you understand unidirectional data flow and component communication patterns.
Sample answer:
“Props are like function parameters—they’re passed to a component from the outside and are read-only. State is data that lives inside a component and can change over time.
For example, in a UserCard component, the user’s name might come in as a prop from the parent component. But whether the card is expanded or collapsed? That’s local state. If I need to share state between sibling components—like when one button click should update multiple components—I’d lift the state up to their common parent.
I usually think of it this way: props are for configuration coming from above, state is for internal behavior. Mixing them up leads to hard-to-debug bugs and components that can’t be reused properly.”
Personalization tip: Describe a time when you incorrectly used state when you should have used props (or vice versa) and what you learned from it.
What are React Hooks and how do they change how you write components?
Why they ask: Hooks have fundamentally changed React development over the past few years. This shows whether you’re current with modern React practices and understand functional vs. class component paradigms.
Sample answer:
“Hooks let you use state and other React features in functional components, which used to only be possible in class components. The main ones I use regularly are useState for managing local state and useEffect for side effects like data fetching.
Before hooks, if I wanted to fetch data when a component mounted, I’d have to write a class component with componentDidMount. Now I just write a simple functional component with useEffect. It’s cleaner and easier to reuse logic. I can extract custom hooks—like useFetch or useLocalStorage—and share them across components without prop drilling or HOCs.
In a recent project, I created a useDebounce hook to throttle search input. Instead of duplicating that logic across three different search components, I just imported the hook. It reduced code duplication and made testing easier.”
Personalization tip: Share a custom hook you’ve built and why it solved a real problem in your codebase.
How do you handle form inputs and form validation in React?
Why they ask: Forms are everywhere in web applications. This tests both your practical coding experience and your ability to weigh trade-offs between simplicity and complexity.
Sample answer:
“For simple forms—like a login page with three fields—I use controlled components and manage them with useState. I store form values in state and handle validation inline.
But for complex forms with lots of fields, complex validation rules, and conditional fields, I reach for Formik or React Hook Form. These libraries handle the tedium of state management, validation, error messages, and form submission. In my last role, we used React Hook Form on a multi-step onboarding form with 20+ fields and conditional sections. Doing that with raw state would’ve been error-prone and hard to maintain.
I also validate on blur to give users feedback as they fill the form, not just on submit. And I always validate server-side too—client validation is for UX, not security.”
Personalization tip: Describe the most complex form you’ve built and why you chose your particular approach.
What is the component lifecycle and how do lifecycle methods work?
Why they asks: This tests depth of React knowledge and whether you’ve worked with class components. Even though hooks are modern, many codebases still use lifecycle methods.
Sample answer:
“The component lifecycle has three main phases: mounting (when the component is created and inserted into the DOM), updating (when props or state change), and unmounting (when it’s removed).
In class components, lifecycle methods let you run code at specific times. componentDidMount runs after the component first renders—that’s where I’d fetch API data. componentDidUpdate runs after every render, and you can use it to check if certain props changed before making another API call. componentWillUnmount is where you clean up—like unsubscribing from listeners.
In my current role, we use functional components with hooks, so I use useEffect instead. But I maintain an older codebase with class components, and understanding lifecycle methods is essential there. If you don’t clean up properly—say, you set up an event listener in componentDidMount but don’t remove it in componentWillUnmount—you get memory leaks.”
Personalization tip: Share a specific bug you fixed by properly using a lifecycle method or hook.
What is prop drilling and how do you avoid it?
Why they ask: This tests whether you think about code structure and have experienced pain points that real applications face.
Sample answer:
“Prop drilling is when you pass props through multiple levels of components that don’t actually need them, just to get the data to a deeply nested child component. It makes refactoring harder and makes it unclear which components actually depend on what data.
I avoid it using the Context API for global concerns like the authenticated user, theme preference, or language settings. I wrap my app in a context provider, and any component that needs that data can consume it directly without intermediate components passing it down.
For more complex state in larger apps, I’ve used Redux. But I’ve learned that Redux is overkill for small to medium apps—Context API often does the job fine.
One thing I’m careful about: Context isn’t a replacement for props. If a component genuinely needs a value to do its job, props are clearer and make dependencies explicit. Context is for truly global stuff that cuts across the component tree.”
Personalization tip: Describe a refactor where you replaced prop drilling with Context API or another solution, and the specific benefits you saw.
How do you optimize performance in React applications?
Why they ask: As apps scale, performance becomes critical. This reveals whether you think proactively about performance or only when things are slow.
Sample answer:
“I start by measuring. React Developer Tools has a profiler that shows which components are re-rendering and why. If a component is re-rendering unnecessarily, that’s my starting point.
Common optimization strategies I use: React.memo to prevent functional components from re-rendering if their props haven’t changed, useCallback to prevent child components from receiving new function references on every render, and useMemo to cache expensive calculations.
For lists, I always use key props correctly and consider virtual scrolling with libraries like react-window if there are thousands of items.
Code splitting with dynamic imports and lazy loading components improves initial load time. And I always check bundle size with tools like webpack-bundle-analyzer.
In a dashboard project with real-time data updates, heavy optimization was crucial. We went from choppy interactions to 60 FPS by memoizing expensive calculations and splitting large components.”
Personalization tip: Quantify a performance improvement you made—“reduced render time from X to Y” or “improved Lighthouse score from X to Y.”
What are higher-order components (HOCs) and when would you use them?
Why they ask: HOCs represent an older pattern that’s less common now but still appears in legacy code. This shows whether you know different ways to solve problems and understand trade-offs.
Sample answer:
“An HOC is a function that takes a component and returns a new component with added functionality. It’s a pattern for reusing component logic.
For example, I once needed to add analytics tracking to several page components—logging when users landed on them. Instead of duplicating that logic in each component, I wrote a withAnalytics HOC that wrapped each component and injected the tracking code. Clean, reusable, and one place to maintain it.
That said, hooks have largely replaced HOCs for most use cases. A custom hook like useAnalytics is usually simpler and more readable than an HOC. But you’ll still see HOCs in established codebases, so it’s good to understand them. And some libraries like Redux still use them, so knowing the pattern is valuable.”
Personalization tip: Describe a specific HOC you’ve written or encountered and what problem it solved.
Explain the concept of lifting state up
Why they ask: This tests whether you understand how to structure React applications and handle component communication.
Sample answer:
“Lifting state up means moving state to a common ancestor component when multiple components need to share the same state.
Say I have a TodoList component with two children: a FilterButtons component and a TodoItems component. If I want the filter selection to affect which items display, I could keep filter state in FilterButtons and pass it down, but then TodoItems can’t use it. So I lift the state up to TodoList—it holds the filter state and passes it as props to both children. When the filter changes, TodoList updates its state and both children re-render with the new data.
This is a core React pattern. Get it wrong and you end up with prop drilling or trying to coordinate state between separate components. Get it right and data flows predictably.”
Personalization tip: Walk through a specific component structure where you lifted state and explain why it improved the architecture.
What is the Context API and when should you use it?
Why they ask: Context is essential for avoiding prop drilling, but it’s also easy to overuse. This tests whether you understand its appropriate scope.
Sample answer:
“Context is a way to pass data through the component tree without having to pass props down at every level. You create a Context, wrap your component tree with a Provider component, and any descendant can access that data with useContext.
I typically use it for data that’s truly global: the authenticated user, the current theme, the app’s language setting. Things that are relevant everywhere and rarely change.
The key limitation is performance: when Context value changes, all consuming components re-render. So if you’re using Context for frequently-changing data like form input, you’ll get unnecessary re-renders. For that, local state or a state management library is better.
I also avoid creating dozens of contexts. It gets messy fast. Usually I have maybe three to five contexts in an app: user/auth, theme, notifications, and maybe one for app-wide settings.”
Personalization tip: Describe a Context you set up in a project, what data it held, and how it simplified your components.
How do you test React components?
Why they ask: Testing is increasingly important. This shows whether you prioritize code quality and have practical testing experience.
Sample answer:
“I use Jest for unit tests and React Testing Library for component testing. React Testing Library focuses on testing components the way users interact with them, not implementation details. That’s more stable and maintainable than testing internal state.
For example, instead of checking if a useState hook was called with certain values, I test: “Does the button render? Does clicking it show a success message?” That’s what matters to users.
I aim for tests that give me confidence without being brittle. Testing implementation details makes tests break whenever you refactor, even if the component works the same from a user’s perspective.
For integration tests, I use tools like Cypress to test user flows across multiple components. In my last role, I had a test that mimicked a user signing up, creating a project, and inviting teammates—catching issues that unit tests wouldn’t catch.”
Personalization tip: Describe the testing strategy you use and give an example of a bug you caught with tests.
What are the benefits of using functional components over class components?
Why they ask: This tests whether you understand modern React practices and can make architectural decisions.
Sample answer:
“Functional components with hooks are simpler and more readable. They’re just JavaScript functions, not classes with confusing this context. The logic is colocated with the effect—useEffect keeps data fetching close to when it actually happens, rather than spread across multiple lifecycle methods.
Hooks encourage better code reuse. Instead of wrapping components in HOCs or render props, you extract custom hooks. It’s more intuitive.
From a performance standpoint, hooks can be slightly more optimized because the React team has more flexibility in how they’re implemented.
That said, class components aren’t going anywhere. They work fine; they’re just more verbose. If I’m maintaining an older codebase with class components, that’s totally reasonable. But for new code, functional components are the standard.”
Personalization tip: If you’ve converted a class component to functional, describe what you learned from that process.
How do you handle asynchronous operations like API calls in React?
Why they ask: Real applications need to fetch data. This tests whether you understand side effects and common patterns.
Sample answer:
“The main way is using useEffect for side effects. When the component mounts, I fetch data and store it in state. I need to be careful to handle the loading state, error state, and success state separately so the UI shows appropriate feedback.
useEffect(() => {
setLoading(true);
fetch('/api/data')
.then(res => res.json())
.then(data => setData(data))
.catch(err => setError(err))
.finally(() => setLoading(false));
}, []);
I also make sure to clean up if the component unmounts while the request is still pending. If I don’t, I might try to set state on an unmounted component, which causes memory leaks.
For more complex scenarios, I use libraries like React Query or SWR that handle caching, retries, and loading states for me. It saves a lot of boilerplate.”
Personalization tip: Share an async bug you encountered and how you fixed it, like a race condition or memory leak.
What is reconciliation in React?
Why they ask: This tests deep understanding of how React works under the hood.
Sample answer:
“Reconciliation is React’s algorithm for updating the UI efficiently. When state or props change, React re-renders the component and compares the new output to the previous one. It figures out the minimum number of DOM changes needed and applies only those.
React uses a couple of heuristics to make this fast: it assumes elements of the same type produce the same DOM structure, and you should use key props for lists so React knows which items are which.
If you mess up keys—like using the index as a key in a list that can be reordered—React might update the wrong components. I’ve seen bugs where form inputs would have the wrong values after reordering a list because React thought those DOM elements were being reused.
Understanding reconciliation helps you write performant code. It’s why memoization and key props matter.”
Personalization tip: Describe a bug you debugged that was related to keys or reconciliation.
Behavioral Interview Questions for React Developers
Behavioral questions explore how you work in teams, handle challenges, and approach problems. Use the STAR method: Situation, Task, Action, Result.
Tell me about a time you had to debug a difficult issue in a React application
Why they ask: Debugging is a regular part of the job. This shows your problem-solving process and persistence.
STAR framework:
- Situation: Set the scene. What app, what was happening?
- Task: What was your responsibility?
- Action: Walk through your debugging process step by step
- Result: What did you learn? How did you fix it?
Sample answer:
“In my last role, we had a performance issue on our dashboard. Pages were taking 8-10 seconds to load, and users were complaining. I was tasked with finding the bottleneck.
I started by using Chrome DevTools to check the network tab—the API responses looked fine. Then I profiled the React component with React DevTools and found a single component was re-rendering 50+ times on mount.
Digging into the code, I found that a useEffect dependency array was missing some dependencies, so it was running on every render and causing a chain of re-renders. I fixed the dependency array, added useMemo to cache a calculation that was running unnecessarily, and refactored one component to use React.memo.
The result was pages loading in under 2 seconds. It taught me the importance of profiling before optimizing and how subtle mistakes in hook dependencies can cascade.”
Personalization tip: Be specific about the tools you used and quantify the improvement if possible.
Describe a situation where you disagreed with a teammate’s code approach
Why they ask: This assesses your communication skills, humility, and ability to collaborate and resolve conflicts.
STAR framework:
- Situation: What was the disagreement about?
- Task: Were you asked to resolve it, or did you initiate the conversation?
- Action: How did you communicate your perspective respectfully?
- Result: What did you learn? Was there a compromise?
Sample answer:
“A senior developer on my team wanted to implement a complex Redux setup for local form state in a small component. I thought Context API would be simpler for that use case.
Instead of pushing back in code review, I asked to pair on it and explained my thinking: Redux is overkill for isolated component state and adds boilerplate. Context would be cleaner. But I acknowledged that Redux is powerful and if we were sharing that state across many components, I’d agree.
We ended up using Context for that component, but we established a pattern: use Context for local shared state, Redux for global app state. It actually improved consistency across the project. I learned that being respectful and asking questions instead of asserting authority goes a long way.”
Personalization tip: Show that you can disagree professionally and that you’re open to being wrong.
Tell me about a time you had to learn a new technology quickly
Why they ask: React and its ecosystem evolve constantly. This reveals your learning ability and adaptability.
STAR framework:
- Situation: What technology? Why did you need to learn it?
- Task: What was the timeline?
- Action: How did you approach learning? What resources did you use?
- Result: How confident are you now? Any projects where you used it?
Sample answer:
“My team decided to migrate a large app from Redux to Redux Toolkit. The boss wanted the migration done in two weeks.
I started by reading the Redux Toolkit documentation and building a small test app to understand the patterns. I watched a couple of focused tutorials—not eight hours of courses, just enough to understand the core concepts. Then I picked one feature in our app and migrated it myself, made mistakes, debugged them, and learned.
After two days, I felt comfortable enough to help other team members. The migration went smoothly, and we finished on time. More importantly, I learned that I don’t need to be an expert before using something new—I can learn by doing.”
Personalization tip: Emphasize your resourcefulness and that you don’t need everything explained perfectly to get started.
Describe your approach to writing clean, maintainable code
Why they ask: Code quality affects the whole team. This shows whether you think about others who’ll read your code.
Sample answer:
“I think about maintainability first. Self-documenting code beats clever code. Meaningful variable and function names, small functions that do one thing, and components that are easy to understand.
I also stay consistent with the team’s style. If we’re using certain patterns or conventions, I follow them. Consistency matters more than any individual preference.
I write components to be tested—that naturally leads to cleaner code. If something is hard to test, it’s usually too complicated.
And I comment when the why isn’t obvious. Not what the code does—that should be clear from reading it—but why we’re doing it this way. Sometimes there’s a non-obvious reason, and documenting that saves future headaches.
I also do code reviews seriously. It’s not about nitpicking; it’s about shared ownership and catching things before they go to production.”
Personalization tip: Mention specific tools or practices your team uses, like linters or testing libraries.
Tell me about a project you’re proud of and what you learned from it
Why they ask: This reveals what you value, your technical depth, and your ability to reflect.
Sample answer:
“I led the rebuilding of our company’s onboarding flow in React. It was outdated, confusing, and had a high drop-off rate.
I worked with product and design to understand what was breaking. We simplified the flow, added clearer progress indicators, and made it mobile-responsive. On the technical side, I used React Hook Form to manage the multi-step form, dynamic component loading for code splitting, and set up comprehensive tests.
The result: drop-off rate went from 40% to 12%, and onboarding time dropped by 50%. From a technical perspective, I learned that sometimes the best solution is simplification, not adding more features. And I learned how much impact good UX can have on the business.
That project also taught me collaboration. Working with non-technical people and translating their feedback into technical decisions was challenging but valuable.”
Personalization tip: Connect technical skills to business impact. Employers care about results.
Tell me about a time you had to meet a tight deadline
Why they ask: Real work has deadlines. This tests your stress management and prioritization.
Sample answer:
“A critical bug was discovered two days before our product launch affecting payment processing. I was asked to fix it while also helping test other critical features.
I immediately assessed the scope: the bug was in a payment validation component. I isolated the problem, fixed it, and wrote tests to ensure it didn’t regress. I communicated progress to the team every few hours so everyone knew where we stood.
I deprioritized nice-to-haves and focused only on what was blocking launch. I also asked for help—another dev reviewed my code that night to catch any issues I might’ve missed.
We shipped on time. The key was clear communication, ruthless prioritization, and not being too proud to ask for help.”
Personalization tip: Show that you can stay calm under pressure and work effectively in chaos.
Technical Interview Questions for React Developers
These questions require more technical depth and often include coding or design components.
Design a component that fetches and displays a list of users with filtering and pagination
Why they ask: This is a realistic task that combines multiple React skills: data fetching, state management, component composition, and performance.
Answer framework:
-
Start with the requirements: What does “filtering” mean? What’s the pagination size? Should filtering persist when changing pages?
-
Plan your component structure:
- Separate concerns: one component for the list, one for filters, one for pagination
- State management: what goes in local state vs. props?
-
Handle data fetching:
- Fetch on mount with
useEffect - Handle loading, error, and success states
- Consider caching: should we refetch when filters change, or filter locally?
- Fetch on mount with
-
Performance considerations:
- Memoize the list component if it’s large
- Consider virtual scrolling if there are thousands of items
- Debounce filter inputs to avoid excessive API calls
-
Implementation sketch:
function UserList() {
const [users, setUsers] = useState([]);
const [filters, setFilters] = useState({ search: '', role: '' });
const [page, setPage] = useState(1);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
fetchUsers(filters, page);
}, [filters, page]);
const fetchUsers = async (filters, page) => {
setLoading(true);
try {
const res = await fetch(`/api/users?page=${page}&search=${filters.search}`);
const data = await res.json();
setUsers(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
return (
<div>
<Filters onFilterChange={setFilters} />
{loading && <Loader />}
{error && <Error message={error} />}
{users.length > 0 && <UserTable users={users} />}
<Pagination page={page} onPageChange={setPage} />
</div>
);
}
Personalization tip: Discuss trade-offs. When would you use React Query instead? When would you add Redux? Show that you think about scalability.
Explain how you would optimize a component that renders a very long list (1000+ items)
Why they ask: Real apps hit this problem. This reveals whether you know advanced React patterns.
Answer framework:
-
Identify the problem: Rendering 1000 DOM nodes is slow, especially on lower-end devices.
-
Solution: Virtual scrolling
- Only render items visible in the viewport
- As the user scrolls, render new items and remove off-screen ones
- Libraries like
react-windoworreact-virtualizedhandle this
-
Implementation approach:
import { FixedSizeList } from 'react-window';
function LargeList({ items }) {
const Row = ({ index, style }) => (
<div style={style}>{items[index].name}</div>
);
return (
<FixedSizeList
height={600}
itemCount={items.length}
itemSize={35}
>
{Row}
</FixedSizeList>
);
}
-
Additional optimizations:
- Use
React.memoon list item components - Memoize callbacks with
useCallback - Use proper
keyprops - Avoid creating new objects in render
- Use
-
Other considerations:
- Could you paginate instead? 50 items per page is better UX
- Could you lazy-load data as users scroll?
- Search/filter server-side to reduce items rendered
Personalization tip: Mention tools you’ve used and performance metrics you’ve measured (fps, render time).
How would you handle state management for a large, complex application?
Why they ask: This is architectural thinking. The answer shows maturity and understanding of trade-offs.
Answer framework:
-
Start with requirements: How complex? How many features? Team size?
-
Tier the state:
- Local component state: Form inputs, UI state (expanded/collapsed), anything private to one component
- Shared component state: Lifted state for related components, use props or Context
- Global state: User auth, theme, notifications, things needed everywhere
-
Choose your tool:
- Small/medium apps: Local state + props + Context API (often enough)
- Large apps with complex state: Redux, Zustand, or Jotai
- Apps with lots of async data: React Query or SWR (separate from app state)
-
Red flags indicating you need more structure:
- Passing props through 5+ levels
- Multiple components managing the same state
- Difficulty tracking where state changes come from
- Time-travel debugging would be helpful
-
Example decision: “For a large project with 50+ pages and complex workflows, I’d use Redux for app state (user, permissions, settings) and React Query for server data (users, posts, comments). Local state stays in components. This separates concerns: Redux handles what the app knows, React Query handles what the server knows.”
Personalization tip: Reference a project where you made this decision and explain why you chose that approach.
What would you do if a component is re-rendering too frequently?
Why they ask: Debugging performance issues is a real skill. This shows your methodology.
Answer framework:
-
Measure first:
- Use React DevTools Profiler to see what’s re-rendering and why
- Check Chrome DevTools performance tab for actual impact
- Don’t optimize by gut feel
-
Diagnose the root cause:
- Is state changing unnecessarily?
- Are props changing when they shouldn’t?
- Is an external value changing (like an object created in render)?
- Are dependencies wrong in hooks?
-
Fix approach (in order of preference):
- Fix the root cause: Why is state changing? Can you prevent it?
- Memoize:
React.memo,useMemo,useCallback - Restructure: Move state to a different component, split components
-
Common culprits:
// BAD: new object created every render
<Child config={{ color: 'blue' }} />
// GOOD: memoize it
const config = useMemo(() => ({ color: 'blue' }), []);
// BAD: context value changes every render
<MyContext.Provider value={{ user }}>
// GOOD: memoize the value
const value = useMemo(() => ({ user }), [user]);
- When to stop: Don’t over-optimize. If the app feels responsive, it’s fine. Premature optimization is the enemy.
Personalization tip: Walk through a specific example from your experience, showing exact metrics.
Design an error boundary component
Why they ask: Error boundaries are important for production reliability and show understanding of React’s error handling.
Answer framework:
-
Explain what it does: Catches JavaScript errors in child components and displays a fallback UI instead of crashing the whole app.
-
Implementation:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
// Log to error tracking service
console.log('Error caught:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return (
<div>
<h1>Something went wrong</h1>
<p>{this.state.error?.message}</p>
<button onClick={() => this.setState({ hasError: false })}>
Try again
</button>
</div>
);
}
return this.props.children;
}
}
-
Where to use:
- Wrap major sections (pages, features)
- Don’t wrap every component or you lose error visibility
- Multiple error boundaries at different levels
-
Limitations:
- Only catches errors in render, lifecycle, and constructors
- Doesn’t catch event handlers (use try-catch there)
- Doesn’t catch async code (use .catch() or try-catch in async functions)
-
Integration:
- Send errors to Sentry or similar service
- Log enough context to debug
- Show user-friendly messages, log technical details
Personalization tip: Discuss your error tracking strategy and how you balance user experience with debugging information.
How would you implement a complex form with multiple steps and validation?
Why they ask: Multi-step forms are common in real applications. This shows your ability to manage complexity.
Answer framework:
-
Choose your approach:
- Simple (3-4 steps): Local state with a step counter
- Complex (many steps, lots of validation): Form library like Formik or React Hook Form
-
Architecture:
function MultiStepForm() {
const [step, setStep] = useState(1);
const [formData, setFormData] = useState({});
const [errors, setErrors] = useState({});
const handleNext = () => {
if (validateStep(step)) {
setStep(step + 1);
}
};
const handleBack = () => setStep(step - 1);
return (
<div>
{step === 1 && <PersonalInfo onChange={setFormData} />}
{step === 2 && <Address onChange={setFormData} />}
{step === 3 && <Review data={formData} />}
<button onClick={handleBack} disabled={step === 1}>Back</button>
<button onClick={handleNext}>
{step === 3 ? 'Submit' : 'Next'}
</button>
</div>
);
}
-
Key considerations:
- Validate only the current step, not the whole form
- Save data between steps so users don’t lose input
- Show progress indicator
- Allow going back
- Show a review step before submitting
-
With a form library:
- Formik or React Hook Form handles state and validation
- Easier to manage complex validation rules
- Built-in error handling
-
Server submission:
- Validate again server-side
- Show progress/loading state
- Handle errors gracefully
Personalization tip: Describe a specific multi-step form you built and a challenge you overcame.
Questions to Ask Your Interviewer
Asking thoughtful questions shows genuine interest, helps you evaluate the role, and demonstrates strategic thinking.
Can you walk me through the current React architecture and how new features are typically added?
Why ask this: You’ll understand how the team works, whether they refactor regularly or have technical debt, and if they value code quality. Their answer reveals the maturity of their codebase.
What to listen for: Do they sound proud of the architecture or frustrated? Are there clear patterns and conventions? How long does a typical feature take?