Tokens, helpers, and the theme composable — installed with one
npx. Files copy into your repo. You own
them.
Bootstrap
$ pnpm dlx shadcn-vue@latest add https://uipkge.dev/r/vue/init.json -y $ npx shadcn-vue@latest add https://uipkge.dev/r/vue/init.json -y $ yarn dlx shadcn-vue@latest add https://uipkge.dev/r/vue/init.json -y $ bunx shadcn-vue@latest add https://uipkge.dev/r/vue/init.json -y npx shadcn-vue@latest add @uipkge/init -y
The shadcn-vue preflight refuses to write components when it can't find a Tailwind setup (No Tailwind CSS configuration found), so wire Tailwind v4 in first. If you're not on Nuxt 4, follow tailwindcss.com/docs/installation then jump to step 02.
1. Install Tailwind v4 + the Vite plugin
npm install -D tailwindcss @tailwindcss/vite typescript 2. Create a placeholder CSS entry
shadcn-vue probes for a Tailwind-importing CSS file before it does anything. Step 03
overwrites this file with the full OKLCH token set, so the placeholder is throwaway. Create app/assets/css/tailwind.css in your editor and paste in:
@import "tailwindcss";
Skip the echo '...' > file shell trick — it
writes a UTF-16 BOM under PowerShell that Vite rejects, and CMD has no mkdir -p. Create the file in your editor.
3. Wire the Vite plugin + CSS in nuxt.config.ts
import tailwindcss from '@tailwindcss/vite'
export default defineNuxtConfig({
css: ['~/assets/css/tailwind.css'],
components: [{ path: '~/components', pathPrefix: false }],
vite: { plugins: [tailwindcss()] }
}) components: [{ pathPrefix: false }] keeps
blocks in sub-folders of components/ from
registering as <BlocksDashboardLayout>
instead of <DashboardLayout>.
components.json
You can let npx shadcn-vue add scaffold this, but the CLI fires interactive prompts (icon library / font / base color) before it reads -y, and it won't pre-register the @uipkge
named registry. Writing it yourself skips both. Our registry items target ~/app/... directly, so files land in the right
place regardless of how the CLI resolves the @/ aliases below.
Create components.json at the project root and
paste this in:
{
"$schema": "https://shadcn-vue.com/schema.json",
"style": "new-york",
"typescript": true,
"tailwind": {
"config": "",
"css": "app/assets/css/tailwind.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"aliases": {
"components": "@/components",
"composables": "@/composables",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib"
},
"registries": {
"@uipkge": "https://uipkge.dev/r/vue/{name}.json"
}
} $ pnpm dlx shadcn-vue@latest add https://uipkge.dev/r/vue/init.json -y $ npx shadcn-vue@latest add https://uipkge.dev/r/vue/init.json -y $ yarn dlx shadcn-vue@latest add https://uipkge.dev/r/vue/init.json -y $ bunx shadcn-vue@latest add https://uipkge.dev/r/vue/init.json -y npx shadcn-vue@latest add @uipkge/init -y
With components.json already in place from step 02, the
CLI resolves init.json's three transitive deps
(tailwind tokens, cn(), useTheme) and writes 4 files. You may see a single overwrite prompt for the CSS file mid-install
— press Enter (default N); the token merge has already run. The
"Circular dependency detected" notice you may see is a benign upstream resolver quirk.
Drops these into your repo — yours to edit, no upgrades.
One-shot bootstrap. Pulls tailwind tokens, the cn() helper, and the useTheme composable in a single command. Run this first when starting a new app.
Tailwind v4 design tokens (light + dark), motion tokens, base layer styling, and shadow utilities. Ships the full canonical tailwind.css as the `files` content so a re-pull with `--overwrite` lands a working setup. The cssVars block stays as a safety net for first-install scenarios where the CLI merges tokens into a consumer's pre-existing tailwind.css. The `css` (@layer base) field is intentionally omitted to avoid duplicating the @layer base block already present in the shipped file when the CLI merges.
Tailwind class merge helper: cn(). Combines clsx and tailwind-merge.
Framework-agnostic useTheme composable (cookie-backed ref, works in plain Vue / Vite / Nuxt) + an optional Nuxt Nitro plugin that inlines the dark-class assignment in <head> for zero-flash SSR. The composable reads/writes the uipkge-theme cookie directly via document.cookie; the plugin is only needed under Nuxt for SSR no-flash.
Copy the source into your project — you own it, no semver, edit freely. Install by direct URL, or register the
named registry once and use the @uipkge/<name> short form anywhere.
$ pnpm dlx shadcn-vue@latest add https://uipkge.dev/r/vue/button.json $ npx shadcn-vue@latest add https://uipkge.dev/r/vue/button.json $ yarn dlx shadcn-vue@latest add https://uipkge.dev/r/vue/button.json $ bunx shadcn-vue@latest add https://uipkge.dev/r/vue/button.json npx shadcn-vue@latest add @uipkge/button Paste the URL of any item from /vue/components or /vue/blocks. Transitive registry deps come along automatically.
Or register the named registry once in components.json:
{
"registries": {
"@uipkge": "https://uipkge.dev/r/vue/{name}.json"
}
}
Configure once, then @uipkge/<name> works anywhere shadcn-vue does.
import { Button } from '@/components/ui/button'
// then in your template:
// <Button>Hello uipkge</Button> Cosmetic noise you'll see during install. None of these break anything; they're listed so you can ignore them without wondering.
init.json. False positive from the
resolver — the init → {tailwind, utils, use-theme} graph is
acyclic. Install completes cleanly.
Button.vue and index.ts. Harmless — your explicit import { Button } from '@/components/ui/button'
wins.
tailwind.css and not the
Nuxt-default main.css
. The tailwind registry item ships a
file-write (target hard-coded to ~/app/assets/css/tailwind.css) and a
token-merge (target read from components.json
). If those paths differ you get two CSS files — naming both tailwind.css makes them converge on one.