Nuxt Layers & Theming for Shopware Composable Frontends
TL;DR: Deliver a first‑class theming and scaffolding system for Shopware Composable Frontends, based on Nuxt Layers, ensuring developers can spin up a performant, SEO‑ready, and extensible storefront with minimal setup and maximum consistency.
🎯 Why This Matters
Currently:
- Developers must scaffold many core UI pages (checkout, PDP, PLP, account) manually (#1784).
- Core UI components (filters, sliders, product cards) are missing or incomplete (#1765).
- No standardized theme layer exists — every project reinvents design tokens, structure, and theming approach.
The result:
- Slower time‑to‑market (~6–8 weeks average for a serious storefront integration).
- Inconsistent implementations across agencies and merchants.
- High ramp‑up cost for new developers unfamiliar with Shopware’s composables.
Solving this unblocks adoption of Shopware Frontends by:
- Reducing initial setup costs for agencies.
- Creating a consistent DX pattern.
- Improving perceived maturity of the Composable Frontends offering.
👤 User Personas
- Frontend Developers @ Agencies: Need templates & components that just work so they can focus on customization instead of rewriting boilerplate. Pain today: struggle with missing docs, broken SSR, and manual scaffolding.
- Merchant Tech Leads: Want confidence that a Shopware project can launch in weeks, not months. Pain today: poor predictability in cost/timeline because every storefront starts from scratch.
- Shopware Core Developers / Ecosystem Maintainers: Need consistency across community contributions. Pain today: smaller contributors reinvent different component APIs, leading to fragmentation.
🚀 Deliverables
- A Nuxt Layers Theme package published to npm.
- A Composable Frontends starter storefront using that theme.
- Full set of core pages + components shipped in layers (extensible, documented).
- DX docs + sandbox examples (Stackblitz templates); these shorten ramp-up and ensure “time-to-first-storefront” is minimal.
- #528
- #1291
- #1181
A closer look
Theme Layer (this epic): * A base Nuxt Layer Theme exists with design tokens (colors, typography, spacing). * Developers can extend/override the theme via configuration (not copy-paste). * Theme switching and multi‑theme support documented with examples. * TCO lens/keeping theme recent, getting fixes and updates from Shopware: "either 1) you extend the template, so you create own versions only of the components you want; 2) you copy the template and own whole thing; need to add SW changes on your own to avoid breaking your site. Additionally for things like API client, composables, helpers we have npm packages so this is only a matter of updating their versions and adjusting to eventual changes."— @patzick * Measure: A new developer can start a themed storefront in <1 hour (baseline today: ~1–2 days).
Pages (#1784): * Core customer‑facing pages exist out-of-the-box (Checkout, PDP, PLP, Landing, My Account, Registration). * Each page uses fully wired composables with extensibility slots. * Measure: Number of “must‑implement” pages reduced to zero; project setup time drops by ~20%.
Components (#1765): * All shared components (buttons, dropdowns, filters, product card, cart item, gallery, slider, etc.) are implemented as Nuxt Layer components. * Fix known SSR and multilingual blockers to drive consistency across locales and SSR: #1965, #1795, #1761, #184. * The storefront theme must "just work" with SEO (OG tags, alternate links) and caching defaults: #1631, #312, #1739, #618. * Payment Flow Docs + Integration → #1484, #1478, #1847, #538. Essential for Checkout experience in Nuxt Layers pages. * Composable extensibility to ensure agencies can adapt the theme cleanly without forking code: #1493, #1450, #536, #1270. * Measure: New intakes/developers use >80% of provided components without modification in first 3 sample storefronts tested.
Documentation & DX: * Guides exist for applying, customizing, and extending layers. * Demo repository provided (Stackblitz‑ready, no blockers). * Onboarding dev survey shows reduction in “time to first hello world storefront” to [X time-TBD]. * Checkout pages ship payment integrations documented with async flows.
⏳ Cost of Delay
- Agency inefficiency: Every project loses dev hrs and money on boilerplate theming and pages.
- Adoption cliff: Agencies trial Shopware but churn because initial experience feels incomplete.
- Competitive risk: Other headless solutions (e.g. Vue Storefront, Next.js Commerce) have more polished “starter kits”.
✅ Acceptance Criteria
- Time-to-first storefront: new dev gets a themed shop running in <1 hour.
- Dev feedback survey: >=80% agree “Composable Frontends are production-ready” in agency partner survey.
Earlier notes / more context
Assumptions
Structure
We propose constructing a complex structure with two levels of layers. The domain layers provide foundational components for each domain, such as checkout, product, account, and so on. The template layer, on the other hand, leverages these domain layers to form the overall template structure.
Domain layers can work without template layer, but template layer cannot without domain layers.
Types
Each layer should depend on @shopware/api-client to utilise the types generated from OpenAPI.
<script setup lang="ts">
import type { Schemas } from "@shopware/api-client/store-api-types";
type Product = Schemas["Product"];
const props = defineProps<{
product: Product;
}>();
</script>
<template>
<div>{{ product }}</div>
</template>
Business logic / component communication
(?) We agree to prepare to create 2 POCs
- [ ] Reuse composables + add new UI composables
- [ ] Use events/hooks for the communication (no composable dependencies)
Translations
Each layer should depend on the vue-i18n module. Translations in .json files in the module
Context data
Context data refers to global information that should be accessible throughout the application. For example, it includes data such as currency, date format, and other session-specific details that need to be fetched only once
The decision on how it will be handled will be made after the Business Logic / Component Communication POC
Solution to the problem
- [x] Use Storybook as a wrapper for the new layers/components
- [x] Develop some essential components
- [x] Use basic components in 2-3 examples to prove that this concept is working
- [ ] Develop all components from the Figma project
- [ ] Create a template from the Figma project
@mdanilowicz @patzick @BrocksiNet this is a rough summary of our PoC and the recent assumptions.
Nuxt Template Layer — Base Architecture & Extensions
This repository provides a reusable Nuxt layer (template) that acts as the foundation for building Nuxt 3 storefronts and CMS-driven apps. It is designed to be extended, themed, and customized across brands and deployment types.
Architecture Overview
@shopware/composables/layer ← business logic (cart, product, account, etc.)
@shopware/cms-base/layer ← CMS rendering (slots, CMS routes, blocks)
▲
│
template ← pages, layouts, UI components, theme, plugins
What the template Layer Includes (nuxt 3 structure yet)
| Directory | Purpose |
|---|---|
pages/ |
App routes like homepage, checkout, login, product, etc. |
layouts/ |
Global and route-specific layout shells |
components/ |
UI components (CMS-aware, fully themed) |
assets/ |
CSS tokens, design system, fonts |
composables/ |
Project-specific logic (optional) |
plugins/ |
Project-specific plugins (e.g. analytics, injections) |
app.vue |
Root shell |
nuxt.config.ts |
Extends external logic layers and defines app settings |
uno.config.ts |
Definitions for UnoCSS - tailwind presets by default |
Extending the Template
You can create many types of projects by extending the template layer. Below are real-world scenarios:
1. Single Page Application (SPA)
// projects/spa/nuxt.config.ts
export default defineNuxtConfig({
extends: ['@your-org/nuxt-template-layer'],
pages: false,
css: ['~/assets/css/spa.css'],
});
With a custom app.vue:
<template>
<LandingPage />
<ProductShowcase />
<ContactForm />
</template>
2. Dark Mode Variant using UnoCSS
Enable UnoCSS in the base template:
// template/nuxt.config.ts
export default defineNuxtConfig({
modules: ['@unocss/nuxt'],
unocss: {
dark: 'class',
},
});
Extend with a dark-mode layer:
// projects/dark-mode-a/nuxt.config.ts
export default defineNuxtConfig({
extends: ['@your-org/nuxt-template-layer'],
appConfig: {
theme: 'dark',
},
});
Use dark class in layout:
<template>
<div :class="{ dark: $config.public.theme === 'dark' }">
<NuxtPage />
</div>
</template>
Style using UnoCSS utilities:
<div class="bg-white text-black dark:bg-neutral-900 dark:text-white p-4">
This box changes in dark mode.
</div>
3. Brand-Specific Storefront
// projects/brand-x/nuxt.config.ts
export default defineNuxtConfig({
extends: ['@your-org/nuxt-template-layer'],
css: ['~/assets/css/brand-x.css'],
runtimeConfig: {
public: {
shopName: 'BrandX',
currency: 'EUR',
}
}
});
Add overrides:
/projects/brand-x/components/Header.vue
/projects/brand-x/assets/css/brand-x.css
4. Internationalized Version
// projects/template-intl/nuxt.config.ts
export default defineNuxtConfig({
extends: ['@your-org/nuxt-template-layer'],
modules: ['@nuxtjs/i18n'],
i18n: {
locales: ['en', 'de', 'fr'],
defaultLocale: 'en',
vueI18n: './i18n.config.ts',
},
});
Add locale files in /locales.
5. Developer Sandbox
// projects/dev-sandbox/nuxt.config.ts
export default defineNuxtConfig({
extends: ['@your-org/nuxt-template-layer'],
runtimeConfig: {
public: {
useMockData: true,
}
}
});
In a composable:
const config = useRuntimeConfig();
if (config.public.useMockData) {
return fetchMockProducts();
}
External Dependencies
This layer depends on:
@shopware/composables/layer: Business logic (cart, product, etc.)@shopware/cms-base/layer: CMS block routing & layout rendering
Best Practices
- Use the
templatelayer for all app-level logic, layouts, and overrides - Extend it for custom branding, internationalization, or simplified UIs
- Keep logic in
@shopware/composables/layer, and use slots/components for display
Abandoned Architecture Approaches with Nuxt Layers
During the planning and implementation of our layered Nuxt architecture, we explored several patterns and structures. Below is a list of the strategies we considered but ultimately did not adopt, along with reasons for abandoning them.
1. Feature-Specific Logic Layers
Proposed Structure
cart-logic/checkout-logic/product-logic/
Each logic layer would provide composables and utilities for its domain, consumed by separate UI layers.
Why We Abandoned It
- Introduced too many layers and cross-dependencies
- Harder to track where a composable originated
- Increased boilerplate and coordination between logic and UI
- Required extra configuration and plugin resolution across layers
2. UI Per Feature Domain (e.g., ui-cart, ui-checkout)
Proposed Structure
ui-cart/ui-checkout/ui-product/
Each feature would have its own UI layer, depending on its logic layer.
Why We Abandoned It
- Too fragmented: lots of small packages to maintain
- Increased difficulty when components needed to be shared across features
- Styling, theming, and global config became harder to manage
- Composing multiple UI layers into one project added unnecessary complexity
3. Event Bus with mitt for Communication Between Layers
Proposed Idea
Use a global event bus to loosely couple components and logic across layers.
eventBus.emit('cart:add', { item });
Why We Abandoned It
- Lacked type safety and visibility
- Harder to debug and trace app behavior
- Risk of memory leaks without careful lifecycle management
- Not well suited for Nuxt's SSR and plugin lifecycle
4. Provide/Inject Pattern for Core Logic Access
Proposed Idea
Manually provide logic via plugins and inject it in components.
provide('cart', useCart());
Why We Abandoned It
- More verbose than using auto-imported composables
- Complicates overrides and dynamic extension across layers
- Adds friction for developers unfamiliar with Vue’s
provide/injectAPI
5. Layer Per Domain (e.g., Cart, Checkout) with Shared Core
Proposed Stack
core/
└─ useCart()
cart-logic/
└─ extends core
cart/
└─ extends cart-logic
Why We Abandoned It
- Too deeply nested for our needs
- Hard to manage overrides (which layer should replace
useCart?) - Difficult to onboard new contributors
- Risked introducing circular dependencies if not carefully structured
Conclusion
While Nuxt layers are powerful, they can also lead to over-architected solutions that are harder to maintain. We chose to simplify by:
- Providing one self-contained template layer
- Extending only external logic layers (
@shopware/composables/layer,@shopware/cms-base/layer) - Consolidating all UI, layout, and routing into the template itself
This approach allows for scalability, customization, and maintainability without unnecessary complexity.
Work plan from late Aug 2025
This aims to drive small increments of value for faster feedback loops.
Increment 1: Lay down the base Nuxt Layer, unblock onboarding, fix reliability blockers, and ship developer docs scaffolding.
1️⃣ Nuxt Layer Base & Theme Infrastructure
- Implement the core Nuxt Layer (colors, typography, spacing, theme config) (this epic)
-
- Provide default theme with override/extension mechanism.
2️⃣ Essential Reliability Fixes (Blockers)
- Fix SSR language/currency inconsistencies #1795.
- Patch mixed language after login/logout #1965.
- Fix language switch on subpages #1761.
- Ensure category route generation is correct #1842.
3️⃣ Onboarding / DX
- Draft step-by-step deployment/setup doc #1291
- resolve dead internal links #1181
4️⃣ Ship First Core Components
- Implement buttons, input fields, basic layout containers
- Document composable override patterns #1450, #536
Increment 2: Deliver feature-complete customer-facing pages using the layer, provide all critical UI building blocks, and ensure performance/SEO are first-class.
1️⃣ Pages (Plug & Play)—implement and document:
- Product Listing Page (PLP) #1786
- Product Detail Page (PDP) #1787
- Checkout Page #1788 * My Account, Registration, Addresses, Cart, Order History #1921, #1919, #1926, #1925, #1924, #1923, #1922
2️⃣ Themed Components Library—Deliver:
- product card [#1782], cart item #1783, filter dropdown #1776], sidebar filter #1775, slider #1777, dropdown navigation #1781, checkbox #1773, switch #1774, etc. * Ensure accessibility, SSR/SEO-compliance.
- Ensure “kitchen sink” page is available in demo.
3️⃣ Payment & Async Flows—integrate and document async payment handling (PayPal Express, Braintree, etc.) #1484, #1478, #538, #1847.
4️⃣ Performance & SEO Defaults
- ship caching strategies, edge caching docs #1631, #312.
- Implement: alternate language links #1739, OpenGraph tags.
- Lighthouse/SEO optimization as acceptance gating.
5️⃣ Feedback Loops
- Run agency/user intake: time to first styled storefront, pain points, gather survey metrics.