Facebook Pixel

Avoiding Prop Drilling Smartly

In this tutorial, you will learn how to avoid prop drilling the right way in React 19 without overusing Context or creating hard to maintain global state.

Prop drilling is not evil. Using the wrong solution for it is.


What We Will Learn

  • What prop drilling actually is
  • When prop drilling is perfectly fine
  • When it becomes a real problem
  • Smart patterns to avoid it in React 19
  • How Context and use fit into the solution

By the end, you will know how to design clean component trees without unnecessary complexity.


What Is Prop Drilling

Prop drilling happens when data is passed through components that do not actually need it.

Example:

<App user={user}>
  <Layout user={user}>
    <Sidebar user={user}>
      <UserProfile user={user} />
    </Sidebar>
  </Layout>
</App>

Only UserProfile needs user, but every component must forward it.

This is prop drilling.


When Prop Drilling Is NOT a Problem

This is important.

Prop drilling is not automatically bad.

It is perfectly fine when:

  • The component tree is shallow
  • The data is clearly owned by a parent
  • Only a few components are involved
  • The data is local and short lived

Example:

<Form>
  <Input value={value} />
</Form>

This is clean and readable. Do not over engineer this.


When Prop Drilling Becomes a Problem

Prop drilling becomes a problem when:

  • Many layers pass props blindly
  • Components become wiring code
  • Refactoring is painful
  • Props explode across layouts
  • The data is global in nature

At this point, readability and maintainability suffer.


The Biggest Mistake: Reaching for Context Too Early

A common beginner mistake is:

I see prop drilling, I must use Context

This leads to:

  • Over scoped global state
  • Hidden dependencies
  • Harder debugging
  • Poor mental models

Context is powerful, but it should be intentional.


Smart Strategy 1: Lift Components, Not State

Often the simplest fix is moving the component, not the data.

Instead of drilling:

<Layout>
  <Sidebar>
    <UserProfile user={user} />
  </Sidebar>
</Layout>

Lift the consumer closer to the data:

<Layout>
  <UserProfile user={user} />
  <Sidebar />
</Layout>

This avoids drilling without adding new abstractions.


Smart Strategy 2: Slot Pattern (Children as Data)

Sometimes you can pass the UI instead of the data.

// components/layout.tsx
export function Layout({ sidebar }: { sidebar: React.ReactNode }) {
  return (
    <div>
      <aside>{sidebar}</aside>
      <main>Main content</main>
    </div>
  )
}
<Layout sidebar={<UserProfile user={user} />} />

No prop drilling. No Context.


Smart Strategy 3: Use Context for Truly Global Data

When data is:

  • Used across many branches
  • Required deep in the tree
  • Conceptually global

Context is the correct tool.

Creating Context

// contexts/auth-context.ts
import { createContext } from "react"
 
export const AuthContext = createContext<{ name: string } | null>(null)

Providing Context

// app/layout.tsx
import { AuthContext } from "../contexts/auth-context"
 
export default function RootLayout({
  children
}: {
  children: React.ReactNode
}) {
  const user = { name: "Alex" }
 
  return (
    <AuthContext value={user}>
      {children}
    </AuthContext>
  )
}

Reading Context with use

// components/user-profile.tsx
import { use } from "react"
import { AuthContext } from "../contexts/auth-context"
 
export function UserProfile() {
  const user = use(AuthContext)
 
  if (!user) return null
 
  return <p>Hello {user.name}</p>
}

No prop drilling. No prop forwarding.


Smart Strategy 4: Split Data by Responsibility

Avoid giant context objects.

Bad:

<AppContext value={{ user, theme, settings, notifications }}>

Good:

<AuthContext value={user}>
  <ThemeContext value={theme}>
    {children}
  </ThemeContext>
</AuthContext>

Smaller contexts mean fewer unnecessary re-renders and clearer intent.


Smart Strategy 5: Keep Local State Local

Not everything needs to escape the component.

If only one or two children need the data, props are the best solution.

Props are:

  • Explicit
  • Easy to trace
  • Easy to refactor

Do not replace them unless there is a clear benefit.


Mental Model

Think of data ownership like this:

  • Local data stays local
  • Shared data moves up
  • Global data goes to Context
  • UI composition can replace data passing

Prop drilling is a signal, not a failure.


A Simple Rule of Thumb

Ask these questions:

  1. Is the data truly global?
  2. Do many distant components need it?
  3. Is prop forwarding hurting readability?

If yes, use Context. If no, props are fine.


Conclusion

Avoiding prop drilling smartly is about choosing the right tool, not eliminating props.

React 19 gives you:

  • Better Context ergonomics
  • The use API for clean consumption
  • Clear separation between local and global data

Use props confidently. Use Context intentionally. And design your component tree to reflect real ownership of data.