Skip to main content
react

Why I Read Source Code Before I Trust a UI Library

What 15 minutes in node_modules teaches you that no README will — and the specific patterns that disqualify a library before you write a single line with it.


5 min read

Before adding a UI library as a dependency, I read its source. Not all of it — 15 minutes, starting from the main export, following the event handlers, checking the useEffect cleanup. This habit has saved me from three separate production memory leaks and at least two accessibility regressions that reviews would not have caught. The README tells you what the library does. The source tells you how, and how is where the problems live.


What you actually learn

A README describes the intended behavior. Source code describes the actual behavior. These diverge more than library authors admit.

In 15 minutes of reading you learn:

What the real dependencies are. Some packages import a full date library for one formatting call. Some pull in lodash for _.cloneDeep. Some have a dependencies in package.json that looks fine, then internally import from a package that has its own heavy tree. You find this by looking at the import statements at the top of the main module.

How errors surface. Does the library throw synchronously, reject a promise, call an onError callback, or silently swallow errors and log to console.error? These are different failure modes and you need to know which one you are dealing with before you build error handling around it.

Whether cleanup happens. In React components and hooks, the pattern that kills you is addEventListener without a corresponding removeEventListener in a cleanup function. Same with timers, subscriptions, mutation observers, and resize observers.

What to look at first

Start with the entry point — whatever is listed in main or exports.import in package.json. Look at the main export's file. Specifically:

  1. useEffect calls — is there a return function? What does it clean up?
  2. Event listeners — is addEventListener paired with removeEventListener?
  3. Refs — are they being mutated inside render? (A bug waiting to happen.)
  4. Global access — document.body, window.innerWidth, navigator.userAgent. These break SSR.
  5. The error path — what happens when the required prop is missing or invalid?

A specific thing I found by reading source

A drag-and-drop library that had 400 GitHub stars and looked well-maintained. README showed a clean API, TypeScript types included, examples worked in the demo. I added it to a project, ran the app, everything seemed fine.

Before shipping I read the source. In the main hook:

useEffect(() => {
  document.addEventListener('mousemove', handleMouseMove)
  document.addEventListener('mouseup', handleMouseUp)
  // No cleanup function returned
}, [])

No cleanup. The event listeners were added on mount and never removed. Every time a component using this hook mounted, two global listeners accumulated. Unmount and remount in a list — which happens constantly in React with key-based remounting — and you leak a new pair of listeners on every cycle.

This would not have appeared in testing unless you specifically tested mount/unmount cycles. It would have shown up as degraded performance and bizarre drag behaviors after extended app use.

The fix was three lines. But knowing to fix it required reading the source.

Fast disqualifiers

Some things end the evaluation immediately:

document.querySelector called directly in a hook or component. This is not testable, breaks SSR, and suggests the author did not think about the rendering environment. Libraries like react-beautiful-dnd (now deprecated) had this problem.

Mutating props or the first argument. If you see options.items = [] or props.style.display = 'none' in library source, close the tab.

console.log left in production code. Not catastrophic, but signals the release process is not careful.

A try/catch that swallows errors. catch (e) {} with nothing in the catch block means failures are silent. You will not know the library errored.

Peer dependencies imported as direct dependencies. If react or react-dom is in dependencies instead of peerDependencies, you may end up with two copies of React in your bundle.

The actual time investment

15 minutes is not precious time — it is due diligence for code that will run in production for months or years. The libraries that hold up under source review are also usually the ones with good documentation, responsive maintainers, and sensible changelogs. The correlation is not coincidence. Authors who think carefully about implementation tend to think carefully about everything else.