Skip to main content

useOutlet

Returns the element for the child route at this level of the route hierarchy. Used internally by <Outlet> to render child routes.

Signature

function useOutlet(context?: unknown): React.ReactElement | null

Parameters

context
unknown
Optional context to pass to the outlet. This is typically used with useOutletContext in child routes.

Returns

element
React.ReactElement | null
The child route element to render, or null if no child routes match.

Usage

Basic usage

import { useOutlet } from "react-router";

function ParentRoute() {
  const outlet = useOutlet();
  
  return (
    <div>
      <h1>Parent Route</h1>
      {outlet}
    </div>
  );
}
This is equivalent to:
import { Outlet } from "react-router";

function ParentRoute() {
  return (
    <div>
      <h1>Parent Route</h1>
      <Outlet />
    </div>
  );
}

Pass context to child routes

function ParentRoute() {
  const [count, setCount] = useState(0);
  const outlet = useOutlet({ count, setCount });
  
  return (
    <div>
      <h1>Count: {count}</h1>
      {outlet}
    </div>
  );
}

// Child route
function ChildRoute() {
  const { count, setCount } = useOutletContext();
  
  return (
    <button onClick={() => setCount(count + 1)}>
      Increment
    </button>
  );
}

Conditional outlet rendering

function Layout() {
  const outlet = useOutlet();
  const hasOutlet = outlet !== null;
  
  return (
    <div>
      <Header />
      
      {hasOutlet ? (
        <main className="with-sidebar">
          <Sidebar />
          <div className="content">{outlet}</div>
        </main>
      ) : (
        <main className="full-width">
          <DefaultContent />
        </main>
      )}
      
      <Footer />
    </div>
  );
}

Animate route transitions

import { useOutlet, useLocation } from "react-router";
import { CSSTransition, SwitchTransition } from "react-transition-group";

function AnimatedOutlet() {
  const outlet = useOutlet();
  const location = useLocation();
  
  return (
    <SwitchTransition>
      <CSSTransition
        key={location.pathname}
        timeout={300}
        classNames="fade"
      >
        <div>{outlet}</div>
      </CSSTransition>
    </SwitchTransition>
  );
}

Custom outlet wrapper

function ProtectedOutlet({ requiredRole }) {
  const outlet = useOutlet();
  const { user } = useLoaderData();
  
  if (!user || !user.roles.includes(requiredRole)) {
    return <Navigate to="/login" />;
  }
  
  return outlet;
}

function AdminLayout() {
  return (
    <div>
      <AdminNav />
      <ProtectedOutlet requiredRole="admin" />
    </div>
  );
}

Common Patterns

Error boundary wrapper

function OutletWithErrorBoundary() {
  const outlet = useOutlet();
  
  return (
    <ErrorBoundary>
      {outlet}
    </ErrorBoundary>
  );
}

Loading wrapper

function SuspenseOutlet() {
  const outlet = useOutlet();
  const navigation = useNavigation();
  
  return (
    <Suspense
      fallback={
        navigation.state === "loading" ? <Spinner /> : null
      }
    >
      {outlet}
    </Suspense>
  );
}

Scroll restoration

function ScrollOutlet() {
  const outlet = useOutlet();
  const location = useLocation();
  
  useEffect(() => {
    window.scrollTo(0, 0);
  }, [location.pathname]);
  
  return outlet;
}

Track depth

function OutletWithDepth({ depth = 0 }) {
  const outlet = useOutlet({ depth: depth + 1 });
  
  return (
    <div style={{ paddingLeft: `${depth * 20}px` }}>
      {outlet}
    </div>
  );
}

function ChildRoute() {
  const { depth } = useOutletContext();
  return <div>Depth: {depth}</div>;
}

Custom transition logic

function FadeOutlet() {
  const outlet = useOutlet();
  const [displayLocation, setDisplayLocation] = useState(outlet);
  const location = useLocation();
  
  useEffect(() => {
    if (outlet !== null) {
      setDisplayLocation(outlet);
    }
  }, [outlet]);
  
  return (
    <div
      style={{
        opacity: outlet === null ? 0 : 1,
        transition: "opacity 300ms",
      }}
    >
      {displayLocation}
    </div>
  );
}

Comparison with Outlet

Feature<Outlet>useOutlet
UsageComponentHook
FlexibilityLess flexibleMore control
Common useMost layoutsCustom rendering logic
Context<Outlet context={...} />useOutlet(context)
Use <Outlet> for simple layouts, and useOutlet when you need:
  • Conditional rendering based on outlet existence
  • Custom wrappers around the outlet
  • Animation/transition control
  • Additional processing of the outlet element

Type Safety

Type outlet context

interface OutletContext {
  user: User;
  settings: Settings;
}

function ParentRoute() {
  const data = useLoaderData();
  const context: OutletContext = {
    user: data.user,
    settings: data.settings,
  };
  
  const outlet = useOutlet(context);
  return <div>{outlet}</div>;
}

function ChildRoute() {
  const context = useOutletContext<OutletContext>();
  return <div>Hello {context.user.name}</div>;
}

Build docs developers (and LLMs) love