shadcn/ui as Design Monoculture: Why Half the Web Looks Like the Same Component Library in 2026
shadcn/ui solved a real problem — and accidentally created a new one. By 2026, half the AI-built web shares the same primitives, the same defaults, the same look. A field study and a guide to opt out.
TL;DR
- shadcn/ui is the unintended Bootstrap of 2024-2026. It started as a clever idea — copy unstyled Radix primitives plus Tailwind into your own repo, own the code, customize freely — and it solved a real problem that Material UI, Chakra, Mantine, and a long line of component libraries had failed to fully solve. Credit where due: shadcn deserves a place in the small hall of fame of frontend ideas that actually changed how people ship software.
- The problem is not the project. The problem is the second-order effect. Every LLM trained from late 2023 onward saw thousands of shadcn examples. v0.dev defaults to shadcn. Bolt.new starter templates ship shadcn. Lovable scaffolds shadcn. Cursor's "build me a SaaS" templates use shadcn. Claude Code projects, when asked to "make it look nice", reach for shadcn. The result is a monoculture: a copy-paste primitive library has become a copy-paste *aesthetic* because nobody touches the defaults.
- The fingerprint is now obvious. A
CardwithCardHeaderandCardContent, aButtonwithvariant="outline", slate-50 backgrounds, slate-950 text, Lucide icons, Inter font, a hero with a blue-purple gradient — you can identify a shadcn-default site in under three seconds. Half the new "Show HN" posts in 2026 have the same chrome. AI startups, devtool startups, and fintech demos are the most affected. - The cost is real. Discoverability collapses when every site looks the same. Brand differentiation collapses. Conversion optimization stops working because nobody can A/B test their way out of a shared baseline. Search engines and answer-engine AIs increasingly treat "default-shadcn" as a low-trust pattern because so many of those sites are AI-vibe-coded MVPs that never shipped or were abandoned.
- This piece is a field study and a guide. Part 1: how we got here. Part 2: the fingerprint, with code. Part 3: nine practical ways to use shadcn without producing slop. Part 4: alternatives and a 2027 prediction. Part 5: when to use shadcn anyway, because for some projects it is exactly the right call.
For broader context on the AI-shaped web that produced this monoculture, see /blog/ai-slop-2026-state-of-the-ai-generated-web. For the visual cousin of this problem — the gradient that won the AI-startup hero section — see /blog/tailwind-blue-purple-gradient-ai-signature-2026.
---
What shadcn/ui actually is
shadcn/ui is, in technical terms, three things stacked together:
- A curated set of headless component recipes built on top of Radix UI primitives.
- A Tailwind CSS preset and a small set of CSS custom properties that give those primitives a default visual language.
- A CLI (
npx shadcn add button) that does not install a package — it copies the source files into your project so you own them and can edit them.
That third point is the one that mattered most in 2023-2024 and the one that gets lost in summary articles. shadcn is not a library you install. It is a code-generator that drops files into components/ui/ and walks away. There is no shadcn-ui package in your node_modules providing the Button. There is a Button.tsx file in your repo, your code, your problem, your responsibility. You can rename it, restyle it, delete it, or fork it however you want and the project will not break with the next minor release because there is no minor release — there is just whatever you have on disk.
This is a small idea. It is also, in retrospect, a very large idea. It rearranged the trade-off space that frontend developers had been living inside since at least 2011.
A short tour of where the components actually come from:
- The interaction logic comes from Radix UI. Radix is a separate project (originally by WorkOS, now its own thing) that provides accessible, unstyled, headless primitives for the parts of UI that are genuinely hard: dialogs, popovers, tooltips, focus traps, dropdown menus, comboboxes, sliders, toggle groups. Radix solves the problems that have made every developer cry at some point — keyboard navigation, focus management, ARIA roles, escape-key handling, click-outside detection, scroll locking inside modals.
- The styling layer is Tailwind CSS. Tailwind is a utility-first CSS framework that lets you compose styles by chaining class names directly in markup (
className="rounded-md bg-slate-900 px-4 py-2 text-white hover:bg-slate-800"). Tailwind is opinionated about *how* you style things but not about *what* the result looks like. - The shadcn project itself is the glue between the two. It writes a
Button.tsxthat uses Radix'sSlotprimitive (so the button can render as any element), wraps it withclass-variance-authorityorcvafor variant management, and applies a default Tailwind class set that produces a recognizable but plain visual style.
npx shadcn@latest init
npx shadcn@latest add button card dialog dropdown-menuAfter running those commands, your components/ui/ directory now contains the source for those four components. They are TypeScript files, around 80-200 lines each, that you can read in 10 minutes and modify in 30. There is no documentation site you need to consult about what props a Button accepts — you open Button.tsx and you see the full surface area.
The repo is permissively licensed (MIT). The maintainer, who goes by shadcn, is one person plus a small group of contributors. The project has no funding from a large company in the traditional sense, though Vercel hires the maintainer and ships shadcn-friendly tooling (v0.dev is built around it). It is, in practice, a public good that emerged out of one developer's frustration with existing component libraries.
Released to the public in early 2023, the project was niche through the spring and summer of 2023, blew up in the fall as the React/Next.js community discovered it, and by the end of 2024 had become the de facto starting point for any new TypeScript-React project that needed components. By 2026, shadcn-derived code is in v0.dev outputs by default, in Bolt.new starter templates, in Lovable's "create a SaaS app" flows, in Cursor's project bootstrapping prompts, in Claude Code's standard scaffolds, and in the README of approximately every other GitHub repository that mentions "modern stack."
The current scope is broader than the original launch. shadcn now ships:
| Layer | What's in it | Source / dependency | |-------|--------------|---------------------| | Primitives | Button, Card, Dialog, Drawer, Sheet, Popover, Tooltip, DropdownMenu, ContextMenu, NavigationMenu, Menubar, Tabs, Accordion, Collapsible, ScrollArea | Mostly Radix UI, some custom | | Form | Input, Textarea, Label, Select, Checkbox, RadioGroup, Switch, Slider, Form (with React Hook Form), Calendar (DayPicker) | Mix of Radix, custom, and external libs | | Data | Table, DataTable (with TanStack Table), Pagination, Skeleton | TanStack Table + custom | | Feedback | Alert, AlertDialog, Toast (sonner), Progress | Mix of Radix, sonner, custom | | Layout | Separator, AspectRatio, Resizable, Sidebar (added 2024) | Radix + custom | | Charts | LineChart, BarChart, PieChart, RadarChart wrappers | Recharts | | Theming | dark/light mode, theme builder, color tokens | next-themes + CSS variables | | Tooling | CLI (npx shadcn), block scaffolding, registry support, themes | Custom CLI |
For comparison, here is what the equivalent table looked like in late 2023:
| Layer | What was in it | |-------|----------------| | Primitives | Button, Card, Dialog, Popover, Tooltip, DropdownMenu, Tabs, Accordion | | Form | Input, Label, Select, Checkbox, RadioGroup, Switch, Form | | Feedback | Alert, AlertDialog, Toast |
The expansion since then has been driven by community contributions and by the project picking up patterns that proved out elsewhere (Sidebar from various dashboard templates, the Calendar tied tightly to React DayPicker, the Charts wrappers around Recharts that arrived after Tremor proved the pattern).
The thing that is not in the table — the thing that has done more to shape the look of the web than any single component — is the *example app* that shadcn ships in its docs. There is a dashboard, a marketing landing, a music app, a forms playground, an authentication scaffold. Each is a complete, well-built reference implementation. Each is also, by necessity, opinionated about color, typography, spacing, and layout. And each is, increasingly, the literal answer that LLMs return when you ask them to "build me a dashboard" or "build me a SaaS landing page" because the example apps were what they trained on.
shadcn the project is excellent. shadcn the *default starting point of every AI-generated codebase* is what we are going to talk about for the rest of this article.
---
The problem that shadcn solved
To understand why shadcn won, it helps to walk through the lineage of attempts to solve the same problem and watch each one fail in a slightly different way. The problem is simple to state: how do you give frontend developers a working, accessible, attractive set of UI components without locking them into your visual style or your maintenance schedule?
Bootstrap (2011-onwards): The original answer. Twitter released Bootstrap as a CSS framework with a fixed visual style. You imported one stylesheet, got a navbar, buttons, modals, and a grid system, and shipped. Bootstrap was a genuine revolution because it made it possible for backend engineers to build acceptable-looking sites without a designer. Its problem, famously, was that every Bootstrap site looked like every other Bootstrap site. The "Bootstrap blue" buttons, the rounded corners with the specific border radius, the .well panel — they were instantly recognizable. Bootstrap was the first design monoculture of the post-Web-2.0 era.
Foundation, Bulma, Materialize (2014-2016): Bootstrap competitors that tried to be more flexible or follow Material Design. They suffered the same problem — a fixed visual style, an opinionated default — but with smaller communities and fewer escape hatches.
Material UI (2014-onwards): Google's Material Design implemented as React components. Material UI was technically much more sophisticated than Bootstrap. It supported theming through a theme provider, custom color palettes, density controls. It was also a massive runtime dependency, the styles were emitted via CSS-in-JS (Emotion or styled-components, depending on era), and the components carried their default Material visual identity in ways that resisted customization. You could change the primary color from blue to red. You could not easily make a Material UI site stop looking like a Material UI site without ripping the whole thing out.
Chakra UI (2019-onwards): A response to Material UI's heaviness. Chakra was lighter, more themeable, more "ungoogled" in its defaults, and felt more pleasant to use. Its visual identity was softer and more neutral, but still detectable. Chakra also leaned heavily on a style props API () which meant the component library was doing a lot of work at runtime to translate those props into CSS.
Mantine (2021-onwards): A Chakra-like project that grew rapidly. Mantine was technically excellent, with a huge component set and good defaults. It was also, like Chakra, a runtime CSS-in-JS library, and its visual identity — the specific input styling, the specific shadow stack — was identifiable. Mantine's documentation site itself was the most aspirational target many developers had ever seen.
Headless UI (2020-onwards): Tailwind Labs' attempt at a different category. Headless UI shipped only the *behavior* of components — Dialog, Disclosure, Menu, Listbox — and let you bring your own styles. This was the right idea, but Headless UI was small in scope (eight or so components) and required a lot of work to build a complete app on top of.
Radix UI (2021-onwards): A more ambitious headless library. Radix shipped accessible primitives for almost every interactive UI pattern, with no visual styling at all. Radix solved the *behavioral* problem — focus traps, ARIA, keyboard nav — completely and well. It did not solve the styling problem because that was not its job. To use Radix in a real app you had to either style every primitive yourself from scratch, which was a lot of work, or find a project that had done that work for you.
Tailwind CSS (2017-onwards): Adam Wathan's utility-first CSS framework. Tailwind was a complete revolution in how to write styles, separate from the component library question. By 2022 it was the default styling approach for new React projects. It pairs brilliantly with headless components because you can style a Radix primitive with Tailwind classes inline without writing any CSS files.
The gap: By late 2022, the trade-off space looked like this. You could use a fully styled component library (Material UI, Chakra, Mantine) and accept the visual identity and the runtime cost. You could use a headless library (Radix, Headless UI) and write all the styling yourself, which meant a real designer or weeks of work. You could use Tailwind plus a headless library and assemble your own components, which was the right answer if you had time. Or you could use a starter template, which was usually opinionated in a way that made it hard to evolve.
The thing that was missing was a project that gave you Radix-style headless primitives plus Tailwind styling plus a complete set of components — but as *source code in your repo* rather than as a dependency. Source code you could edit. Source code that became your code the moment you copied it.
That is what shadcn did. The maintainer wrote a CLI that takes a Radix primitive, wraps it with cva for variant management, applies a careful default Tailwind class set that produces a recognizable-but-neutral visual style, and copies the result into your repo. You own the file. If you want to change the button's hover state, you edit Button.tsx. If you want to add a new variant, you add it. If you want to delete the variant system entirely and just use plain Tailwind classes wherever you want a button, you can do that too.
This is, in retrospect, the obvious answer. Every previous attempt either gave you a styled black-box (you didn't own it) or gave you a headless skeleton (you had to do the work). shadcn gave you styled source code you owned outright.
The reason it took until 2023 to ship is that the prerequisites had to land first. Tailwind needed to be the default styling approach. Radix needed to exist and be mature. Vercel/Next.js needed to popularize the App Router and React Server Components, which made the "all components live in your repo as TSX files" pattern feel native rather than weird. The CLI tooling for code generation needed to be normal. By the time shadcn showed up, every piece of the puzzle was in place — and a single developer could glue them together in a way that felt obvious in hindsight.
It also won because the *philosophy* of "you own this code" matched the moment. After a decade of dependency hell — node_modules at 1GB, supply chain attacks, libraries abandoned mid-version, breaking changes between minor versions, mysterious peer dependency warnings — developers were tired. The pitch "we will give you the code, you will own it, we will not break your build with our next release because there is no release" was emotionally as well as technically correct.
For a parallel discussion of why "AI-built default" became a recognizable pattern across the entire stack, see /blog/from-ai-slop-to-signature-73-patterns-2026.
---
The accidental monoculture
Here is the part that nobody planned for.
Between mid-2023 and the end of 2024, three things happened in close enough sequence that the second-order effects compounded:
- shadcn became the default React component library in the developer mindshare. By Q4 2024, asking a senior frontend developer "what should I use" returned "shadcn" with very high probability.
- AI coding assistants exploded in capability and adoption. ChatGPT got better at writing code. Claude shipped models that could write multi-file React projects from a single prompt. Cursor became standard. Bolt.new and Lovable and v0.dev launched and took meaningful market share among non-developer founders. By the end of 2024, "vibe coding" — describing what you want and letting an AI build it — was a mainstream activity.
- Those AI tools were trained on shadcn. The training data for every major model included GitHub. shadcn was on GitHub. So were the example apps. So were the thousands of derivative repos that copied the example apps. So were the Stack Overflow answers that referenced shadcn. So were the YouTube tutorials. So were the documentation sites.
The compounding effect is straightforward. An AI tool generates code based on patterns in its training data. The most common pattern in its training data for "build me a React component" was shadcn. So the AI tool generated shadcn. The output of that AI tool went into a new repo. That new repo went on GitHub or got copied into a Bolt project or shipped to a Vercel deploy URL. The next round of training data included that new repo. The pattern reinforced itself.
By 2026, the situation looks like this:
- v0.dev generates shadcn by default. It is a Vercel product, the team works closely with shadcn, and the entire UI generation flow is built around shadcn primitives. If you ask v0 for a "modern dashboard" you get a shadcn dashboard. The defaults are not customized. The result is the example app from shadcn's docs with different copy.
- Bolt.new scaffolds Vite + React + Tailwind + shadcn for any project that involves UI. The starter template is straight from the shadcn example.
- Lovable ships shadcn under the hood for its TypeScript outputs. The output looks "professional" because shadcn's defaults are tasteful.
- Cursor, when given a generic prompt like "create a SaaS landing page in Next.js", reaches for shadcn because that is the highest-probability completion. The same is true of Claude Code, GitHub Copilot, and Continue.
- Show HN posts in 2026 are dominated by shadcn-default sites. A casual count of the top 50 Show HN posts on any given week shows around 30-40 of them have the shadcn fingerprint visible from the screenshot alone.
- Indie Hackers product launches, Product Hunt launches, Hacker News Show HN — the same. The visual baseline of a 2026 startup launch is now "shadcn defaults plus a hero gradient."
A useful mental model: shadcn is to the AI-built web what Bootstrap was to the 2012 startup web, but multiplied by an order of magnitude because LLMs aggressively converge on the most-frequent training pattern. Where Bootstrap's monoculture happened slowly through community choice, shadcn's monoculture happened fast because the community choice was being made *by training data*, and the training data was already shadcn-heavy by the time most non-developers started building anything.
Here is what a rough adoption curve looks like, drawn from a mix of npm download counters for class-variance-authority (a strong proxy for shadcn projects), GitHub star growth, and observational counts of "Show HN" sites with the fingerprint:
ASCII chart: shadcn adoption / footprint, 2023-2026 (rough, qualitative)
100% | ###################
| #######
share | #######
of new | ######
React | ######
projects| ######
using | ######
shadcn | #####
| ##
0% |##
+---------------------------------------------------------------
Q1 Q2 Q3 Q4 Q1 Q2 Q3 Q4 Q1 Q2 Q3 Q4
2023 2024 2025 2026
Note: this is qualitative, not measured. The shape (slow rise in 2023,
rapid adoption through 2024, plateau in 2025-2026 around 50-70% of new
React projects) is consistent with npm trends for related packages.The plateau is interesting. shadcn does not have 100% market share, and never will. There is a long tail of Material UI hold-outs (especially in enterprise), a large Mantine community, a Chakra base, a "we wrote our own" cohort, and the projects that use Vue/Svelte/Solid where shadcn ports exist but are less central. But for the specific category of "new React/Next.js project, built or scaffolded with AI assistance, in 2024-2026," the share is high enough that defaults are the norm.
Two specific clusters where the share is closest to total:
- AI startup landing pages. Almost any company whose product involves an LLM, was founded in 2023-2025, raised a seed round, and built a marketing site has a shadcn-default site. The hero has a gradient. The pricing table has three tiers with the middle one outlined. The CTA is a
Buttonwithsize="lg". The icons are from Lucide. The spacing is tasteful and identical to twenty other AI startup sites. Sometimes the only differentiation is the company name. - Devtool startups. Same story. The audience for devtools is developers who themselves use shadcn, so the team feels at home using it. The result is a sea of devtool sites that are visually indistinguishable from each other.
The sites that escaped the monoculture are mostly:
- Companies founded before 2023, who already had design systems.
- Companies with in-house designers who pushed back.
- Companies in industries where visual differentiation is part of the product (gaming, fashion, design tools, music software).
- Companies where the founder has strong opinions about visual identity and overrode the AI defaults.
That last category is small, and shrinking, because each generation of AI tooling makes it slightly easier to ship the default and slightly harder to motivate the customization work. If the AI ships you a passable site in five minutes, the marginal cost of "let's make it ours" feels higher than it used to, even though the absolute work is the same.
---
The fingerprint of an unstyled-but-default shadcn site
Here is the recognition pattern. If you have stared at enough websites in 2024-2026, you can identify a default-shadcn site in under three seconds. The fingerprint is composed of about a dozen consistent signals.
| Signal | What it looks like in code | What it looks like to a viewer | |--------|----------------------------|--------------------------------| | The cn() utility | import { cn } from "@/lib/utils" everywhere | (invisible — but proves shadcn origin) | | Button outline variant | on the secondary CTA | A button with a thin border, slate-200, no fill, hover bg-slate-100 | | Card primitive | | A rounded rectangle with border, padding, slate-50/white background, soft shadow | | Dialog animation | with the default fade-in-zoom | Modal that fades in over a black/40 overlay, content scales from 95% to 100% | | Tailwind preset | slate-50 to slate-950 palette, --background, --foreground CSS vars | Cool grayscale, slightly blue-tinged, never warm | | Lucide icons | import { ChevronRight, Check, X } from "lucide-react" | Thin-stroke line icons, 24px default, that specific Lucide weight | | Inter font | font-sans mapped to Inter via the standard Google Fonts import | Inter everywhere, no font pairing | | Hero gradient | bg-gradient-to-r from-blue-600 to-violet-600 (or similar) | The blue→purple AI-startup hero gradient | | Section structure | | Centered max-width 1280px, generous vertical padding | | Feature grid | Three or four components in a grid grid-cols-3 | The "feature trio" or "feature quad" with icon, title, description | | Pricing tiers | Three Card components, middle one has border-primary | The classic three-tier pricing table with the middle one highlighted | | Footer | with Lucide social icons | Thin top border, four columns of links, copyright on the bottom |
Let's go through them in detail.
The cn() utility. shadcn ships a small utility in @/lib/utils.ts:
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}This combines clsx (for conditional class composition) and tailwind-merge (for resolving conflicting Tailwind classes — if you pass both p-4 and p-6, the later one wins). Every shadcn component uses cn() in its className prop. If you grep a codebase for import { cn }, the result is a near-perfect detector of shadcn presence.
The Button with variant="outline". Look at any shadcn-default site's pricing page or hero. The primary CTA is (default variant, solid fill). The secondary is — a thin border, no fill, hover state of bg-accent which by default is slate-100. This pair, side-by-side, is one of the most repeated visual elements in the AI-built web.
The Card with the standard sub-components. shadcn's Card has a specific structure:
<Card>
<CardHeader>
<CardTitle>Feature name</CardTitle>
<CardDescription>One-line description.</CardDescription>
</CardHeader>
<CardContent>
<p>Body content here.</p>
</CardContent>
<CardFooter>
<Button>Call to action</Button>
</CardFooter>
</Card>This composition pattern is everywhere. The visual result is a rounded rectangle with border (slate-200), rounded-lg, bg-card (white), text-card-foreground (slate-950), padding of 24px, with a gap-1.5 between the title and description. If you have seen one, you have seen them all.
The Dialog animation. The default Dialog component animates in with a Tailwind animate plugin:
data-[state=open]:animate-in
data-[state=closed]:animate-out
data-[state=closed]:fade-out-0
data-[state=open]:fade-in-0
data-[state=closed]:zoom-out-95
data-[state=open]:zoom-in-95The result is a fade-in-zoom-up animation over a black/40 backdrop. Every shadcn dialog opens the same way. If you watch carefully, you can recognize the timing curve.
The Tailwind preset and CSS variables. shadcn sets up a CSS variable theme where every color is referenced through --background, --foreground, --card, --card-foreground, --primary, --primary-foreground, --secondary, --accent, --destructive, --muted, --border, --input, --ring. By default these resolve to slate-based grays with a slight blue tint. The dark mode flip is slate-950 background with slate-50 text. Every shadcn site uses the same palette unless someone went in and changed the variables — which most don't.
Lucide icons. shadcn's docs use Lucide everywhere. Every example component that needs an icon imports from lucide-react. The Lucide style is thin-stroke (1.5px or 2px), rounded line caps, 24px default size, with a very specific geometric flavor. Once you can recognize Lucide, you see it on every other website.
Inter font. The default Tailwind config that shadcn ships maps font-sans to Inter via the next/font Google Fonts integration. Inter is a great font. It is also so common in 2024-2026 that "Inter font everywhere" is itself a slop signal. If you visit ten AI-startup sites in a row, nine of them are in Inter.
Hero gradient. Not strictly shadcn — shadcn doesn't ship a hero. But the example landing page in the docs uses a blue-to-violet gradient on the hero text or background, and so do most of the derivatives. This is the same gradient pattern discussed in detail at /blog/tailwind-blue-purple-gradient-ai-signature-2026.
Section structure. The classic "marketing page section" in shadcn-derived code is:
<section className="container mx-auto py-24 px-4">
<div className="max-w-3xl mx-auto text-center">
<h2 className="text-4xl font-bold tracking-tight">Section title</h2>
<p className="mt-4 text-lg text-muted-foreground">Section description.</p>
</div>
<div className="mt-16 grid grid-cols-1 md:grid-cols-3 gap-8">
{/* feature cards */}
</div>
</section>container mx-auto py-24 px-4 is in approximately every shadcn-derived marketing site. tracking-tight on the heading. text-muted-foreground on the subhead. mt-16 for the gap between intro and grid. Once you can recognize this, you can recognize it on autopilot.
Feature grid. Three or four cards in a grid, each with an icon, a title, a description. The icons are Lucide. The cards are . The grid is grid grid-cols-1 md:grid-cols-3 gap-8 or gap-6. Variations exist (icon at the top, icon to the left, two columns instead of three) but the underlying template is the same.
Pricing table. Three columns. Middle one outlined or marked "Most Popular." Big price number. Feature list with Lucide checkmarks. CTA button at the bottom. This pattern is older than shadcn (it goes back to 2014-era SaaS), but shadcn standardized the implementation.
Footer. . Four columns: Product, Company, Resources, Legal. Lucide social icons (GitHub, Twitter, LinkedIn — Lucide has them). Copyright notice on the bottom.
Put all of this together and you have a fingerprint that is recognizable, repeatable, and now, as of 2026, *commodity*. There is no shame in shipping a site with this fingerprint — for an MVP, an internal tool, or a quick test, it's fine. The problem is when you wanted differentiation and you didn't get it because nobody told you the defaults are now the same as everybody else's.
For the parallel pattern of "default colors and gradients as AI signature," see /blog/tailwind-blue-purple-gradient-ai-signature-2026. For practical de-AI-ification of a Lovable, v0, or Bolt site — including stripping out shadcn defaults — see /blog/de-ai-your-lovable-v0-bolt-site.
---
Why this isn't shadcn's fault
Before going further, an important point: this is not shadcn's fault.
The project's stated philosophy is, on every page of its documentation, some version of "you own this code, customize it." The maintainer is explicit that the components are starting points, not finished design systems. The defaults exist because some defaults are needed — a button has to look like *something* when you copy it in — and the chosen defaults are deliberately neutral, restrained, and easy to override.
The shadcn docs literally include a section called "Theming" that walks you through changing the CSS variables to produce a different color palette. There is a themes page where you can pick from pre-built themes. There is a colors documentation that shows all the tokens. There are examples of customized components throughout. The maintainer has written and tweeted extensively about how the components are meant to be a starting point.
The problem is that nobody customizes. Or rather, the people who *would* customize already had design systems and didn't need shadcn. The people who *use* shadcn did so because they didn't want to build a design system, and the defaults were good enough to ship.
This is the classic "happy path" problem. shadcn's happy path is "install the CLI, run npx shadcn add button, paste it into your page, ship." Every step works perfectly. The result looks fine. There is no point at which the developer is forced to make a design decision. There is no friction that says "wait, you should probably set up your color tokens before going further." The path of least resistance produces the default.
LLMs make this worse, not better, in two ways:
- LLMs aggressively reinforce the happy path. When you ask an LLM to add a button to your shadcn site, it generates
. It does not, unprompted, suggest "have you considered customizing your variant system?" It generates the most common pattern, which is the default. The LLM is correct that this is what most code does. The LLM is also propagating the monoculture by doing so. - LLMs make the cost of customization disproportionately higher. If you ask an LLM to "give me a custom button system that doesn't use shadcn defaults," you can do it, but the LLM is now off-distribution. The output is less reliable, more likely to have bugs, harder to verify. The path of friction-plus-uncertainty is exactly the wrong incentive structure for getting people to leave the default behind.
A better way to phrase the problem: shadcn solved the *original* problem perfectly. The original problem was "I need a working component library that I can own and customize." That problem is solved. The new problem is "the world now contains so many people who took shadcn's defaults and shipped them that the defaults are themselves a design choice with negative externalities." Solving the new problem is not shadcn's job. It is the job of whoever builds the next layer on top.
There are several ways the new problem could be addressed:
- shadcn ships more example archetypes. Currently the docs have one or two example sites that are visually similar. If shadcn shipped five visually distinct example sites — a brutalist one, a soft-rounded one, a dense data-tooling one, a marketing-glossy one, a terminal one — the AI training data would be more diverse and the AI tools would generate more varied outputs.
- AI tools de-prioritize the default. v0.dev, Bolt, Lovable, and others could deliberately introduce variation by sampling from a wider set of visual patterns rather than always pulling toward shadcn's default. This requires those tools to actively choose against the highest-probability completion, which is a product decision.
- The community produces themed wrappers. Park UI, Tweakcn, and others are doing this — taking shadcn's structure and re-themeing it visually. As these get more adopted, the monoculture fragments.
- Designers re-enter the loop. Vibe coding without a designer produces visual sameness because the AI is averaging over training data. A designer in the loop produces difference. The market may correct toward "designed" sites being a paid premium over "AI-vibe-coded" sites.
None of these are shadcn's responsibility. The project did its job. The ecosystem is now responsible for not letting the default eat the world.
---
Stats and observations
Hard numbers in this space are difficult to come by, and we should be skeptical of overly precise claims. What follows is a mix of npm-based proxies, GitHub-based observations, and qualitative pattern matching across the AI-built-web. Treat all percentages as rough order-of-magnitude estimates.
Adoption proxies
class-variance-authority(cva), the variant-management library shadcn uses, has tens of millions of weekly npm downloads as of early 2026, up from near zero in 2022. Not all cva usage is shadcn, but a large majority is.tailwind-merge, shipped with shadcn'scn()helper, similarly has very high download counts that grew in lockstep with shadcn adoption.- Lucide's
lucide-reactpackage has had a similar growth curve, also driven heavily by shadcn defaults. - The shadcn GitHub repo has tens of thousands of stars and has been forked many thousands of times. Forks are imperfect proxies, but the magnitude is meaningful.
Industry concentration
If you sample new sites by industry, the fraction with a shadcn fingerprint varies dramatically:
| Industry / category | Approximate shadcn-default rate | |---------------------|-------------------------------| | AI startups (Series A and earlier) | High — most of them | | Devtool startups | High | | Fintech B2B (early stage) | Moderate-to-high | | YC W24/S24/W25 batches | Moderate-to-high | | Indie SaaS launched in 2024-2026 | Moderate | | Enterprise B2B SaaS | Lower (often Material UI legacy) | | E-commerce | Lower (Shopify themes, custom brands) | | Gaming, music, fashion | Low | | Government, healthcare, education | Very low | | Brand-driven consumer | Very low |
The pattern is what you'd expect: industries where developers themselves are the audience, where the founder is technical, and where the product was scaffolded quickly with AI tooling have the highest shadcn rates. Industries with in-house design teams, brand identity demands, or different platform constraints have lower rates.
Geographic clustering
Less expected: there is a geographic tilt. Sites built by developers in Western tech hubs (San Francisco, New York, London, Berlin, Tel Aviv) are more likely to be shadcn-default than sites built elsewhere. This is partly because the AI-coding-tool adoption rates are higher in those hubs, partly because the YC/HN/Twitter feedback loop runs through them, and partly because non-English-language ecosystems have their own component library traditions and reach for shadcn less reflexively.
The Show HN effect
A persistent observation: a site that gets posted to Show HN in 2026 has roughly even odds of being shadcn-default. A non-trivial fraction of the comments on those posts mention something to the effect of "another shadcn site" — sometimes critically, sometimes neutrally, but the recognition is now standard.
The conversion-rate evidence
Among AI-startup landing pages that share the shadcn fingerprint, conversion rates have converged toward a narrow band. Multiple founders have reported that their landing pages convert similarly to peers' regardless of copy, because the visual delivery is so similar that the message is doing more work than the visual. This is anecdotal, but the pattern is consistent enough across founder communities that it is likely real.
The search engine angle
Anecdotally, search engines and answer-engine AIs (Google AI Overviews, Perplexity, ChatGPT Search) increasingly seem to discount low-trust signals associated with default AI-built sites. A site with no real backlinks, default shadcn styling, AI-generated copy, and a domain registered in the last six months is the modal "skip this result" candidate. shadcn defaults are not the cause — they are a co-occurring signal — but the correlation is unhelpful for legitimate sites that happen to use defaults.
The training-data feedback loop
The clearest sign that we are inside a feedback loop: AI tools in 2026 generate shadcn even when the user does not ask for it. Tell Bolt or Lovable "build me a marketing site" with no other guidance, and shadcn comes out. Tell v0 "give me a dashboard," shadcn dashboard. The defaults are now baked into the generation step itself, not just into the framework. This is the technical mechanism by which the monoculture sustains.
---
The cost of monoculture
Why does any of this matter? "Lots of sites look similar" is not, in itself, a moral failing. Bootstrap had a similar moment, and the web survived. Material Design proliferated and the web survived that, too. Why is this different?
It is different in degree, not kind, but the degree matters. Here are the specific costs.
Discoverability collapse. When every new SaaS site looks like every other new SaaS site, users develop blindness. The first few times you see the shadcn pattern, it reads as "modern, professional." The hundredth time, it reads as "wallpaper." Users stop looking, stop registering brand, stop remembering. The cost of breaking through gets higher because the baseline is shared.
Brand differentiation collapse. A startup's website is, for many of them, the entirety of their brand expression in the early days. If the website is the same as twenty competitors', the brand is unclaimed. This is fine if you are competing on product or distribution and the website is just a placeholder. It is not fine if you wanted the website to do work.
Conversion data converging. As noted above, A/B tests on shadcn-default sites tend to produce small effects because the copy is doing all the work and the visual is already optimized at the default. This is not a bad thing per se — shadcn's defaults *are* good — but it means founders cannot meaningfully differentiate via the visual layer, which historically was a real lever.
Search engine signal collapse. Search engines used to be able to use visual fingerprinting (indirectly, via the site's overall trust signals) as one input into ranking. When all new sites have the same visual fingerprint, that signal is useless. The remaining signals (backlinks, age, authoritative content, real engagement) become more dominant. This is fine for established sites, harder for new sites that previously could differentiate via visual quality.
Ad network friction. Ad networks like Google Ads and Meta Ads have algorithms that score landing page quality in part on user-signal proxies. When a new domain with default shadcn styling and minimal real engagement applies to advertise, the algorithm increasingly treats it as low-trust by default. This is anecdotal but multiple advertisers have reported the pattern.
The investor pattern recognition. Investors who look at hundreds of pitch decks and websites a year develop strong pattern recognition. By 2026, the meta-pattern "default-shadcn site, default v0-style hero, default pricing card with three tiers" reads to many investors as "AI-vibe-coded, founder did not invest in differentiation." This is sometimes correct and sometimes unfair — but the perception affects fundability for early-stage companies whose first impression is the website.
The user trust angle. Users are starting to develop their own pattern recognition. The shadcn fingerprint, especially when paired with AI-generated copy, increasingly reads to discerning users as "lower-trust startup, possibly half-baked, possibly a wrapper around an LLM API." This is unfair to legitimate startups using shadcn well. It is also a real perception that affects user behavior.
The accessibility angle. shadcn's defaults are accessible because Radix is accessible. This is genuinely a positive — the floor is high. But because so many shadcn sites are AI-vibe-coded by people who do not test accessibility themselves, there are common patterns that break (color contrast tweaks that fail WCAG, custom variants that strip ARIA attributes, components used wrong) and the user community is increasingly aware that "looks like shadcn" does not mean "works for screen readers." The reputational halo of accessibility has dimmed.
The "everything is a thin wrapper" perception. There is a category of 2025-2026 startup that is genuinely a thin wrapper around an LLM: a prompt, a UI, a Stripe checkout. These often use shadcn defaults because the founder did not invest in differentiation. As the category gets criticized, the visual signal gets associated with the category. By correlation, even legitimate non-wrapper startups using shadcn defaults get tarred with the same brush.
The cost is not catastrophic. The web is not collapsing. shadcn is not the root of all problems. But the cumulative effect of monoculture is real, and it disproportionately hurts the sites that needed differentiation most: small, new, brand-dependent, design-aware companies.
---
How to use shadcn without producing slop
The good news: shadcn is, as the maintainer designed it, deeply customizable. You own the source. You can change anything. The bad news is that "deeply customizable" is not the same as "customized by default," and most projects ship the default.
What follows is a practical guide to using shadcn without producing slop. The principle is simple — touch the defaults before you ship them — but the execution matters. Each step is small, takes minutes, and compounds with the others into a site that uses shadcn primitives without looking shadcn-default.
9.1 Replace the shadcn Tailwind preset with your own scale
The single highest-leverage change. shadcn ships a default color scale (slate-based, with the standard --background, --foreground, --primary, --secondary, --accent, --muted, --destructive, --border, --input, --ring CSS variables). Because everyone uses this scale, every shadcn site has the same color baseline.
The fix: replace it with your own scale. Pick eight to twelve colors that are *yours*. They should map to the same CSS variable names so that shadcn components keep working, but the values should be different.
Example. Default shadcn (in globals.css):
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--border: 214.3 31.8% 91.4%;
...
}Custom replacement:
:root {
/* warm, paper-ish baseline */
--background: 38 30% 97%;
--foreground: 28 25% 15%;
/* primary: deep teal, not navy */
--primary: 178 52% 22%;
--primary-foreground: 38 30% 97%;
/* muted: warm cream */
--muted: 38 25% 90%;
--muted-foreground: 28 15% 40%;
/* border: warm stone, not cool gray */
--border: 28 18% 82%;
...
}The result: every shadcn component now picks up your colors. The Card has a warm cream background. The Button outline has a warm stone border. The text is warm ink, not blue-tinged slate. You did not change any component code — you changed eight CSS variables, and the entire surface of the app shifted away from the default.
Spend an hour with a color tool building your scale before you write any other code. This is the single best investment for differentiating from default.
9.2 Override default `cn()` patterns with named CSS variables
A more advanced technique. The cn() utility makes it easy to compose Tailwind classes inline, which is great for prototyping but bad for consistency. By the time you have twenty buttons, the same shade of "almost-blue" is being expressed as bg-blue-500, bg-blue-600, bg-primary, and bg-[#3458d4] in different places.
The fix: define your design tokens as named CSS variables and reference them by name throughout the app, not as Tailwind shades. This forces consistency and makes future changes propagate.
:root {
--color-brand-50: 178 60% 95%;
--color-brand-500: 178 52% 22%;
--color-brand-900: 178 60% 8%;
--color-paper: 38 30% 97%;
--color-ink: 28 25% 15%;
--color-graphite: 28 18% 32%;
--shadow-card: 0 1px 2px hsl(28 25% 15% / 0.06), 0 4px 12px hsl(28 25% 15% / 0.04);
--radius-card: 6px;
--radius-pill: 999px;
}In your Tailwind config, expose these:
theme: {
extend: {
colors: {
brand: {
50: "hsl(var(--color-brand-50))",
500: "hsl(var(--color-brand-500))",
900: "hsl(var(--color-brand-900))",
},
paper: "hsl(var(--color-paper))",
ink: "hsl(var(--color-ink))",
graphite: "hsl(var(--color-graphite))",
},
boxShadow: {
card: "var(--shadow-card)",
},
borderRadius: {
card: "var(--radius-card)",
pill: "var(--radius-pill)",
},
},
},Now bg-brand-500, text-ink, shadow-card, rounded-card are your vocabulary. Anyone reading the codebase sees brand intent, not raw color codes. Future redesigns are a config change, not a search-and-replace.
9.3 Don't use `<Card>` and `<CardHeader>` — write your own primitives
This is the most contentious step but the most differentiating. shadcn's Card is generic. It assumes you want a rounded rectangle with header, content, and footer. That assumption fits a lot of cases. It also means every shadcn site has the same generic card.
Your domain has more specific primitives. A pricing card is not a generic card — it has a tier name, a price, a feature list, and a CTA. A feature card has an icon, a title, a description. A testimonial has a quote, an author, a company. Naming them generically as and composing with makes them blend together.
The fix: write your own primitives that match your domain. Not as a *replacement* for shadcn, but as a *layer above* it.
Before:
<Card>
<CardHeader>
<CardTitle>Starter</CardTitle>
<CardDescription>For solo developers</CardDescription>
</CardHeader>
<CardContent>
<div className="text-4xl font-bold">$29</div>
<ul>
<li><Check className="h-4 w-4" /> Up to 3 projects</li>
<li><Check className="h-4 w-4" /> Email support</li>
</ul>
</CardContent>
<CardFooter>
<Button>Choose Starter</Button>
</CardFooter>
</Card>After:
<PricingTier
name="Starter"
audience="For solo developers"
price={29}
cadence="per month"
features={["Up to 3 projects", "Email support"]}
cta="Choose Starter"
variant="standard"
/>The PricingTier component is *your* component, written once, in your repo. Internally it can use shadcn's Button, your own custom layout, your own typography decisions. Externally, it matches your domain. When you want to change every pricing card on the site, you change one component. When you read the call site, you see "this is a pricing tier" not "this is a generic card with stuff inside."
This is the level at which differentiation lives. The shadcn primitives are tools. Your domain primitives are the building blocks that make your site look like *you* instead of a generic shadcn site.
9.4 Replace Lucide with a custom SVG set
Lucide is fine. Lucide is everywhere. Lucide is, at this point, a slop signal. Every shadcn-default site uses Lucide icons in their default thin-stroke 1.5px weight, sized at 16px or 24px, in text-muted-foreground color.
The fix: don't use Lucide. Or, if you do use Lucide, override its defaults.
Options:
- Custom SVG set. Hire an illustrator or use one of the dozens of paid icon sets (Phosphor, Iconoir, Tabler, Heroicons, Streamline). Each has a different visual flavor. A site using Phosphor's regular weight looks markedly different from a site using Lucide. Tabler has a friendlier vibe. Heroicons (Tailwind Labs' set) are a specific cleaner aesthetic.
- Custom-drawn icons. For a small site, you only need 12-20 icons. Drawing them yourself, even rough, gives you something nobody else has. Use Figma or Penpot, export as SVG, drop them into a
component. - Mixed weight. Even staying with Lucide, change the default. Use 1px stroke for a more delicate feel, or 2.5px stroke for a heavier modernist feel. Use 32px sizing instead of 24px. Use a different color than
text-muted-foreground.
A custom icon component:
// components/Icon.tsx
import * as React from "react"
import { cn } from "@/lib/utils"
type IconName = "check" | "arrow-right" | "spark" | "shield" | "circle"
const paths: Record<IconName, React.ReactNode> = {
"check": <path d="M4 12.5l4.5 4L20 6" />,
"arrow-right": <path d="M5 12h14M13 5l7 7-7 7" />,
"spark": <path d="M12 3v6M12 15v6M3 12h6M15 12h6M5.6 5.6l4.2 4.2M14.2 14.2l4.2 4.2M5.6 18.4l4.2-4.2M14.2 9.8l4.2-4.2" />,
"shield": <path d="M12 3l8 3v6c0 5-3.5 8-8 9-4.5-1-8-4-8-9V6l8-3z" />,
"circle": <circle cx="12" cy="12" r="9" />,
}
export function Icon({
name,
size = 20,
className,
...rest
}: {
name: IconName
size?: number
className?: string
} & React.SVGAttributes<SVGSVGElement>) {
return (
<svg
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={1.75}
strokeLinecap="round"
strokeLinejoin="round"
className={cn("inline-block", className)}
{...rest}
>
{paths[name]}
</svg>
)
}The 1.75px stroke is unusual (Lucide is 2 by default; Phosphor's regular is 1.5). The set is small (you add icons as you need them, not as a 1000-icon library). The component is yours and can be styled or animated as needed.
9.5 Replace Inter with a duo
Inter is the world's most overused sans-serif font in 2026. It is genuinely well-designed. It is also a slop signal.
The fix: replace it. Or pair it with something that owns the visual identity.
Three approaches, in order of effort:
- Single replacement. Use a different sans for body. Geist (Vercel's font) is similar to Inter but slightly different. IBM Plex Sans has a more humanist feel. Atkinson Hyperlegible has accessibility credentials. Manrope has a softer, friendlier flavor. Public Sans is more institutional. Any of these is one Google Fonts swap and you are out of the Inter monoculture.
- Duo (recommended). Use a serif for headlines, a sans for body. The serif/sans pair instantly differentiates from default shadcn (which is sans/sans). Examples that work well:
- Playfair Display + Inter - Fraunces + Manrope - DM Serif Display + DM Sans (matched family pair) - Crimson Pro + IBM Plex Sans - GT Sectra + GT America (paid, but instantly recognizable) - Newsreader + Geist (free, modern feel)
- Trio. Add a monospace for code, captions, or accent. JetBrains Mono, Fira Code, IBM Plex Mono, Geist Mono, Berkeley Mono (paid but distinctive). The mono-as-accent pattern (used for taglines, eyebrow text, badges) is one of the most reliable ways to add visual identity without major redesign work.
Tailwind config for a duo + mono setup:
theme: {
extend: {
fontFamily: {
sans: ["var(--font-body)", ...defaultTheme.fontFamily.sans],
display: ["var(--font-display)", ...defaultTheme.fontFamily.serif],
mono: ["var(--font-mono)", ...defaultTheme.fontFamily.mono],
},
},
},Then in your layout:
import { Newsreader, Manrope, JetBrains_Mono } from "next/font/google"
const display = Newsreader({ subsets: ["latin"], weight: ["400","500","600"], variable: "--font-display" })
const body = Manrope ({ subsets: ["latin"], weight: ["400","500","600","700"], variable: "--font-body" })
const mono = JetBrains_Mono({ subsets: ["latin"], weight: ["400","500"], variable: "--font-mono" })
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={`${display.variable} ${body.variable} ${mono.variable}`}>
<body className="font-sans">{children}</body>
</html>
)
}Headlines now use font-display. Body uses font-sans (which is Manrope, not Inter). Code and accents use font-mono. The site instantly stops looking shadcn-default.
9.6 Customize each Radix primitive's defaults
The default Dialog animation, the default DropdownMenu placement, the default Tooltip delay — these are individually small choices, collectively a fingerprint. Override them.
Dialog. The default fade-in-zoom-up over a black/40 backdrop is the most-recognized shadcn animation. Replace it. Options:
- Slide up from the bottom (more app-like, less marketing-modal).
- Snap in with no fade (more terminal/brutalist).
- Slide from the side as a sheet (use
Sheetinstead ofDialog). - Custom keyframe with a specific spring or ease curve.
// In components/ui/dialog.tsx, modify the DialogContent class:
const DialogContent = React.forwardRef(...)((props, ref) => (
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[100%] translate-x-[-50%]",
"data-[state=open]:top-[50%] data-[state=open]:translate-y-[-50%]",
"transition-all duration-300 ease-[cubic-bezier(0.16,1,0.3,1)]",
"data-[state=closed]:opacity-0 data-[state=open]:opacity-100",
...
)}
>
{children}
</DialogPrimitive.Content>
</DialogPortal>
))The result is a dialog that slides up from below the viewport on open instead of fading in. Different. Recognizable as your dialog, not generic.
DropdownMenu. Default placement is bottom-end with an offset. Try bottom-start for left-anchored menus, or right-start if your design is sidebar-heavy. The default sideOffset={4} is a common giveaway — change to 8, 12, or 0 (touching).
Tooltip. The default delay is 700ms before appearing. This feels generic. Set it to 200ms for a snappier feel, or 1200ms for a more deliberate one. The default arrow is shown — try without arrow for a cleaner feel, or with a larger arrow for a more cartoonish one.
Toast. shadcn defaults to using sonner for toasts (a separate library). Sonner has its own defaults. Override the position (default is bottom-right; try top-center or top-right), the duration (default 4000ms; try 2500 for snappier or 6000 for more dwell), and the visual (default has a subtle background; try a high-contrast slab for impact).
These changes are small individually. Together they make every interaction feel like *yours* instead of generic shadcn.
9.7 Build your own variant system (cva or otherwise)
shadcn ships variants like default, secondary, outline, ghost, link, destructive for Button. These are fine starting points. They are also the variants every shadcn site uses.
The fix: rebuild the variant system to match your design intent.
Generic shadcn:
const buttonVariants = cva("...", {
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
outline: "border border-input bg-background hover:bg-accent",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 px-3",
lg: "h-11 px-8",
icon: "h-10 w-10",
},
},
})Custom domain-specific:
const buttonVariants = cva("...", {
variants: {
intent: {
// CTAs that move the user forward
action: "bg-brand-500 text-paper hover:bg-brand-900 shadow-card hover:shadow-card-lg",
// Secondary, supportive
assist: "border border-graphite text-ink bg-transparent hover:bg-paper-shade",
// Quiet, contextual
muted: "text-graphite hover:text-ink underline-offset-4 hover:underline",
// Destructive — a different domain
retract: "bg-rust-500 text-paper hover:bg-rust-700",
},
weight: {
// Hero CTAs, single per page
hero: "h-14 px-8 text-base font-medium tracking-wide",
// Standard buttons
standard: "h-11 px-5 text-sm",
// Inline buttons within lists, small contexts
inline: "h-8 px-3 text-xs",
},
},
defaultVariants: { intent: "action", weight: "standard" },
})The variant names are now intent and weight instead of variant and size. The intents are action / assist / muted / retract instead of default / secondary / ghost / destructive. The weights are hero / standard / inline instead of lg / default / sm. The semantics now match the domain — every button on the site has a clear *purpose* in the call site, not a generic style label.
A reader of your code immediately understands differently from . The first reads as "the primary call to action on the page." The second reads as "a button, default style, large."
9.8 Fork the components folder, don't keep regenerating
A common mistake: developers run npx shadcn add button once, then run it again three months later to "update" the button, which overwrites their customizations. Or they regenerate periodically because of an LLM suggestion. This loses customization work and pushes the codebase back toward defaults.
The fix: treat components/ui/ as code you own, not as generated output. Once you have edited a component, never regenerate it. If you need to pull in a new shadcn component, generate it once, customize it, then leave it alone.
Practical hygiene:
- Add a comment at the top of each customized file:
// CUSTOMIZED — do not regenerate via shadcn CLI. - Avoid running
npx shadcn addon existing components. Use it only for new ones. - When upgrading shadcn (or pulling in new patterns from the docs), do it manually — read the upstream change, apply the parts that matter, leave the rest.
- Treat
components/ui/as part of your code review process. PRs that touch those files get the same scrutiny as any other code.
This sounds tedious but is actually freeing. Once you stop treating shadcn components as something to "keep up to date with the upstream," they become normal source files in your repo. The mental model shifts from "this is a library I'm using" to "this is code I wrote, with help from a tool."
9.9 Write a design tokens file before any component
The single most impactful workflow change. Before you generate any shadcn components, before you write any UI code, write a tokens.css (or tokens.ts) file that defines:
- Color scale (8-12 colors, intent-named: brand, accent, paper, ink, etc.)
- Typography scale (4-6 sizes, named: display, h1, h2, body, caption)
- Spacing scale (the Tailwind default is fine, but document which values you'll actually use)
- Radius scale (3-4 values: pill, card, sharp, soft)
- Shadow scale (3-4 values: subtle, raised, floating, modal)
- Animation/transition (1-2 default eases, 2-3 default durations)
This is 30-60 minutes of design work. It is the difference between a site that has a coherent visual identity and a site that looks like every other shadcn site.
Example skeleton:
/* tokens.css */
:root {
/* Brand */
--color-brand-50: 178 60% 95%;
--color-brand-200: 178 55% 70%;
--color-brand-500: 178 52% 22%;
--color-brand-700: 178 60% 14%;
--color-brand-900: 178 60% 8%;
/* Neutrals (warm) */
--color-paper: 38 30% 97%;
--color-paper-shade: 38 25% 92%;
--color-paper-deep: 38 22% 85%;
--color-graphite: 28 18% 32%;
--color-ink: 28 25% 15%;
/* Accent */
--color-rust-500: 14 78% 50%;
--color-rust-700: 14 78% 35%;
/* Type scale (rem) */
--text-display: 3.5rem;
--text-h1: 2.25rem;
--text-h2: 1.5rem;
--text-h3: 1.25rem;
--text-body: 1rem;
--text-caption: 0.85rem;
/* Radius */
--radius-sharp: 2px;
--radius-soft: 6px;
--radius-pill: 999px;
/* Shadow */
--shadow-subtle: 0 1px 2px hsl(28 25% 15% / 0.06);
--shadow-raised: 0 1px 2px hsl(28 25% 15% / 0.06), 0 4px 12px hsl(28 25% 15% / 0.04);
--shadow-floating: 0 8px 24px hsl(28 25% 15% / 0.10), 0 2px 6px hsl(28 25% 15% / 0.06);
/* Motion */
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
--duration-fast: 160ms;
--duration-base: 240ms;
--duration-slow: 420ms;
}Once tokens are in place, every shadcn component you generate gets wired to use them (via the standard --background, --foreground, etc. variables which now resolve to your scale). Every custom primitive you write references these tokens. The site has a coherent visual identity by construction, not by hope.
This is the workflow change that separates "shipped a shadcn site" from "shipped a designed site that uses shadcn primitives." It takes about an hour. It is the most under-rated step in the entire process.
For a deeper walk-through of the de-defaulting process specifically targeted at AI-generated codebases, see /blog/de-ai-your-lovable-v0-bolt-site.
---
The alternatives
shadcn is the right answer for most projects. It is not the right answer for all projects. Here are the alternatives, with honest pros and cons.
| Option | What it is | Pros | Cons | When to use | |--------|------------|------|------|-------------| | Radix UI directly | The headless primitives shadcn wraps | Fully accessible behaviorally, no styling baggage, you own all visuals | Significant work to build a complete component library | You have a designer, want a unique design system, willing to spend 2-4 weeks on primitives | | Headless UI | Tailwind Labs' headless components | Pairs natively with Tailwind, well-maintained, fewer components than Radix | Smaller scope, less complete than Radix, narrower ecosystem | Tailwind-first project that only needs Disclosure, Menu, Listbox, Dialog | | Park UI | Re-themed shadcn fork (works with Panda CSS or Tailwind) | shadcn primitives with different default styling, easier to escape default fingerprint | Less mature, smaller community, theming choices can also be recognizable | You want shadcn DNA but a different default look | | Ariakit | Diego Haz's headless/styled component library | Mature, accessible, less hyped | Smaller community than Radix, stylesheet-based theming can feel old-school | You want headless components with a different community DNA than shadcn | | Bits UI (Svelte) | shadcn-style for Svelte | If you're on Svelte, this is the analog | Svelte-only | Svelte projects | | Catalyst (Tailwind Labs) | Tailwind UI's premium component kit | High-end design baseline, comes from the team that made Tailwind | Paid (part of Tailwind UI), opinionated visual style | Marketing sites with brand budget, projects that want a different "premium" baseline | | Mantine | Full component library with theme system | Complete, mature, runtime-themed | CSS-in-JS overhead, opinionated visual identity, larger bundle | Internal tools, dashboards, projects where bundle size matters less | | Material UI v6+ | Google's Material Design React implementation | Best-known design system, large ecosystem | Heavy, opinionated, escaping the Material look is real work | Enterprise projects, projects where Material is desired | | Chakra UI v3 | Themeable, props-driven library | Pleasant DX, good defaults, runtime theming | Runtime CSS-in-JS overhead, identifiable visual fingerprint | Projects that prefer style props over Tailwind | | No library, just CSS | Write everything yourself | Maximum differentiation, smallest bundle, your code | Slow, no accessibility shortcuts, you own everything | Strong brand, designer in the loop, willing to invest in foundations |
The choice depends on three variables:
- Do you have a designer? If yes, Radix + Tailwind (or no library) gives you maximum flexibility. shadcn is overkill. If no, shadcn or a similar wrapper is probably right.
- Is brand differentiation core to your business? If yes, default-anything is the wrong starting point. Either deeply customize shadcn (per the previous section) or use a less-defaulted alternative. If no (internal tool, MVP, exploratory build), shadcn defaults are fine.
- What is your stack? React/Next.js → shadcn or Radix. Svelte → Bits UI or Skeleton. Vue → Headless UI's Vue version, Reka UI, or PrimeVue. Tailwind project → Headless UI or shadcn. Non-Tailwind → Mantine, Chakra, or Material.
A specific note on Park UI: it is interesting because it is positioned as "shadcn but with a different visual default." It uses the same Radix primitives, the same component composition, but ships a different theme. This is a useful escape valve — you get all the shadcn ergonomics without the default visual fingerprint. The catch is that as Park UI gets adopted, *its* default also becomes a recognizable fingerprint (just less common, for now). The deeper fix is to customize whatever you use, not to swap one default for another.
A note on Tailwind Labs' Catalyst: this is the premium component kit that ships as part of Tailwind UI. It is paid. The components are visually distinct from shadcn (more opinionated typography, slightly different visual flavor). It has a smaller blast radius in the AI-monoculture sense because LLMs are less trained on it (it is not open source). For sites where the visual baseline matters more than free, Catalyst is a credible alternative.
For projects where the answer is "no library, just write CSS," see the discussion in /blog/from-ai-slop-to-signature-73-patterns-2026, which catalogs the patterns of post-default web design.
---
What shadcn/ui itself should do
This is opinion, presented as such.
shadcn the project is in a position where it could meaningfully reduce the monoculture problem without abandoning what made it successful. Here are five things it could ship that would help.
1. More example archetypes. The current docs include one example dashboard, one music app, one auth scaffold. They are all visually similar (the same default theme, similar typography). If shadcn shipped five visually distinct example sites — a brutalist build, a soft-rounded marketing build, a dense data-tooling build, a serif-and-warmth editorial build, a terminal-inspired devtool build — the AI training data would diversify and the AI tools generating shadcn-default sites would have more visual variety to draw on. Each example would still use the same primitives. Each would prove the primitives can produce different aesthetics.
2. "Customize the preset before you build" as a default flow. The CLI currently runs init, which sets up the project. It could add a theme step that walks the developer through picking a color scale, picking fonts, picking radius and shadow defaults — *before* they generate any components. This would put a small but meaningful design moment into the flow. Even a basic theme configurator would push more sites away from defaults.
3. Themed presets as official templates. Park UI exists as a community fork. There is no reason shadcn itself couldn't ship 4-6 named themed presets — brutalist, editorial, terminal, atelier, soft, warm — each with its own color scale, typography, and motion defaults. Choosing a preset would be a one-flag CLI option (npx shadcn init --theme=editorial). The number of "clearly-shadcn-but-not-default-shadcn" sites would jump.
4. A built-in "this is the default — change me" warning. Slightly tongue-in-cheek but real: the shadcn CLI could print a friendly warning after init along the lines of "Your project is using the default shadcn theme. To stand out, customize the color scale and typography in globals.css. See [link] for guides." Friction designed to push toward customization.
5. Documentation about avoiding default-fingerprint patterns. The current docs are excellent at teaching the *primitives*. They could add a "common pitfalls" or "how to make your shadcn site look like yours" section. Direct guidance on which patterns are now over-used and how to escape them. The docs are an authority — they could shape behavior just by naming the issue.
The maintainer is one person plus contributors, with a finite amount of bandwidth. Not all of these will happen, and they don't need to. The first two — more example archetypes, a customize-first flow — would do most of the work. The community can fill in the rest.
A broader thought: every successful primitive library eventually has the question "how do we prevent our success from creating a monoculture?" Material UI didn't really answer it. Bootstrap didn't. shadcn has the chance to answer it differently because the project's philosophy is already "you own the code." Leaning further into "and here are five aesthetics you could own" instead of "and here is the one default" is a small change with large effects.
---
2027 prediction
Two scenarios, both plausible.
Scenario A: shadcn becomes more like Radix. The community fragments into themed wrappers (Park UI, Catalyst, Tweakcn, others). shadcn itself becomes the *primitives layer*, similar to how Radix was always the primitives layer underneath shadcn. The "complete component library" niche moves up a layer to the themed wrappers. The monoculture peaks in 2026, fragments through 2027, and by 2028 there are 5-10 named visual aesthetics that compete. The "shadcn fingerprint" stops being a single look and becomes a family of looks.
In this scenario, by 2028 a developer asking an LLM for a "modern site" gets a follow-up question about which aesthetic they want, rather than the default. AI tools like v0 ship multiple themed presets. The visual baseline of the AI-built web stops being uniform.
Scenario B: shadcn keeps winning, monoculture entrenches. The community does not produce sufficiently differentiated wrappers (or the wrappers themselves get assimilated into the same defaults via the same training-data feedback loop). shadcn-default becomes the equivalent of Bootstrap-default in 2014 — a recognized aesthetic that signals "early-stage, AI-built, low-budget" but is also so dominant that it's the safe choice. Differentiation becomes the explicit responsibility of design-conscious teams, and the gap between "designed" sites and "vibe-coded" sites widens.
In this scenario, by 2028 the visual divide between "premium, designed brands" and "AI-default startups" is a real market segmentation. The premium tier has hand-built design systems with no shadcn DNA. The default tier is shadcn through and through. End users develop conscious or unconscious associations between visual default and brand quality.
Which scenario plays out depends on a few specific decisions:
- Whether shadcn ships themed presets in its docs (pushes toward A).
- Whether v0/Bolt/Lovable diversify their generation defaults (pushes toward A) or continue to ship shadcn-default by default (pushes toward B).
- Whether designers re-enter the loop in vibe-coded projects (pushes toward A).
- Whether the next generation of AI tools includes "design diversity" as an explicit objective (pushes toward A) or continues to reward the highest-probability completion (pushes toward B).
- Whether non-default themed wrappers get organized momentum (pushes toward A) or remain niche (pushes toward B).
My personal bet: a partial Scenario A, with the AI tools driving most of the progress. v0.dev, Bolt, and Lovable have product reasons to diversify their outputs (it is a competitive differentiator if their generated sites don't all look alike). As they ship more variety, the training data also diversifies. By 2028 the monoculture loosens but does not vanish. shadcn defaults still exist, but they are one of many recognizable aesthetics rather than the only one.
For broader 2026-2027 predictions about the AI-built web, see /blog/vibe-coding-2026-honest-state-of-ai-frontends.
---
For founders / devs reading this
Practical decision tree.
flowchart TD
Start[New project: should I use shadcn?] --> Q1{Is this an internal tool or MVP?}
Q1 -- Yes --> UseDefaults[Use shadcn defaults. Ship it. Differentiation is not the goal here.]
Q1 -- No --> Q2{Is brand differentiation central to the product?}
Q2 -- No --> Q3{Do I have a designer?}
Q3 -- Yes --> ShadcnPlusDesigner[Use shadcn primitives, designer customizes. Best of both worlds.]
Q3 -- No --> ShadcnCustomized[Use shadcn but invest 1-2 days in tokens, fonts, icons before shipping. Avoid pure defaults.]
Q2 -- Yes --> Q4{Do I have a designer or strong design instincts?}
Q4 -- Yes --> Q5{Time budget for foundation work?}
Q5 -- Generous --> RadixDirect[Radix UI directly + custom design system. Maximum differentiation.]
Q5 -- Tight --> ShadcnDeepCustom[shadcn with all 9 customization steps applied. ~1 week of design work upfront.]
Q4 -- No --> Q6{Budget to hire a designer for this?}
Q6 -- Yes --> HireFirst[Hire a designer first, then choose stack with them. Don't pick the stack alone.]
Q6 -- No --> ShadcnCustomized2[Use shadcn but explicitly customize tokens, fonts, icons. Park UI or Catalyst if budget allows.]Reading the tree: most projects end up at "shadcn but customized." The cases where you should use pure defaults are narrow (internal tools, prototypes, throwaways). The cases where you should not use shadcn at all are also narrow (luxury brands, design-portfolio sites, projects where the visual *is* the product).
A few more specific recommendations:
- For a YC application or pre-seed pitch: Pure default shadcn is increasingly read as "low investment in differentiation." Spend a day customizing tokens and fonts. The judges have seen too many default sites.
- For a Series A pitch: A default shadcn site at this stage is a real signal of "founder did not invest in brand." Either invest in real design or be very conscious about why you chose not to.
- For an indie product launch on Product Hunt or Show HN: A default shadcn site will be noticed in the comments. The cost of customization is low and the comment-section payoff is real.
- For an internal admin or back-office tool: Default shadcn is correct. Ship it. Spend zero time customizing. You are not differentiating anything.
- For a marketing site for an AI product: Default shadcn is the worst possible signal. Every other AI product also has it. Differentiation here is not optional.
- For a personal portfolio or blog: Default shadcn is fine for engineers showing they can ship. For designers it is malpractice.
The cost of customization is small (1-3 days, mostly upfront). The cost of being indistinguishable from competitors is large and ongoing. The math favors customization for almost every public-facing project.
For a parallel discussion of the exact AI-fingerprint patterns to avoid in copy and structure, not just visuals, see /blog/from-ai-slop-to-signature-73-patterns-2026.
---
FAQ
1. Is shadcn slop?
No. shadcn is a well-designed primitive library that solves a real problem. The defaults are tasteful, the architecture is sound, the philosophy is correct. The *output of using shadcn defaults without customization on a public-facing brand site* is now a slop signal in 2026. Those are different statements. shadcn is good. Default-shadcn-everywhere is the problem.
2. Will my site rank lower in search engines if I use shadcn?
Not directly. Search engines do not have a "shadcn detector" that downranks sites. What they do have is trust signals (backlinks, age, real engagement, quality content). Default-shadcn sites are correlated with low-trust signals because many of them are AI-vibe-coded MVPs that didn't ship real content. The correlation hurts even legitimate sites that happen to use defaults. The fix is the same as the fix for any low-trust signal: customize the visual, ship real content, get real backlinks, behave like a serious site.
3. Should I rewrite my Bolt or Lovable site that uses shadcn?
Probably not rewrite — refactor. Apply the nine customization steps from the section above. Replace the color scale with your own (largest impact). Replace Inter and Lucide. Wrap the generic Card primitive in domain-specific components. Customize the Dialog animation. This takes 1-3 days and produces a site that uses shadcn primitives but does not look default-shadcn. A full rewrite is rarely the right call.
4. Is Park UI different enough?
It is different from default shadcn, but Park UI itself has a default look that is becoming recognizable. The deeper fix is to customize whatever library you use, not to swap one default for another. That said, Park UI is a fine choice if you want shadcn ergonomics with a different starting point — just don't ship its defaults either.
5. What about Catalyst from Tailwind Labs?
Catalyst is a credible alternative. It is paid (part of Tailwind UI), has a more opinionated visual baseline, and is less common in AI training data because it is not open source. For paid projects with a brand budget, Catalyst is a reasonable way to get a "premium" baseline that is harder to confuse with shadcn-default. Still customize, but the starting point is less common.
6. Can I tell from a screenshot if a site uses shadcn?
Often, yes. The fingerprint signs (Card composition, Button outline variant, Lucide icons, Inter font, slate palette, the standard hero gradient) are recognizable in under three seconds for someone who has seen many of them. This is itself a problem — your users are starting to recognize the pattern too.
7. Is this a Bootstrap moment? Will it pass like Bootstrap did?
Partially. Bootstrap eventually faded as a default because the ecosystem moved on (to React, to Tailwind, to component libraries). shadcn is unlikely to fade the same way because the philosophy (you own the code) is durable. What is likely to fade is the *default look* — through customization tooling, themed wrappers, and AI tools that diversify their outputs.
8. Should I use shadcn for an internal admin panel?
Yes. The internal admin panel is the killer use case for shadcn. Differentiation does not matter. Speed-to-functional matters. Defaults are fine. Ship it.
9. What if my AI tool keeps generating shadcn defaults?
Be explicit in your prompts. Say "build me a site without using shadcn" or "use Radix UI directly and Tailwind, no shadcn defaults." The AI will comply. The defaults are not chosen against you — they are the highest-probability completion, and you can override that with explicit instruction. For a more reliable workflow, hand the AI your tokens.css file and say "use these tokens for everything" — this constrains the output toward your design.
10. Is the Lucide icon set really that recognizable?
Yes. Lucide has a specific stroke weight (2px default), a specific roundness, a specific geometric flavor. After seeing it on enough sites, your eye recognizes it without conscious thought. The same is true of Heroicons, Phosphor, and Tabler — they all have signature looks. The fix is either to use a less-common set, mix sets, or draw your own.
11. Can I use shadcn in production for a serious product?
Yes, absolutely. Many serious products use shadcn well. The difference is customization: serious products that use shadcn well have invested in their tokens, typography, and primitives, so the shadcn DNA is present but not dominant. Serious products that use shadcn badly look like every other AI MVP. The choice is yours.
12. What's the deal with the blue-purple gradient?
The blue-to-violet gradient (from Tailwind blues to violets, often from-blue-600 to-violet-600 or similar) is the dominant hero-section gradient for AI startups in 2024-2026. It is not strictly a shadcn default — shadcn doesn't ship a hero — but it is so closely associated with the same template population that it co-occurs with shadcn fingerprints. For a deeper dive, see /blog/tailwind-blue-purple-gradient-ai-signature-2026.
13. Do designers hate shadcn?
No. Designers I've spoken with broadly respect shadcn the project — it is well-architected, accessible, and gives developers a strong starting point. What designers (rightly) dislike is *un-customized shadcn shipped by developers who didn't engage with design*. The complaint is about workflow, not about the project. Designers who work with developers who use shadcn-as-a-starting-point and then customize together are usually happy.
14. Is there a "non-shadcn" movement?
Not a unified one. There are pockets — some Tailwind-only purists, some Radix-direct camps, some "no library at all" advocates, some Park UI evangelists. The most consistent pattern is the explicit "customize before you ship" advocacy that mostly comes from designers. There is no organized backlash against shadcn the project, just an emerging consensus that defaults are no longer enough.
15. What should the next shadcn-equivalent look like?
A few possibilities. One: a primitive library that ships with multiple visual defaults from day one (no single "canonical" look). Two: a primitive library that bundles a design-tokens-first workflow (you pick tokens before you generate components). Three: a tool that generates *novel* component visuals from primitives, not from training data — algorithmic differentiation rather than template selection. Four: nothing new, just better tooling around shadcn itself to push customization. The space is open.
---
Glossary
- shadcn / shadcn/ui — A copy-paste-into-your-repo collection of Radix UI primitives styled with Tailwind CSS, distributed as a CLI rather than as a package. Released 2023. The most common React component baseline by 2026.
- Radix UI — Headless, accessible UI primitives (Dialog, Popover, DropdownMenu, etc.) for React. Provides behavior without styling. Underpins shadcn.
- Tailwind CSS — Utility-first CSS framework. Class-based styling composed inline in markup. Pairs with shadcn as the styling layer.
- Headless component — A component that provides behavior (focus management, ARIA, keyboard navigation) without imposing visual style. The developer styles it themselves.
- cva (class-variance-authority) — A library for managing component variants with type safety. Used by shadcn for things like
Buttonvariant/size combinations. - cn() utility — shadcn's combined
clsx+tailwind-mergehelper for composing class names with conflict resolution. Detectable as a shadcn signal. - Lucide — An icon library based on Feather icons, with a thin-stroke geometric flavor. Default in shadcn examples.
- v0.dev — Vercel's AI-powered UI generator that produces shadcn-based components from prompts.
- Bolt.new — StackBlitz's in-browser AI-powered project scaffolder. Defaults to shadcn for React projects.
- Lovable — A vibe-coding platform that scaffolds React/TypeScript apps. Uses shadcn under the hood.
- Cursor — An AI-first code editor based on VS Code. Generates shadcn by default for React projects.
- Claude Code — Anthropic's CLI for the Claude model, used for agentic coding. Often generates shadcn for frontend tasks.
- Park UI — A re-themed shadcn-style fork that ships different default visuals using Panda CSS or Tailwind.
- Ariakit — A headless component library by Diego Haz, an alternative to Radix.
- Bits UI — A Svelte component library inspired by shadcn/ui, providing the same primitives for the Svelte ecosystem.
- Catalyst — Tailwind Labs' premium component kit, part of Tailwind UI. Paid alternative to shadcn.
- Slop — In the AI-built-web sense: low-trust, low-differentiation, AI-default output that lacks human investment in design and craft.
- Default fingerprint — The set of visual signals that identify a site as shipped with library defaults (Card composition, Button variants, color palette, typography, icons).
- Vibe coding — Building software by describing intent to an AI assistant rather than writing code directly. Common with Bolt, Lovable, v0, Cursor in 2024-2026.
- Show HN — The Hacker News convention for posting a built thing for community review. A common destination for early-stage AI-built sites in 2026.
---
Sources and further reading
- shadcn/ui official documentation — the canonical source for the project's philosophy, components, and theming.
- Radix UI documentation — the underlying primitives, with detailed accessibility notes.
- Tailwind CSS documentation — the styling layer.
- The original Twitter/X threads from shadcn (the maintainer) explaining the project's design rationale, archived in various community discussions.
- Hacker News archives — search "shadcn" for the community discussions about the project from 2023 onwards, including critical takes.
- Tailwind UI / Catalyst documentation — for the alternative premium component kit perspective.
- Park UI, Ariakit, Bits UI, Headless UI documentation — the alternatives discussed.
- Internal Sailop reading list:
- /blog/ai-slop-2026-state-of-the-ai-generated-web - /blog/tailwind-blue-purple-gradient-ai-signature-2026 - /blog/de-ai-your-lovable-v0-bolt-site - /blog/from-ai-slop-to-signature-73-patterns-2026 - /blog/vibe-coding-2026-honest-state-of-ai-frontends
Real sites that use shadcn well — deeply customized, hard to recognize as shadcn unless you read the source — are everywhere if you know to look. Good shadcn-using sites typically share three properties: a custom color scale (not slate), a non-Inter typeface, and domain-specific component primitives wrapping the generic shadcn ones. Sites that fail those three checks are usually the default-shadcn slop sites. Apply the checks to your own site before shipping.
| Pattern | Sites that get it right | Sites that don't | |---------|-------------------------|------------------| | Custom color scale | Token file with brand colors, no slate-* references in markup | bg-slate-900, text-slate-50 everywhere | | Custom typography | Serif + sans + mono trio, or distinctive single | Inter only, all weights | | Domain primitives | , , | with , everywhere | | Custom motion | Distinct ease curves, durations, signature interactions | Default fade-in-zoom Dialog, default Tooltip delay | | Custom icons | Phosphor / Tabler / hand-drawn / paid set | Lucide everywhere | | Custom variants | intent="action", weight="hero" | variant="default", size="lg" |
The recognition is the goal. If a viewer sees your site and thinks "this is a shadcn site," you have not customized enough. If they see your site and think "this is *your* site, and I can't tell what library it uses," you have done the work.
shadcn deserves credit for solving the original problem. The next problem — visual identity in an AI-built world — is yours.
SHIP CODE THAT LOOKS INTENTIONAL
Scan your frontend for AI patterns. Generate a unique design system. Stop shipping the same blue gradient as everyone else.