Fixing a circular dependency in bits-ui that broke SSR
November 15, 2025
I was working on a SvelteKit project that used bits-ui for headless UI components. Everything worked fine in the browser, but on every SSR render the server was throwing:
TypeError: Cannot read properties of undefined (reading 'Root')
The stack trace pointed into bits-ui internals. The component was defined — the TypeScript
types were fine — but at runtime on the server, the import was resolving to undefined.
Finding the cause
The issue was a circular dependency created by barrel-file imports. The library was
exporting everything through a single $lib/index.js entry point, and components
were importing from that same barrel file. During SSR, Node.js evaluates modules
synchronously. When module A imports from the barrel, and the barrel imports from module A
(transitively), one of them gets an incomplete module object — undefined — at the
point it's needed.
The browser doesn't hit this because it uses a different module evaluation strategy that handles cycles more gracefully. SSR exposes it immediately.
The fix
The solution was to replace barrel-file imports inside the library with direct module references. Instead of:
import { Dialog } from '$lib/index.js'; Import the module directly:
import { Dialog } from '$lib/components/dialog/index.js'; This breaks the cycle. Each module now has a clear, acyclic dependency path, so SSR evaluation completes without hitting an undefined intermediate.
The broader pattern
Barrel files are convenient for consumers of a library, but they create risk when used internally. Inside a library, prefer direct imports. Reserve the barrel for the public API surface — the thing your users import from.
If you're debugging an SSR crash where something is undefined despite being correctly
typed, circular dependencies through barrel files are worth checking early. A quick way to confirm:
temporarily replace a suspected barrel import with the direct path and see if the crash disappears.