DEV Community

Talisson
Talisson

Posted on • Edited on

Part 1 - Rendering Components Outside the DOM Tree with the Outlet Pattern in React

When working on complex UI layouts, you often encounter scenarios where a component must render in a different part of the DOM tree than where it is declared. This is where the Outlet Pattern becomes a powerful technique. Unlike patterns tied to routing (like React Router's Outlet), this approach is layout-agnostic and purely structural — it helps you control where in the DOM a component should render, without tight coupling to its declaration point.

In this article, we’ll explore how the Outlet Pattern works, when you should consider using it, and how to implement it with React Portals.

The Problem: Component Misalignment

Consider a situation where you’re building a design system component — for example, a Bulk Actions Bar that appears when items are selected. You want this bar to:

  • Render at a specific position within a content layout.
  • Be decoupled from its logical parent component (e.g., a Table or List component).
  • Avoid issues with CSS stacking context and layout constraints.

If you render the bar inline, it might break the layout flow. You need a way to teleport its rendered output to a precise location in the DOM — but still control it declaratively in React.

The Solution: Outlet Pattern with React Portals

The Outlet Pattern introduces a placeholder (Outlet component) in the desired DOM location and a render controller (OutletProvider) that allows components elsewhere in the tree to render into that placeholder.

The Key Pieces:

  1. Outlet Component — A placeholder that provides a DOM node reference.
  2. OutletProvider/Context — Shares the target DOM node with components that want to render into the Outlet.
  3. Portals — Used by child components to render into the provided DOM node.

Implementing the Outlet Pattern

Step 1: Create the Outlet Context

import { createContext, useContext, useRef, useState } from 'react'

const OutletContext = createContext(null)

export function useOutlet() {
  const context = useContext(OutletContext)
  if (!context) {
    throw new Error('useOutlet must be used within an OutletProvider')
  }
  return context
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Build the Outlet Component

export function Outlet() {
  const [, setNode] = useOutlet()

  const callbackRef = useCallback((nodeRef) => {
    if(nodeRef) {
       setNode(nodeRef)
    }
  }, [setNode])

  return <div ref={callbackRef} />
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Provide the Outlet Context

export function OutletProvider({ children }) {
  const [node, setNode] = useState(null)
  return (
    <OutletContext.Provider value={[node, setNode]}>
      {children}
    </OutletContext.Provider>
  )
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Rendering into the Outlet

import { createPortal } from 'react-dom'

export function BulkActionsBar() {
  const [node] = useOutlet()

  if (!node) return null

  return createPortal(
    <div className="bulk-actions-bar">I’m rendered in the Outlet!</div>,
    node
  )
}
Enter fullscreen mode Exit fullscreen mode

Usage Example

<OutletProvider>
  <Layout>
    <MainContent />
    <Outlet />
  </Layout>

  <BulkActionsBar />
</OutletProvider>
Enter fullscreen mode Exit fullscreen mode

When Should You Use the Outlet Pattern?

  • You need to render floating elements (toolbars, modals, bulk action bars) into a specific layout area.
  • You want a clean separation of component logic vs layout positioning.
  • You need a flexible layout structure without deep prop drilling.

Conclusion

The Outlet Pattern is a simple yet powerful technique when you need to control where a component renders in the DOM, independently from its logical position in the React tree. Combined with React Portals, it becomes an elegant solution for layout composition challenges.

Top comments (0)