Mahamudul Hasan Rubel
HomeAboutProjectsSkillsExperienceBlogPhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • About
  • Projects
  • Skills
  • Experience
  • Blog
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
ReactNext.jsJune 20, 20264 min read

React State Management: How to Lift State Up Effectively

React state management gets easier when you learn how to lift state up. Discover how to sync sibling components and build a predictable data flow today.

ReactFrontend EngineeringState ManagementJavaScriptWeb DevelopmentNext.jsTutorial
Close-up of JavaScript code on a laptop screen, showcasing programming in progress.

I remember sitting through my first code review where a senior dev pointed out that my sibling components were fighting over the same data. I had duplicated the useState hook in two separate child components, leading to a UI that felt like a broken mirror—update one side, and the other stayed stubbornly stuck in the past.

If you've ever felt like your UI is out of sync, you aren't alone. Mastering lifting state up is the single most important step toward professional-grade component architecture.

Why Sibling Components Can't Talk

In React, data flows one way: down. A parent passes information to a child via React props and state: Where your data should live. Because the flow is unidirectional, a child component cannot pass data back up to its sibling directly. They are essentially isolated islands.

When I first started, I tried to solve this by using global variables or complex event emitters. Both were terrible mistakes that made debugging a nightmare. The "React way" is much simpler: find the closest common ancestor of the two components that need to share data and move the state there.

The Anatomy of Lifting State Up

Imagine a dashboard with a search bar and a results list. If the search bar manages its own query state, the results list has no way of knowing what the user typed.

Here is the visual logic:

  1. Identify the common ancestor (the dashboard).
  2. Move the useState call from the SearchBar to the Dashboard.
  3. Pass the state value down to the ResultsList.
  4. Pass a setter function down to the SearchBar.
JSX
// The Parent (Dashboard)
function Dashboard() {
  const [query, setQuery] = useState("");

  return (
    <>
      <SearchBar onSearch={setQuery} />
      <ResultsList filter={query} />
    </>
  );
}

By moving the source of truth to the parent, both children now receive the same data. When the user types into the input, the parent re-renders, and both children update instantly. This is the bedrock of component architecture that survives a growing team in Next.js.

Watch Out for Prop Drilling

Once you start lifting state, you’ll inevitably run into "prop drilling"—the act of passing data through several layers of components that don't actually need it. I’ve seen projects where a user ID was passed through five layers just to reach a tiny button.

Before you get frustrated, remember that prop drilling is often a sign that you should be using component composition. Instead of passing props through, you can pass the child component itself as a children prop. This keeps your hierarchy flat and your logic clean. If you're still confused about the fundamentals of hooks, useState and useEffect: A Mental Model for React Beginners provides a great refresher on how these pieces connect.

When to Keep State Local

Don't fall into the trap of lifting everything to the top. If a piece of state is only used by a single component or its immediate children, keep it there.

I usually ask myself these three questions before moving state:

  • Does another component need this data to render?
  • Will this data need to persist if the current component unmounts?
  • Is this data used in more than two places?

If the answer is "no," leave it where it is. Over-engineering your react state management is just as bad as having a messy codebase. I once spent about two days refactoring a perfectly fine local state into a global context, only to realize I had made the code 30% harder to read for no performance gain.

Troubleshooting Your Flow

If your components aren't updating, check these three things:

  1. The Source of Truth: Is the state actually being managed in the parent?
  2. The Setter Function: Are you passing the function correctly, or are you accidentally calling it during the render phase?
  3. Immutability: Are you updating the state with a new object or array? React won't trigger a re-render if you mutate the existing state object directly.

Final Thoughts

Lifting state up is about control. It’s about deciding where your data lives so that your application remains predictable. You’ll make mistakes—I still catch myself trying to force data into the wrong component occasionally—but the goal is to keep the data flow as simple as possible.

Next time you find yourself trying to sync two components, don't reach for a complex library. Just look up the tree, find that common ancestor, and let the data flow down. What are you currently struggling to sync in your project? Sometimes just sketching the tree on a napkin is enough to see where the state really belongs.

Back to Blog

Similar Posts

Close-up of JavaScript code on a laptop screen, showcasing programming in progress.
ReactNext.jsJune 20, 20264 min read

React props and state: Where your data should live

React props and state management can be confusing. Learn the right way to structure your data, avoid prop drilling, and keep your components predictable.

Read more
Close-up image of ethernet cables plugged into a network switch, showcasing IT infrastructure.
ReactNext.jsJune 20, 20264 min read

Next.js App Router Server Actions for Atomic State Synchronization

Next.js App Router Server Actions can handle atomic state synchronization. Learn how to manage complex dashboard state without the bloat of global stores.

Read more
Close-up of JavaScript code on a laptop screen, showcasing programming in progress.
ReactNext.jsJune 20, 20264 min read

Fetching data in a React component the right way

Fetching data in a React component the right way isn't just about calling an API. Avoid common useEffect traps and build faster, more stable apps today.

Read more