Context, portals, error boundaries
No prop drilling, rendering elsewhere in the DOM, errors isolated per section.
Updated
What Context, portals and error boundaries are
Three React tools for situations that props and regular state don't handle well:
| Tool | The problem it solves |
|---|---|
| Context | a value many components need, deep in the tree, without props at every level |
| Portal | an element that has to be rendered somewhere else in the DOM (a modal, a tooltip) than its place in the tree |
| Error boundary | an error in one component mustn't break the whole page |
Context
The problem — prop drilling: user passes through 5 components that don't use it, just to reach the sixth.
const ThemeContext = createContext<'light' | 'dark'>('light') // the default value
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<'light' | 'dark'>('light')
return <ThemeContext value={theme}>{children}</ThemeContext> // React 19: <Context> directly
}
function Badge() {
const theme = use(ThemeContext) // or useContext(ThemeContext)
}| Rule | Details |
|---|---|
| the nearest provider above wins | you can override the value for one section |
no provider → the default value from createContext |
|
| changing the value re-renders every consumer | put rarely changing values in Context |
| a new object on every render = needless re-renders | useMemo on the value or split into several contexts |
| in Next, the provider is a Client Component | you put it in layout.tsx, wrapping children (which stay Server Components) |
For global state that changes often → Zustand / Redux.
Portals
A modal rendered inside a card with overflow: hidden or inside a stacking context gets clipped or shows up below other elements. createPortal renders it into another DOM node (usually at the end of body), but in the React tree it stays where you wrote it — it receives Context, and events bubble up to its React parents.
import { createPortal } from 'react-dom'
function Modal({ children }: { children: React.ReactNode }) {
return createPortal(<div className="fixed inset-0 grid place-items-center bg-black/40">{children}</div>, document.body)
}The modern alternative, without a portal: the native <dialog> element with showModal() and the Popover API (the "All topics" menu in this app) — both render in the top layer, above everything, with focus and Esc handled by the browser.
Error boundaries
An error thrown while a component renders unmounts, by default, the whole app. An error boundary catches the error and shows a fallback only for that section.
| Where | How |
|---|---|
| Next.js App Router | the error.tsx file in a segment — see Layouts and states |
| plain React | a class with getDerivedStateFromError, or the react-error-boundary library |
They don't catch: errors in event handlers (catch those with try/catch), in async code outside rendering, in Server Actions (return those as state).
Summary
- Context = transport without prop drilling; the nearest provider wins; only for rarely changing values.
- A portal = render elsewhere in the DOM, stay in the React tree;
<dialog>and popovers as native alternatives. - An error boundary = the error breaks only its section; in Next,
error.tsx.