The Road to Next — your interactive course for Next.js with React

Agentic Coding: Bet on the Primitives

Robin Wieruch

A freelance project recently needed three custom charts: a stacked bar chart, a line chart, and a donut. Nothing exotic on paper, but each one had to match the client’s design system exactly. The spacing, the typography, the entrance animations, the way tooltips and legends behave everywhere else in the application. Recharts was already installed in the project. Past-me would have reached for it without a second thought. And here is the ironic part: I learned D3 properly years ago, and precisely because I know how much work hand-rolled charts are, I would never have budgeted them for a client. At least, that was the math before agentic coding.

The Experiment: D3 Primitives vs. Recharts

This time I ran an experiment that would have been irresponsible not long ago: I spiked both approaches within an hour. One set of chart components built on D3’s math primitives, with React rendering the SVG. And a twin set built on Recharts, with the same component API and the same design tokens, so both were interchangeable from the outside.

The result was not what I expected. The primitive version matched the design exactly, entrance animations, crosshair tooltips, legend filtering and all, without fighting anything, because there was nothing to fight. The Recharts version got 80% of the way there faster, but the last 20% required the same custom SVG code anyway: custom shapes, custom tooltips, custom labels. And then it hit a wall. Its entrance animation froze in the project’s React stack, and the only workaround was turning the animation off. The workarounds piled up on the high-level side, not the low-level one.

What an Abstraction Actually Is

That afternoon inverted a rule I had internalized over fifteen years of building for the web: never hand-roll what a library already solves. To understand why the rule is starting to crack, it helps to be precise about what a high-level library actually is.

An abstraction is encoded, prepaid implementation labor. Somebody already spent the thousands of hours it takes to make axis ticks land on round numbers, to make a tooltip follow a cursor without flickering, to make a legend toggle a series. That labor was frozen into a package you can install in seconds. You do not pay for it with money. You pay for it with flexibility: the library’s opinions about rendering, styling, and behavior come bundled with its solutions, and the moment your requirements leave its happy path, you start paying again. In wrapper components, patched styles, prop escape hatches, and GitHub issues that close with “not planned”.

High-level libraries were never free: they traded flexibility for implementation labor, and they won because human implementation time was the scarcest resource in software. When hand-rolled charts cost three developer weeks and a library costs an afternoon of reading docs, accepting the library’s opinions is not a compromise. It is the only rational choice. That math, not elegance, is what pushed every team up the abstraction ladder and kept them there.

Twenty Years Up the Ladder

The history of web development is largely the story of that ladder. jQuery plugins encoded DOM manipulation that nobody wanted to write twice. Backbone, Angular, and React encoded the synchronization of UI and state. Component libraries and design-system kits encoded thousands of design decisions into installable packages. ORMs encoded SQL and schema migrations. create-react-app, and later the meta frameworks, encoded entire build pipelines and routing architectures. Every step was celebrated, rightly, because every step converted expensive human labor into a cheap dependency.

Read More
React Libraries for 2026

There were early hints that the ladder was not a one-way street. Bootstrap gave way to Tailwind: finished components traded for low-level utilities that let every team build its own look. Shadcn UI pushed further: you do not install its components, you copy them into your project and own them. Both were bets on ownership, placed before ownership got cheap.

The Swing Back Down

Agentic coding is what makes ownership cheap. When an agent can produce a working, reviewed chart component in minutes instead of weeks, the scarcity that justified the high-level library evaporates. And when implementation labor stops being scarce, the rational bet moves back down the abstraction ladder: unopinionated primitives plus a thin custom layer that your team owns, shaped exactly to your use case.

To be clear about what this is not: it is not a return to hand-written everything, and it is certainly not “AI changes everything”. The pendulum swings from renting opinions to owning a layer, not from libraries to assembly.

labor was scarce: rent opinions
Meta-frameworks & create-react-app
build pipelines, routing architectures
ORMs
SQL, schema migrations
Component & design-system kits
thousands of design decisions
React, Angular, Backbone
synchronizing UI and state
jQuery plugins
DOM manipulation
Your thin layer
a few hundred lines, shaped to your product, written with agents
The primitives
DOM · SVG · SQL · HTTP · d3-scale · date-fns · zod
labor collapsed: own a layer

In my case, the thin layer was a handful of chart components, a few hundred lines each, built on D3’s scale and shape modules, rendered by React, styled with the project’s design tokens from day one. There is no upgrade treadmill, no waiting for a maintainer to support the next React release, no reading someone else’s source to find out why an animation freezes. When the design changes, the team changes the layer. The code reads like the rest of the codebase because it is the rest of the codebase.

Owning a layer is only viable if your team can maintain it, and this is where agentic workflows matter twice: once for writing the layer, and once for keeping it consistent. A custom layer without documented patterns and reviews degrades into a liability like any other unowned code. I wrote about how we handle that side of the equation in Agentic Code Review: Pattern Matching for AI.

Primitives Are Not Zero Dependencies

An objection I want to get ahead of: d3-scale is a library. So are date-fns and zod. Betting on primitives does not mean betting against dependencies.

The distinction that matters is not library versus no library. It is unopinionated versus opinionated: does the dependency compute something for you, or does it decide something for you? A scale maps numbers to pixels and has no opinion about what you draw with them. A charting framework owns the SVG, the animation lifecycle, the tooltip behavior, and hands you configuration props to negotiate with. The first is a primitive, the second is a framework, and the difference shows up exactly at the moment your requirements diverge from the author’s assumptions.

A primitive computes for you
value: 42scaleLinear137 px
It maps numbers to pixels and has no opinion about what you draw. Rendering, animation, and interaction stay yours.
A framework decides for you
<BarChart>
rendering — ownedanimation — ownedtooltips — owned
</BarChart>
Its opinions ship with its solutions. When your requirements leave the happy path, you negotiate through config props and workarounds.

The economics have not changed for genuinely hard, stable problems. Nobody should regenerate date arithmetic, color interpolation, schema validation, or floating-element positioning. Those are solved problems with brutal edge cases, and the primitives that solve them are exactly the dependencies worth keeping. What you want to own is the part that carries your product’s identity: rendering, interaction, behavior.

When the High-Level Library Still Wins

I want to argue against myself for a moment, because this bet is not one-sided.

If your team has no established agentic workflow, the old math still applies in full. Implementation labor has not gotten cheaper for you, and a hand-rolled chart layer is still three weeks of work plus indefinite maintenance. The bet on primitives is a bet on a capability, and you cannot place it before you have the capability.

If your UI is commodity, the library wins even with agents. An internal dashboard with default-styled charts sits squarely on Recharts’ happy path, and the happy path is genuinely excellent. Spending an afternoon rebuilding what a library gives you in ten minutes is not ownership, it is vanity.

Maintenance responsibility is real. Every line you own is a line you maintain. A popular library has thousands of projects surfacing its edge cases and a community fixing them; your custom layer has one project and your team. For anything with deep accessibility requirements, a combobox, a date picker, a modal with correct focus management, mature libraries encode years of screen-reader and keyboard testing that I would not casually regenerate.

And one more honest caveat: my spike worked because I could judge the output. I knew what a scale was, what a shape generator does, what correct looks like. An agent collapses the cost of writing the layer, not the cost of understanding it.

Where the Crossover Sits

Every library decision has always been a bet on where your requirements will land relative to the library’s happy path. The closer your needs sit to that happy path, the more the library wins. The further your needs drift toward custom, the more the primitives win. That heuristic is old and it still holds. Agentic coding did not change the heuristic; it moved the crossover point, dramatically, toward the primitives.

Requirements that used to sit comfortably on the library side of the line, three charts matching a design system, a bespoke form flow, a custom data grid, now sit on the other side, because the cost that kept them there was implementation labor and that cost has collapsed.

Here is the whole argument in one picture. Drag the cost of implementation labor down from hand-written toward agentic and watch the crossover move:

Where the crossover sits
Total cost to ship a feature, by how far its requirements drift from the library's happy path. Drag the slider to change what implementation labor costs.
100%
hand-writtenagentic
high-level libraryprimitives + thin layer
↑ total cost
library wins
primitives win
on the happy pathfully custom →
With implementation labor at 100% of the hand-written baseline, primitives win the 12% most custom requirements.

And yes, the chart above is a few hundred lines of React and raw SVG in a folder next to this article’s markdown. No charting library was harmed, or installed.


For twenty years, reaching for the highest sensible abstraction was the safe default, and questioning it was a waste of a client’s budget. The next time a high-level library is already installed and waiting, it is worth asking what it is actually saving you. I suspect the most valuable layer in your codebase a few years from now will not be one you installed, but the thin one your team wrote on top of primitives, because you finally could afford to own it.

Never Miss an Article

Join 50,000+ developers getting weekly insights on full-stack engineering and AI.

AI Agentic UI Architecture React Next.js TypeScript Node.js Full-Stack Monorepos Product Engineering
Subscribe on Substack

High signal, low noise. Unsubscribe at any time.