Why ReactFlow Edges Disappear in Next.js (Two Subtle CSS and Context Bugs)
Source: Dev.to
We were building a knowledge‑graph view — a React Flow canvas that shows topic chains and semantic links between video segments. Nodes rendered fine, but every time we loaded the view the edges (the connecting lines between nodes) were simply gone.
No errors in the console. Nodes in place. Just… no edges.
Below is the two‑bug chain that caused it.
Bug 1: overflow‑hidden creates a stacking context that clips SVG edges
The first culprit was this wrapper:
// GraphView.tsx — BEFORE
overflow: hidden does two things most people don’t think about:
- Clips content that overflows the box (the obvious thing).
- Creates a new stacking context.
React Flow renders its edges as an SVG layer that is absolutely positioned within the canvas container. When an ancestor has overflow: hidden, that SVG layer gets clipped—even if it’s technically within the visible bounds—because the stacking context changes how the browser composites layers.
Fix
Use overflow: clip instead:
// GraphView.tsx — AFTER
{/* overflow‑clip instead of overflow‑hidden: clips visually without creating a new
stacking context that would cause React Flow's SVG edge layer to be invisible */}
overflow: clip behaves like overflow: hidden visually—content is clipped at the box boundary—but does not create a new stacking context. The SVG edge layer can now composite correctly.
This is one of those CSS properties that exists specifically because
overflow: hiddenhas an unfortunate side effect that trips up complex rendering scenarios like SVG overlays, fixed‑positioned children, and canvas‑based UIs.
Bug 2: Next.js dynamic() imports need an explicit ReactFlowProvider
Even after fixing the overflow issue, edges were still missing in some cases. The second bug was more subtle.
React Flow uses React context internally. The <ReactFlow> component expects a ReactFlowProvider to be present somewhere up the tree. In most setups you either wrap your app/page in a provider, or rely on React Flow’s own internal provider being initialized before the component renders.
The problem: we were loading the graph component with Next.js dynamic():
// project/page.tsx
const VideoKnowledgeGraph = dynamic(
() => import('@/components/VideoKnowledgeGraph'),
{ ssr: false }
);With dynamic(), the component is loaded in a separate chunk and initialized asynchronously. When this happens, the React Flow context—specifically the provider that manages the internal store for nodes, edges, and the SVG renderer—can be absent or not yet initialized at the time the SVG edge layer tries to render.
Result: nodes render (they don’t depend on the internal edge store in the same way), but edges don’t.
Fix
Explicitly wrap the dynamically imported component in <ReactFlowProvider>:
// VideoKnowledgeGraph.tsx — AFTER
import { ReactFlow, ReactFlowProvider, Background, Controls } from '@xyflow/react';
export default function VideoKnowledgeGraph({ graph, ...rest }) {
// …
return (
// ReactFlowProvider is required here because this component is loaded via Next.js
// dynamic() import. Without an explicit provider, the React Flow context (including
// the SVG edge renderer) can be missing, causing edges to not render.
<ReactFlowProvider>
<ReactFlow
nodes={graph.nodes}
edges={graph.edges}
// …other props
>
<Background />
<Controls />
{/* toolbar */}
{/* …other UI */}
</ReactFlow>
</ReactFlowProvider>
);
}Rule of thumb: any React Flow component loaded via dynamic() should include its own ReactFlowProvider. Don’t assume the context will be present from a parent—async chunk loading makes the initialization order uncertain.
Why both bugs were needed to hide edges
Either bug alone might have been survivable:
| Bug | Possible outcome |
|---|---|
| Stacking‑context issue only | Edges might render in some browsers but get clipped in others |
| Missing provider only | React Flow could fall back to an ancestor provider if one existed |
Together they compounded: the SVG layer wasn’t rendering at all (provider issue), and even if it had rendered, it would have been clipped (overflow issue).
When debugging React Flow edge visibility, check these two things first:
- Any ancestor with
overflow: hidden? Switch tooverflow: cliporoverflow: visible. - Is the component loaded dynamically? Wrap it in
<ReactFlowProvider>.
Takeaway
overflow: hidden is one of CSS’s “does more than it says” properties—it creates a new stacking context that can interfere with SVG overlays, fixed‑positioned children, and canvas‑based UIs. When using libraries like React Flow that render edges in an SVG layer, prefer overflow: clip (or overflow: visible) unless you explicitly need the stacking‑context side effect. And always ensure the required context providers are present when loading components asynchronously with Next.js dynamic().
t creation. And when using React Flow with Next.js dynamic(), always provide the context explicitly rather than hoping it exists up the tree.
Both are easy one‑line fixes once you know what to look for. The hard part is knowing what to look for.
