Ejecting Components
When to use this
Install @mercadoeletronico/eds-next normally for everyday use. Use ejection only when a
specific component or block needs customization beyond what its props and slots allow. Once
ejected, your team owns that code — it no longer receives updates from the design system.
One-time setup
If your project does not have a shadcn-vue setup yet, avoid the interactive init — with no
extra flags, or with -d/--defaults, it auto-selects a preset (e.g. nova) that installs
shadcn-vue's own base theme: a full set of unused CSS custom properties (--card, --popover,
--sidebar-*, --chart-1..5, etc.) that no eds-next component reads. Pass explicit flags
instead — this skips the preset entirely and keeps your CSS entrypoint to just the Tailwind
import:
npx shadcn-vue@latest init -y -t nuxt --base reka --style nova --icon-library lucide \
--font geist-sans --base-color neutral --no-base-style
(swap -t nuxt for -t vite on a plain Vue + Vite project — the rest of the flags are the
same. --style/--icon-library/--font/--base-color can be any supported value; they only
affect components.json metadata, not the CSS output, once --no-base-style is set.)
Ejecting a component still adds a small amount of CSS boilerplate on top of your bare
@import "tailwindcss"; — a font import, @import "tw-animate-css";, and a
@custom-variant dark (&:is(.dark *)); line. Install the one missing dependency this
introduces:
pnpm add tw-animate-css
You may also see a leftover block like this, left over from the base-style skip:
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
}
Delete it. It references --border/--background/--foreground/--ring, none of which
are defined without the base style — Tailwind v4 fails the build with
Cannot apply unknown utility class if you leave it in.
Then register the eds-next registry as a namespace in your components.json:
{
"registries": {
"@mercadoeletronico": "https://eletro.design/r/{name}.json"
}
}
If your project uses pnpm 10+, run this once before your first add command:
pnpm approve-builds --all
Without it, shadcn-vue add fails with a scary ERR_PNPM_IGNORED_BUILDS error. It's harmless —
pnpm 10+ blocks dependency build scripts until you approve them — but it looks like the eject
itself failed.
Nuxt projects: register the @nuxt/ui module
Register the @nuxt/ui module before ejecting anything that renders a U* component (e.g.
login-form). This is required even if your project already has @mercadoeletronico/eds-next
installed via npm — the eds-next Nuxt module only registers its own Me* components, it does
not register @nuxt/ui for you:
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxt/ui']
})
Without it, @nuxt/ui's components (UAuthForm, etc.) are never registered — they won't appear
in .nuxt/components.d.ts and templates that use them will fail to resolve.
Color tokens
Add the eds-next color tokens once per project:
npx shadcn-vue@latest add @mercadoeletronico/theme
This creates ~/assets/eds-tokens.css and installs @nuxt/ui as an npm dependency. The file's
first line is @import "@nuxt/ui"; — that's what defines the semantic utility classes
(bg-default, text-highlighted, border-accented, etc.) that ejected components like
tooltip, spinner, and login-form render with. Registering the Nuxt module above is not a
substitute for this import — the module wires up components/auto-imports, but the CSS chain
still has to be imported explicitly, the same way it does for a plain Vite project.
Import it once in your global CSS entrypoint (e.g. app/assets/css/main.css on a project
created by shadcn-vue init):
@import "../eds-tokens.css";
The relative path depends on where your CSS entrypoint actually lives relative to
app/assets/eds-tokens.css — adjust it if your entrypoint isn't one level down, at
app/assets/css/.
On a fresh Nuxt 4 project (which uses srcDir: 'app' by convention), the file lands at
assets/eds-tokens.css in the project root, not app/assets/ — the shadcn-vue CLI's ~ alias
doesn't read Nuxt's srcDir config. Move it into app/assets/eds-tokens.css yourself, or Nuxt
won't bundle it.
Primary color
app.config.ts is fully yours — the CLI never ships or overwrites it. To match eds-next's own
primary color, set it yourself:
// app.config.ts
export default defineAppConfig({
ui: {
colors: { primary: 'blue' }
}
})
Plain Vue + Vite projects only
A bare npm create vite@latest -- --template vue-ts project has no Tailwind CSS and no @ path
alias configured — both are required before shadcn-vue init -t vite will proceed. Add Tailwind
v4:
pnpm add tailwindcss @tailwindcss/vite
// vite.config.ts
export default defineConfig({
plugins: [tailwindcss(), vue()]
})
/* src/style.css */
@import "tailwindcss";
Then add a @ alias in both tsconfig.json and vite.config.ts:
// tsconfig.json
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}
// vite.config.ts
export default defineConfig({
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
Nuxt projects register @nuxt/ui components via the Nuxt module (see "Nuxt projects: register
the @nuxt/ui module" above). Plain Vite projects have no such module, so register the Nuxt UI
Vue plugin manually instead — do this once, before ejecting any component that renders a U*
component (e.g. login-form):
// main.ts
import ui from '@nuxt/ui/vue-plugin'
app.use(ui)
// vite.config.ts
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [ui()]
})
Ejecting a component or block
npx shadcn-vue@latest add @mercadoeletronico/tooltip
npx shadcn-vue@latest add @mercadoeletronico/spinner
npx shadcn-vue@latest add @mercadoeletronico/login
Adding login pulls in login-form and use-i18n automatically — they are declared as
registryDependencies of the login and login-form items. This is expected: MeLoginForm
bundles the full eds-next translation composable, including all 7 shipped locales, even if
you only render the login form in one language. Ejecting login always brings the full
login-form + use-i18n bundle with it — there's no lighter option today. If you only need the
split-layout page and want to keep using the npm package's MeLoginForm for everything else,
don't eject login at all; build the layout yourself instead.
Available items
Almost every component and block in @mercadoeletronico/eds-next is ejectable — the registry
is generated from the same source used to build the npm package, so there is no separate list
to keep in sync. This also includes composables — alongside use-i18n (see above), useTheme
is ejectable as use-theme:
npx shadcn-vue@latest add @mercadoeletronico/use-theme
Browse what's available at
/r/{name}.json for any component/block name shown in
the components or blocks docs (convert the name to kebab-case,
dropping the Me prefix — e.g. MeButtonBar → button-bar). A small number of sub-components
documented only as part of a parent component's page aren't ejectable as their own item — the
registry only generates an item when a component has its own dedicated docs page to source a
title and description from. MeForeheadCartArea and MeForeheadActionBar (documented on
Forehead's own page, not individually) fall in this category and aren't delivered by any item
today. MeForeheadBadge is documented the same way, but it IS delivered: it ships as part of
the forehead item itself, since MeForehead's own template renders it directly. Ejecting
forehead gets you MeForeheadBadge too — it just isn't ejectable as its own standalone item.
Known limitations
Component name collisions
If your project already has @mercadoeletronico/eds-next installed via npm and you eject a
component that also exists in the npm package (e.g. spinner), the ejected file keeps its
original internal name — defineOptions({ name: 'MeSpinner' }) — identical to the npm package's
own component. There's no file-path collision, but the shared name can still cause ambiguity in
Vue devtools, <KeepAlive include="MeSpinner">, or any code that references that literal name
string.