There is a quiet argument happening in modern React teams every time someone reaches for a shared UI component. Do you install it from npm and let a version number govern your dependency, or do you copy the source into your repository and own every line forever? The shadcn registry made the second option mainstream, and now a lot of engineers genuinely cannot decide which side they are on. The honest answer is that both models are correct, just for different situations. What is rarer, and more interesting, is a component that ships both ways from a single source of truth so you do not have to pick at the wrong moment.
The traditional npm package is a contract. You write npm install some-component, you import it, and you accept that the maintainer owns the internals. When they ship a fix, you get it with a version bump. Your code stays small because the implementation lives in node_modules, not in your diff. The cost is control: if you need to change a tiny behavior, you are filing an issue, forking, or wrapping.
The shadcn registry flipped that contract. Instead of importing a black box, you run a CLI command that writes the component's actual source files into your project. From that moment you own them. There is no dependency to update, no version to track, and no maintainer standing between you and the markup. You can rename props, delete features you do not use, and rewrite the styling to match your design system without asking permission.
npm is "I trust the maintainer to keep this correct." shadcn is "I want this code to be mine." Neither is wrong. They are answers to different questions.It is easy to romanticize "you own the code," so let me be blunt about what ownership costs. When you copy a component via a registry, you also copy the responsibility for its bugs, its accessibility, and its future maintenance. If the upstream project fixes a focus-management edge case six months later, you will not get that fix automatically. You either re-copy and re-merge your local changes, or you live without it.
Conversely, the npm model's convenience is also its constraint. The moment a versioned package does not quite fit your composer layout, you are bending it from the outside with wrapper components and CSS overrides. Anyone who has fought a third-party input's internal padding from two layers away knows the feeling.
Here is a compact way to reason about it:
That last bullet is the one teams forget. A "copy the source, you own it" component is a gift only if the source is clean. If it pulls in ProseMirror or a five-package editor framework underneath, you now own a forest, not a file. The promise of copy-paste React components is that you can read and maintain what you pasted; that promise is worthless if the code you pasted is just a thin shim over a dependency you still cannot see into.
There is also a version-management nuance worth stating out loud. With npm, your lockfile is a record of exactly which version of the component you are running, which makes audits and rollbacks mechanical. With a registry copy, the "version" is whatever you pasted on the day you pasted it, and there is no automatic signal when the upstream source has moved on. Neither is better in the abstract, but if your team relies on lockfile-driven supply-chain checks, that difference deserves a conscious decision rather than a default.
The clearest way to understand "one source, two distributions" is to look at a component built for it from the start. Prompt Area is a production-grade contentEditable input purpose-built for prompt-style and chat-composer interfaces, the kind of text box you see in ChatGPT, Claude, Linear, or a Slack composer. It is not a document editor bent into a chat box; it is the input itself, with trigger-based chips for @ mentions and / commands, inline markdown preview, attachments, undo/redo, and a tiny surface area of one component and one hook. Crucially for this discussion, it ships as both a versioned npm package and a shadcn registry entry, from the same codebase.
If you want the npm contract (install it, import it, get updates), you do this:
npm install prompt-area
It ships a self-contained styles.css, so you do not need Tailwind in your project for it to look right. If you happen to run Tailwind and want token-level theming, there is an optional prompt-area/tailwind.css preset, but it is genuinely optional. That detail matters more than it sounds: a lot of "drop-in" components quietly assume your whole Tailwind config, and then the install is not really drop-in at all.
If instead you want the shadcn model, where you copy the source into your repo and own it, you point the shadcn CLI at the registry URL:
npx shadcn@latest add https://prompt-area.com/r/prompt-area.json
Now the component lives in your project. You can read every line, rename a prop, swap the chip styling, or strip a feature you do not need. Because Prompt Area carries zero extra editor dependencies (no ProseMirror, Slate, or Lexical), what lands in your repo is something you can actually read in an afternoon, not a framework you have inherited by accident.
Plenty of projects offer an npm package or a registry entry, maintained separately, drifting apart over time. The version on npm gains a fix; the copy-paste source lags; the two slowly diverge until the docs lie. When both outputs are generated from the same source, the npm consumer and the shadcn consumer are looking at the same component. The choice of distribution becomes a deployment decision, not a fork in the product.
There is now a third audience for component docs, and it does not have hands. Coding agents, the ones wired into editors and CLIs, increasingly install dependencies on a developer's behalf. They read documentation, decide what to add, and run the commands. For that to work, the docs have to be machine-legible.
This is where an llms-full.txt file earns its keep. Prompt Area exposes one, a flattened, full-text description of the component and its API designed to be dropped straight into an LLM's context. An agent can read it, understand that the component installs via either npm install prompt-area or the shadcn registry URL, and pick the path that matches the project it is editing. As "npm vs shadcn" becomes a decision an agent makes, the projects that document both paths in a structured, ingestible way are the ones agents will reliably reach for.
A quick mental model for who consumes what:
If you are the one shipping a component rather than consuming one, the lesson from this pattern is straightforward: do not treat npm and shadcn as rival camps. Treat them as two outputs of one build. Keep the source canonical, generate the registry JSON from it, and publish the npm package from it too. Ship self-contained styles so the package does not silently require a CSS framework. Provide an llms-full.txt so agents are first-class consumers. Then your users get to make the npm-versus-shadcn decision per project, per situation, instead of being forced into your preference.
And if you are consuming, the questions are equally simple. Will I fork this? Do I want free updates? How heavy is its dependency tree? Answer those honestly and the distribution model picks itself. A component like Prompt Area is useful here precisely because it refuses to make you answer them up front. You can start with npm install prompt-area today, and if you later decide you want to own the source, the same component is one shadcn command away.
The distribution debate, in the end, is not really about npm or shadcn. It is about ownership versus maintenance, and the most considerate component authors give you a clean way to switch sides without rewriting anything. That is the bar worth holding new components to.