UI libraries

Copy-paste or headless: which approach to choose for your UI

Copy-paste gives you the code, headless gives you the behaviour. The choice decides who updates the components and what the bundle weighs.

Key points

  • Copy-paste means ownership: the code is yours, and so are the updates.
  • Headless means delegation: behaviour and accessibility arrive with the package.
  • The test is not fashion: it is who maintains that code in two years, and how stable your design is.

The first decision is not which library to install. It is where the component code lives. That answer drives updates, bundle size, accessibility and the cost of leaving.

Two models, not two tastes

DimensionCopy-pasteHeadless
Where the code livesIn your repositoryIn the installed package
UpdatesYou do them, file by fileThey arrive with the version
StylingFree, no imposed themeYou have to write it
AccessibilityYou inherit it, and you can break itHandled by the package
BundleOnly what you copyDepends on tree-shaking
Cost of leavingLow: the code is already yoursMedium: you rewrite against the API

When copy-paste pays off

It pays off when the design is specific, the component is simple and the team wants to own every line. A button, a card, a badge: copy-paste removes a dependency and leaves the behaviour in plain sight.

It pays off less when the component is complex. A hand-built accessible dialog needs a focus trap, focus restore, scroll lock, Escape handling and correct ARIA. Copying 400 lines you do not understand is not ownership. It is debt with an expiry date.

When headless pays off

It pays off when the behaviour is hard and the styling is yours. Headless primitives split the two: you write the appearance, they guarantee keyboard, focus and ARIA.

The test that exposes the wrong choice

# Quanti file copiati contengono logica di focus o tastiera?
rg -l "focus|keydown|aria-" src/components/ui | wc -l

# Quante dipendenze entrano per un solo componente?
npx depcheck

If the copied files contain focus and keyboard handling, you are maintaining behaviour nobody designed to be maintained. That is where headless wins.

The middle path, explained properly

The shadcn/ui model is copy-paste of components built on headless primitives. The code is yours, the hard behaviour stays delegated. It works because the boundary is clear: you own the styling, the primitive owns the interaction.

  • Install the primitive as a dependency.
  • Copy only the component you need to adapt.
  • Do not rewrite the behaviour of the primitive.
  • Record which version you copied from, so you can compare updates later.

What the catalogue numbers say

EneUI indexes 111,985 components from 1,472 libraries (31 October 2026). The catalogue is useful exactly here: it shows how many different models coexist, with source, licence and dependencies on every record, instead of selling one model as universal.

Frequently asked questions

Is copy-paste better than a library?

Not in the abstract. It is better when you want to own the styling and the component is simple. It is worse when you copy complex behaviour you could not maintain.

Is headless accessible by definition?

No. The primitive handles the behaviour, but your markup, tab order or focus-hiding styles can still break accessibility.

Does copy-paste increase the bundle?

It usually shrinks it, because you never install the rest of the library. Copy components that pull in heavy utilities and the saving disappears.

How do I update copied components?

Keep a note of the source version and diff the files instead of overwriting them. Manual updates are the real cost of this model.

Can I mix the two approaches?

Yes, and most teams do. Keep the boundary explicit: headless for interaction, copy-paste for appearance.