If you have built anything in React over the last couple of years, you have almost certainly met shadcn/ui and its slightly subversive idea: instead of installing a component library from node_modules and importing black boxes you can never quite restyle, you run a CLI that copies the actual component source into your project. The code becomes yours. You edit it, fork it, and forget about upstream version locks. It was a genuinely refreshing take on the component-library problem, and React developers ran with it.
Astro developers, meanwhile, watched from the sidelines. Astro's whole pitch is shipping less JavaScript, so pulling in a React-and-Radix-based library to render a button always felt like bringing a forklift to carry a teacup. That is the gap Bearnie steps into. It is the same copy-in, own-your-code philosophy, rebuilt for Astro and Tailwind CSS, with components that are framework-free wherever possible. Think of it as "shadcn/ui, but for Astro."
A quick, honest caveat before we dig in: Bearnie is early-stage software. It sits at version 0.1.5, the repository is only a few months old, and it is largely a single-maintainer project. The API has not stabilized and the component set is still growing release to release. This is a "this is genuinely promising, try it on a side project" tour, not a "bet your production design system on it today" recommendation. With that on the table, let us see what makes it worth your attention.
The Copy-In Model, Explained
The most important thing to understand about Bearnie is that the bearnie npm package is not the component library. It is a command-line tool. You do not add Bearnie to your dependencies and import Button from it. Instead, you run the CLI, it reaches into a registry of component source files, and it writes those .astro files directly into your project's source tree.
This has a few consequences that are easy to miss if you are used to the traditional model. There is no Bearnie version pinned in your package.json governing your components, because once the files land in your repo they are simply your files. There is no upstream maintainer who can break your button with a minor release, because there is no upstream button anymore. And when a designer asks for a slightly different focus ring or a new variant, you do not file an issue and wait. You open the file and change it.
The trade-off, of course, is that you do not get automatic updates either. If Bearnie improves its accordion next month, you re-run the CLI to pull the new version and reconcile any local edits yourself. For a component library, most teams consider that a fair deal: components are exactly the kind of code you want to own outright.
Under the hood, the interactive components avoid shipping a heavy framework. Rather than mounting React or Vue islands, Bearnie's registry includes small vanilla-JavaScript runtime helpers (it ships pieces like a disclosure-trigger helper, a popover helper, and a command-palette helper) that power behavior with minimal client code. That keeps the spirit of Astro intact: static HTML where you can, a sprinkle of JavaScript only where you must.
What You Actually Get
Bearnie is not a toy with three components. The live registry carries roughly fifty of them, and they cover the surface area you would expect from a serious UI kit:
- Forms: button, button group, input, input group, OTP input, textarea, label, checkbox, radio, select, switch, slider, toggle, toggle group, and file upload.
- Layout and overflow: card, separator, scroll area, aspect ratio, sidebar, and sheet.
- Navigation and menus: breadcrumb, tabs, dropdown menu, menubar, pagination, context menu, command palette, and combobox.
- Feedback: alert, alert dialog, badge, progress, skeleton, spinner, toast, tooltip, keyboard hint, and empty state.
- Disclosure and overlays: accordion, collapsible, dialog, popover, and hover card.
- Display: avatar, table, tree, carousel, and stepper.
Two things matter beyond the list itself. First, accessibility is a stated goal, not an afterthought: the components are built to follow the WCAG 2.1 AA guidelines, which is exactly the kind of work you do not want to reinvent under deadline. Second, the API conventions will feel instantly familiar to anyone arriving from shadcn/ui. Components are compound and slotted, variants are passed as props, and the naming mirrors what React developers already know. The muscle memory transfers cleanly.
Getting Bearnie Into Your Project
Because Bearnie is a CLI rather than a runtime dependency, you do not really "install" it in the traditional sense. You point it at an existing Astro project and let it scaffold itself. The recommended path is to use npx so you always run the latest CLI without a global install.
# Run the CLI directly with npx (recommended)
npx bearnie init
# Or, if you prefer a global install
npm install -g bearnie
bearnie init
# Yarn users can reach the CLI the same way
yarn dlx bearnie init
Running init does the housekeeping for you. It creates a bearnie.json configuration file in your project root, sets up the components directory, drops in a cn() class-merging utility, and installs the small set of dependencies the components rely on: clsx, tailwind-merge, and tailwindcss. That last point is worth underlining: the only runtime packages that land in your package.json are these styling helpers, not Bearnie itself.
The generated configuration is deliberately small:
{
"componentsDir": "src/components/bearnie",
"utilsDir": "src/utils",
"typescript": true
}
You can change where components are written, where the utilities live, and whether you want TypeScript. One requirement to be aware of: Bearnie components use the @/ path alias, so your tsconfig.json needs baseUrl set to . and a paths entry mapping @/* to src/*. The init command is the moment to verify that, since every imported component depends on it.
Adding and Composing Components
With the project initialized, pulling in components is a single command. You name the ones you want, and the CLI fetches their source and writes the files:
# Add a few specific components
npx bearnie add button card input
# Browse and pick interactively (run add with no arguments)
npx bearnie add
# Pull in everything at once
npx bearnie add --all
# See what is available before committing
npx bearnie list
Once the files exist in src/components/bearnie, you import them like any other Astro component. Here is a small sign-in card that stitches a few of them together. Notice how the compound components (the card and its header, content, and footer pieces) compose exactly the way the shadcn equivalents do:
---
import Card from "@/components/ui/card/Card.astro";
import CardHeader from "@/components/ui/card/CardHeader.astro";
import CardTitle from "@/components/ui/card/CardTitle.astro";
import CardDescription from "@/components/ui/card/CardDescription.astro";
import CardContent from "@/components/ui/card/CardContent.astro";
import CardFooter from "@/components/ui/card/CardFooter.astro";
import Input from "@/components/ui/input/Input.astro";
import Label from "@/components/ui/label/Label.astro";
import Button from "@/components/ui/button/Button.astro";
---
<Card class="w-96">
<CardHeader>
<CardTitle>Welcome back</CardTitle>
<CardDescription>Sign in to your account</CardDescription>
</CardHeader>
<CardContent>
<form class="space-y-4">
<div>
<Label for="email">Email</Label>
<Input type="email" id="email" placeholder="you@example.com" />
</div>
<div>
<Label for="password">Password</Label>
<Input type="password" id="password" />
</div>
</form>
</CardContent>
<CardFooter>
<Button class="w-full">Sign In</Button>
</CardFooter>
</Card>
Variants are driven by props, again in the shadcn idiom. A button speaks the vocabulary you would guess: variant for visual style and size for scale.
---
import Button from "@/components/ui/button/Button.astro";
---
<Button>Default</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="destructive">Delete</Button>
<Button size="sm">Small</Button>
<Button size="lg">Large</Button>
Because the source lives in your repo, that variant list is not a fixed menu handed down from a maintainer. If your brand needs a ghost or a subtle variant, you open Button.astro, add the class mapping, and it is there forever. This is the quiet superpower of the whole approach: the components are a starting point, not a contract.
Running Your Own Registry
The feature that elevates Bearnie from "nice Astro component kit" to "foundation for a design system" is its support for custom registries. The CLI reads two environment variables that let you redirect where components come from:
# Point the CLI at a self-hosted registry over HTTP
BEARNIE_REGISTRY_URL=https://ui.mycompany.dev/registry npx bearnie add button
# Or read components from a local registry on disk (handy in a monorepo)
BEARNIE_REGISTRY_PATH=/path/to/registry npx bearnie add button
This is genuinely useful, and it is exactly how Bearnie describes itself: a tool to build your own component library. The pattern looks like this. You fork Bearnie's components, restyle them to your brand, and host the resulting registry somewhere your team can reach. Then every project in your organization runs the same familiar bearnie add workflow, but pulls your components instead of the defaults. You get shadcn-style distribution ergonomics for an in-house design system, without writing your own CLI to do it.
For a small product team or an agency that spins up a lot of Astro sites, that is a meaningful amount of plumbing you do not have to build yourself. The CLI handles fetching, file placement, the cn() utility, and dependency installation; you supply the components and the registry endpoint.
A Word on the CLI Itself
It is worth noting, because it speaks to the care behind the project, that the CLI is a pleasant thing to use. It is built on the usual solid foundations (Commander for argument parsing, Ora for spinners, Prompts for interactive selection) and wrapped in a friendly amber-bear presentation, complete with clickable terminal links and gentle error messages when you fat-finger a command. None of that is load-bearing, but it signals that the maintainer cares about the small stuff, which tends to correlate with components that were also built with care.
Should You Reach for Bearnie?
Bearnie occupies a specific and, until recently, underserved niche: the shadcn-for-Astro slot. If you are building an Astro site and you want accessible, Tailwind-styled components that you fully own, without dragging a React runtime into a framework designed to ship less JavaScript, it is the most natural fit available. The copy-in model, the framework-free interactivity, the WCAG 2.1 AA focus, and the self-hostable registry are a coherent and genuinely appealing package.
Temper that with the reality of where the project is. At 0.1.5, with a young repository and effectively one maintainer, you are adopting something that will change underneath you and does not yet have the battle scars of a library with years and tens of thousands of stars behind it. The flip side, and it is a real one, is that the copy-in model insulates you from a lot of that risk: once the components are in your repo, an unstable upstream cannot break a site you have already shipped.
So the honest recommendation is this. For a new Astro project, a personal site, an internal tool, or the seed of a homegrown design system, Bearnie is well worth a try right now, and pleasantly so. For a large production application where you need long-term API stability and a deep support ecosystem, keep it on your watchlist and check back as it matures toward 1.0. Either way, it is a welcome arrival, and Astro is better for having a real answer to "where is our shadcn?"