A layer is a Nuxt project fragment that another Nuxt project extends. Structurally it is an ordinary Nuxt app directory (nuxt.config.ts, app/components/, app/composables/, server/, app/app.config.ts); the consuming app pulls it in with extends or by placing it in layers/, and Nuxt treats the two as one application: every layer's directories are scanned, every layer's config is deep-merged, and the project always wins.
For a team, that is the mechanism behind "every app starts from the same place". A base layer carries the design system, the shared nuxt.config preset (modules, ESLint, fonts, icons, route rules) and the composables everyone needs; feature layers add auth, analytics or a CMS integration; each product app extends what it needs and overrides by placing a file at the same path. The role you are interviewing for is to design, publish and maintain exactly those layers, so this section is the spine of the guide and the module content is its supporting cast.
nuxt.options._layers; extends just appends more of them. That is why a layer's directory structure is identical to an app's, and why the layer starter template ships a .playground/ app that extends it.'../base', './layers/base'), an npm package ('@team/nuxt-layer-base'), or a git repository fetched with giget ('github:team/layer#v1.2.0'). Local layers/* folders are auto-registered since Nuxt 3.12; git and npm layers must be listed in extends.server/ routes and middleware, nuxt.config.ts (including modules and runtimeConfig), app.config.ts, public/ assets, i18n messages, type declarations. Anything Nuxt discovers by convention.nuxt.config (that is how it ships a preset), and Nuxt deduplicates modules declared by several layers.nuxt.config, then everything is merged with defu: objects deep-merge, arrays concatenate, and the higher-priority layer's scalar wins. The same rule applies to app.config.layers/ folders (alphabetical, so Z beats A) → extends entries in order (first beats second). Two layers defining app/components/Hero.vue is not an error; the higher one silently wins.import.meta.url), because ~ and @ resolve against the consuming project. This is the single most common layer bug.The shape that keeps coming back in senior interviews, and the one this guide assumes:
| Package | Kind | Ships |
|---|---|---|
@team/nuxt-layer-base | layer | nuxt.config preset, app.config theme tokens, design-system components, shared composables, ESLint/TS conventions |
@team/nuxt-layer-auth | layer | auth pages, middleware, session composables, server/api/auth/* |
@team/nuxt-toolkit | module | build-time machinery: generated types, virtual files, transforms, hooks, DevTools tab |
Layers ship app code and conventions; modules ship build-time behaviour and injected runtime. A company toolkit is usually both, and the layer lists the module in its modules array so consumers get everything from one extends entry.
This app extends a real layer: layers/base holds the Nuxt UI module registration, the CSS entry (resolved with import.meta.url), the fonts and icon configuration, the app.config colour tokens and the callout components used on these pages. The project's app/app.config.ts overrides one value from the layer (site.name), which you can see in the sidebar. Open those files while reading the next pages; every rule below is visible there.
"A layer is a partial Nuxt app that other apps extend. It ships conventions and app code: config presets, components, composables, pages, server routes, app.config. Nuxt scans every layer's directories and deep-merges every layer's config, with the project winning and earlier layers beating later ones, so a consuming app inherits everything and overrides by placing a file at the same path. I reach for a layer when I want to standardise how apps are built and look; when I need something to happen at build time, generated code or a Vite transform, that is a module, and a team toolkit is usually a layer that lists the module."
Candidates often describe layers as "modules with files". The difference is where the code runs: a module's setup() runs in Node at build time and mutates nuxt.options; a layer never runs, it is merged. If you catch yourself wanting an if in a layer, you want a module, or a nuxt.config option the consumer sets.
Create a fresh app with npm create nuxt -- --template layer nuxt-team-base, read every file it generates, and write down which directory each one would be scanned into by a consuming app. Then create a second app, add extends: ['../nuxt-team-base'], and run nuxt dev with debug: true to see the layer appear in the resolved _layers.