Nuxt aliases
New Semantics themes define two Nuxt aliases with fundamentally different purposes — #extensions and #widgets/plugin-registry. They're often confused. This page explains what each alias is for and, importantly: which alias is not meant for user imports.
The two aliases
_theme/vue-base/nuxt.config.ts:
import { resolve } from 'node:path'
export default defineNuxtConfig({
alias: {
// Cross-plugin imports
'#extensions': resolve(process.cwd(), '../../_public/extensions/core/backend'),
// Auto-generated Pagebuilder widget map — DO NOT edit manually!
'#widgets/plugin-registry': resolve(process.cwd(), 'app/widgets/plugin-registry.js'),
}
})Both aliases look similar at first glance (# prefix, "widget" in both names), but they serve completely different roles:
| Alias | Target | Purpose | Are you allowed to import? |
|---|---|---|---|
#extensions | _public/extensions/core/backend/ | Cross-plugin imports without ../../../../ paths | Yes |
#widgets/plugin-registry | app/widgets/plugin-registry.js | Auto-generated widget map for the Pagebuilder render loop | No — not for user imports |
#extensions — cross-plugin imports
This alias is for user imports: when plugin A needs something from another plugin B (composable, Vue component, helper file), it imports it through this alias instead of through long relative paths.
The problem without an alias
Without #extensions, e-learning needs five ../ to reach the shop plugin:
// E-learning widget loading the shop user composable
import { registerUserCenterItem } from '../../../shop/widgets/user/template/useUserCenter'It works, but it's fragile:
- Breaks on restructuring
- Hard to read
- No IDE autocomplete from context
With #extensions
import { registerUserCenterItem } from '#extensions/shop/widgets/user/template/useUserCenter'Stable, readable, makes cross-plugin dependencies explicit.
Typical use cases
// Import the user-center registry (shop plugin)
import { registerUserCenterItem } from '#extensions/shop/widgets/user/template/useUserCenter'
// Vue component from another plugin
import MyCoursesView from '#extensions/elearning/widgets/elearning/template/MyCoursesUserCenter.vue'
// Helper from a plugin
import { resolvePageUrl } from '#extensions/menueditor/widgets/menu/helpers/urls'
// Plugin-local store import
import { useSw6Store } from '#extensions/shopware6/widgets/shared/sw6Store'Rule: everything under _public/extensions/core/backend/ is reachable via #extensions/....
Details on user-center registration: Widgets › User-Center Items.
#widgets/plugin-registry — NOT for user imports
This alias points to an auto-generated JavaScript file produced during the cache rebuild. It contains a widget_template → Vue component mapping and is consumed exclusively by the Pagebuilder render loop.
What the file looks like
// _theme/vue-base/app/widgets/plugin-registry.js
// AUTO-GENERATED — do not edit manually
// Theme: vue-base
import NewsletterWidget from '/absolute/path/.../emailmarketing/widgets/newsletter/template/index.vue'
import TitleBannerElementWidget from '/absolute/path/.../titleBannerElement/widgets/.../index.vue'
import ShopWidget from '/absolute/path/.../shop/widgets/shop/template/index.vue'
// … more imports per installed widget
export default {
'newsletter': NewsletterWidget,
'titleBannerElement': TitleBannerElementWidget,
'shop': ShopWidget,
'checkout': CheckoutWidget,
// …
}The file is generated by the core script vueRegistry.php (under _public/extensions/core/backend/cache_settings/script/) during the cache rebuild. The script scans every widgets/*/template/index.vue file and generates the imports automatically. The target path is theme-specific: _theme/{theme}/app/widgets/plugin-registry.js.
Who consumes it?
Only PagebuilderWidget.vue:
import widgetRegistry from '#widgets/plugin-registry'
const Component = computed(() => widgetRegistry[widget.type])At runtime, when rendering a placed widget, Vue looks up the matching component in the map via widget.type (e.g. "testimonial").
Don't use it in your own imports
#widgets/plugin-registry is not a stable import endpoint:
- The file is fully overwritten on every cache rebuild
- Keys are
widget_templatestrings, not stable identifiers - It contains absolute paths from the build context — only works in the active project's theme
If you need another widget's component from your own code: import it directly via #extensions/{plugin}/widgets/{widget}/template/index.vue.
Custom theme overrides
The registry generator respects theme-specific overrides:
_theme/{theme}/app/custom/{plugin}/widgets/{widget_template}/template/index.vueIf an override file exists, it's imported instead of the original. The generated plugin-registry.js then contains the override path under the same widget_template key. That lets themes override any plugin widget without modifying plugin code.
Details: Widgets › Widget Anatomy › Custom theme overrides.
Decision guide
| Situation | Alias |
|---|---|
| "I want to use a composable from another plugin" | #extensions/{plugin}/.../composable |
| "I want to import a Vue component from another plugin" | #extensions/{plugin}/widgets/.../index.vue |
| "I want to display the shop component inside my own widget" | #extensions/shop/widgets/.../index.vue |
| "I want to enumerate every Pagebuilder widget programmatically" | NOT through #widgets/plugin-registry — build your own scan logic |
"I want to load the widget component for widget_template=testimonial" | Direct via #extensions/{plugin}/widgets/testimonial/template/index.vue |
| "I want to debug the widget registry in the frontend" | One-off as a debug: import registry from '#widgets/plugin-registry'; console.log(registry) — but never in production |
When does plugin-registry.js get rebuilt?
The vueRegistry rebuild runs on:
/admin/cache→ "Clear cache"- Plugin install/uninstall (shop, e-learning, or your own plugin)
- A manual trigger (running
vueRegistry.phpdirectly)
Not automatically on:
- Nuxt dev-server start (
npm run devonly reloads Vue files, not the registry) - Repo checkout (
git pull) - A change to an existing
template/index.vue(that's already hot-reloaded; the registry stays valid)
Required on:
- A new widget under
widgets/{widget}/template/index.vue - A new custom theme override under
_theme/{theme}/app/custom/... - Renaming a
widget_templatein a plugin
Details on the cache system: Design tokens › Cache integration.
TypeScript typing for #extensions
By default, the alias has no types — every file behind it must export its own. Example:
// _public/extensions/core/backend/shop/widgets/user/template/useUserCenter.ts
export interface UserCenterItem { … }
export function registerUserCenterItem(item: UserCenterItem) { … }On import, the type information comes along automatically:
import { registerUserCenterItem, type UserCenterItem } from '#extensions/shop/widgets/user/template/useUserCenter'For Vue components from other plugins, defineComponent/defineProps work just like they do for local imports.
Common issues
Importing #widgets/plugin-registry
// WRONG — the registry is overwritten on cache rebuild, imports aren't stable
import widgetRegistry from '#widgets/plugin-registry'
// RIGHT — direct import via #extensions
import TestimonialWidget from '#extensions/widgetsBase/widgets/testimonial/template/index.vue'If you absolutely need registry access (e.g. a custom widget picker), build your own scan logic — don't consume this map.
Plugin folder names with hyphens or mixed case
#extensions/MenuEditor/... does not match menueditor/. The alias performs no normalization — exact folder name, exact case.
Importing the alias from a third-party package
#extensions only works in theme code (_theme/vue-base/app/ and below) and in plugin code that the theme consumes. npm packages have no access to the alias — they must use absolute or relative paths.
#extensions path not adjusted in a project fork
A project fork (_theme/projekt01/) has its own nuxt.config.ts. The #extensions path ../../_public/extensions/core/backend stays the same, because the plugin directory is global. But when you create a new theme, do not omit the #extensions entry — otherwise every cross-plugin import from widget code breaks.
See also
- Theme structure — where
nuxt.config.tslives inside the theme folder - Environment variables — the
.envhas no alias configuration - Deployment — cache rebuild as a deploy step
- Design tokens › Cache integration — when
plugin-registry.jsgets regenerated - Widgets › Widget Anatomy › Custom theme overrides — how overrides end up in the registry
- Widgets › User-Center Items — cross-plugin import via
#extensions