If You Build Multiple Applications, Nuxt Layers Are Your Lifesaver
Nuxt layers let you package reusable parts of your application — components, configuration, plugins, server APIs, authentication, payments, and more — so every new project starts with your best work already in place.
Every developer has a collection of things they repeat in almost every project.
You install the same modules. You configure Tailwind CSS. You set up Nuxt Image. You create the same buttons, containers, loading states, error messages, and utility composables. Then, if the project needs authentication, you build the login page, session handling, middleware, API requests, and error states all over again.
After doing this a few times, you start to wonder:
Why am I rebuilding things I have already built?
For a long time, my answer was copying code from an older project.
I would open one of my previous Nuxt applications, find the parts I needed, copy them into the new project, fix the imports, update the configuration, and hope I had not forgotten anything important.
That works, but it is not a system.
The moment you improve the copied code in one project, every other copy becomes outdated. A bug fix in one project does not fix the others. A better authentication flow does not magically appear in the five applications that are still using the older version.
This is exactly the kind of problem Nuxt layers solve.
If you are developing Nuxt applications—especially if you are a freelancer, work at an agency, maintain several products, or repeatedly build similar systems—Nuxt layers can save you an enormous amount of time.
They have certainly saved me.
What Is a Nuxt Layer?
A Nuxt layer is, in simple terms, a reusable piece of a Nuxt application.
It can contain much more than a group of Vue components. A layer can include:
- Nuxt configuration
- App configuration
- Components
- Composables
- Utilities
- Pages and layouts
- Route middleware
- Plugins
- Server API routes
- Server middleware
- Shared client-and-server code
Its structure is almost identical to the structure of a normal Nuxt application. That is one reason layers feel natural once you start using them: you are not learning an entirely separate architecture. You are packaging part of a Nuxt application so another Nuxt application can extend it.
You can keep a layer inside the same repository, publish it as an npm package, place it in another local directory, or load it from a Git repository.
A component library shares components.
A Nuxt layer can share an entire working piece of your application.
That difference becomes very important as your projects grow.
Why Nuxt Layers Matter So Much for Freelancers
When you are working on only one application, repeating a setup might not feel like a major problem.
When you are maintaining five applications, it becomes painful.
A freelancer may build:
- A company website this month
- A dashboard next month
- A subscription product after that
- An internal administration panel
- Another client application with almost the same authentication requirements
These products may look different, but underneath, they often need many of the same things.
They need sensible defaults. They need image handling. They need internationalization. They need error handling. They may need authentication, payments, subscriptions, caching, rate limiting, or a reusable design system.
Nuxt layers allow you to turn those repeated requirements into reusable foundations.
Instead of beginning every project with an empty Nuxt installation, you can begin with months—or even years—of your previous work already available.
That is the real benefit.
It is not only about writing fewer lines of code. It is about starting with code that has already been used, tested, debugged, and improved in real applications.
The Nuxt Layers I Use in My Own Projects
I have been using layers for quite a while now, and my setup has gradually grown into several reusable layers.
Some of these layers are relatively small. Others are complex systems that took months to build and refine.
Here are a few examples.
My Nuxt Base Layer
The first one is my base layer.
This is the layer that almost every one of my Nuxt projects extends. It contains the basic decisions and setup I do not want to repeat every time I start a project.
It includes things such as:
- Nuxt UI configuration
- Tailwind CSS configuration
- Internationalization setup
- Nuxt Image configuration
- Common components
- Reusable composables
- Shared utilities
- Basic layouts and UI patterns
- Common application defaults
It also contains infrastructure features I commonly need, including caching and rate limiting.
I have written separate practical guides about both:
Those features do not need to be manually reconstructed whenever I create a new application. The base implementation is already there, and each project can configure or override it where necessary.
As a result, when I create a new project using this layer, it does not feel like I am starting from zero.
A considerable part of the boring foundational work is already finished.
My Authentication Layer
I also have an authentication layer.
This one is more complex because authentication is never just a login form.
A real authentication system may involve:
- Login and registration interfaces
- Session handling
- API communication
- Protected routes
- Authentication middleware
- User state
- Error handling
- Password recovery
- Logout behavior
- Backend endpoints
- Loading and empty states
My authentication layer handles both the application UI and the backend integration surrounding authentication.
Whenever one of my projects needs authentication, I do not have to reinvent the entire process. I extend the layer, provide the project-specific configuration, and override whatever is unique to that application.
The authentication system can still look and behave differently in each project. The important point is that its stable foundation is already built.
My Payment Layer
Another layer I maintain handles payments.
It is not responsible for deciding what the application sells. It does not need to know whether the product is a subscription, a physical item, a service, or something else.
Instead, it provides an abstraction around the payment process itself.
It includes concepts such as:
- Creating and tracking payments
- Payment history
- Successful-payment interfaces
- Failed-payment interfaces
- Vendor configuration
- Payment-provider adapters
- Shared server-side payment logic
Because payment providers can be different, the layer uses adapters. The rest of the application communicates with a consistent interface while the adapter handles the details of each payment vendor.
That means I can reuse the general payment workflow while changing the payment provider or project-specific business logic.
Subscriptions and Shops
I also have separate reusable packages or layers for subscriptions and shop-related functionality.
The point is not that every project automatically receives every feature. That would create a bloated and unnecessarily complicated foundation.
The point is that I have created separate building blocks.
A simple website might use only the base layer.
A membership platform might use:
- The base layer
- The authentication layer
- The payment layer
- The subscription layer
An e-commerce application might use another combination.
This lets me assemble a project from existing, tested parts without forcing every project into exactly the same structure.
“But AI Can Generate All of This Now”
Whenever I discuss reusable architecture, there is an obvious argument:
Why maintain all these layers when AI can generate the code for each new project?
I use AI heavily in development, but I do not believe this removes the value of layers.
AI can generate a login page quickly. It can generate an API endpoint. It can help configure a module or create a payment component.
But generating code is not the same as having a system that has already survived real projects.
Some of my layers took months to build, even with AI.
That time was not spent typing every line manually. It was spent making decisions, discovering edge cases, fixing incorrect assumptions, improving the API, simplifying configuration, handling failures, and learning what should or should not be reusable.
AI can help you create the first version faster.
It does not automatically give you the accumulated knowledge contained in a mature implementation.
A good layer contains more than code. It contains your decisions.
It remembers the problems you encountered in previous projects so that you do not have to encounter all of them again.
Creating a Simple Nuxt Base Layer
Let us create a small base layer to demonstrate the idea.
A Nuxt layer needs a nuxt.config.ts file, even when that file is nearly empty. Beyond that, its directory can resemble a regular Nuxt application.
For this example, imagine the following structure:
nuxt-base-layer/
├── app/
│ ├── components/
│ │ └── BaseContainer.vue
│ └── composables/
│ └── useAppName.ts
├── public/
│ └── images/
├── nuxt.config.ts
├── package.json
└── tsconfig.jsonThis layer will:
- Install and configure Nuxt Image
- Define a reusable runtime configuration value
- Provide a basic container component
- Provide a simple composable
Step 1: Create the Layer Configuration
Inside the layer, create nuxt.config.ts:
export default defineNuxtConfig({
modules: <span class="text-token-text-primary cursor-text rounded-sm" data-placeholder-token="true">[
'@nuxt/image',
]</span>,
image: {
quality: 80,
format: <span class="text-token-text-primary cursor-text rounded-sm" data-placeholder-token="true">['webp']</span>,
},
runtimeConfig: {
public: {
appName: 'My Nuxt Application',
},
},
})This is a deliberately small example, but the same file could contain the shared module setup and defaults you normally add to every project.
When an application extends this layer, it receives this configuration.
Step 2: Add a Reusable Component
Create app/components/BaseContainer.vue:
<template>
<div class="mx-auto w-full max-w-7xl px-4 sm:px-6 lg:px-8">
<slot />
</div>
</template>The application extending the layer can now use <BaseContainer> like any other auto-imported Nuxt component.
<template>
<BaseContainer>
<h1>My new project</h1>
</BaseContainer>
</template>There is no need to copy the component manually into every application.
Step 3: Add a Reusable Composable
Create app/composables/useAppName.ts:
export function useAppName() {
const config = useRuntimeConfig()
return computed(() => config.public.appName)
}You can then use it in an application extending the layer:
<script setup lang="ts">
const appName = useAppName()
</script>
<template>
<h1>{{ appName }}</h1>
</template>Again, this is a small example. In a real base layer, the composables might handle notifications, API errors, pagination, user preferences, analytics, or other patterns shared by your projects.
Using the Layer in a Nuxt Project
You can extend a local layer from the project's nuxt.config.ts:
export default defineNuxtConfig({
extends: <span class="text-token-text-primary cursor-text rounded-sm" data-placeholder-token="true">[
'../nuxt-base-layer',
]</span>,
})Nuxt also supports extending layers from npm packages and Git repositories.
For example:
export default defineNuxtConfig({
extends: <span class="text-token-text-primary cursor-text rounded-sm" data-placeholder-token="true">[
'@your-scope/nuxt-base',
]</span>,
})Or from a GitHub repository:
export default defineNuxtConfig({
extends: <span class="text-token-text-primary cursor-text rounded-sm" data-placeholder-token="true">[
'github:your-name/nuxt-base-layer',
]</span>,
})For private projects, I generally prefer keeping shared layers in a private package registry, a private Git repository, or a monorepo, depending on how the projects are organized.
The Real Power: Layers Can Be Overridden
This is where Nuxt layers become much more useful than copying code or maintaining a basic component library.
A layer can provide a default, while the application using it can replace that default.
Suppose the layer contains:
app/components/BaseButton.vueYour project can create its own component with the same path and name:
app/components/BaseButton.vueThe project-level version has higher priority, so Nuxt uses it instead of the version provided by the layer. Project files take priority over layer files. When several layers are involved, their order also determines which implementation wins.
This means the layer does not trap you.
It gives you a default, but the final project remains in control.
That distinction is extremely important.
You might use the default button in ten projects, while one particular application needs a completely different version. You can replace it only in that application without modifying the shared layer or affecting the other projects.
The same idea applies to:
- Components
- Pages
- Layouts
- Composables
- Middleware
- Plugins
- Server routes
- Configuration
You can start from the common implementation and replace only what is genuinely different.
Overriding Runtime Configuration Per Project
Configuration is another major reason I prefer layers over simply sharing components.
Imagine the base layer provides a default application name and API configuration:
export default defineNuxtConfig({
runtimeConfig: {
privateApiKey: '',
public: {
appName: 'Default Application',
apiBase: '/api',
},
},
})A project extending the layer can supply its own values:
export default defineNuxtConfig({
extends: <span class="text-token-text-primary cursor-text rounded-sm" data-placeholder-token="true">[
'@your-scope/nuxt-base',
]</span>,
runtimeConfig: {
public: {
appName: 'Customer Dashboard',
apiBase: 'https://api.example.com',
},
},
})The layer establishes the expected configuration and sensible defaults. The individual project supplies its own values.
Environment variables can then provide deployment-specific values:
NUXT_PUBLIC_APP_NAME="Customer Dashboard"
NUXT_PUBLIC_API_BASE="https://api.example.com"This pattern allows the reusable layer to understand what it needs without hard-coding the details of a specific project.
For example, my rate-limiting setup can provide the general implementation while each application chooses its own limits.
A small public website and an authenticated API should not necessarily have the same rate limits. The layer gives them the shared mechanism, while runtime configuration gives each project control over the final behavior.
The same principle can apply to:
- API addresses
- Feature flags
- Authentication settings
- Payment-provider settings
- Upload limits
- Cache durations
- Analytics identifiers
- Application names and branding
Layers Versus Component Libraries
A component library is useful when the main thing you want to share is UI.
You might package:
- Buttons
- Inputs
- Modals
- Cards
- Tables
- Form components
A Nuxt layer can include all of that, but it can also include the parts surrounding the UI.
An authentication layer, for example, may contain:
- Login components
- Authentication composables
- Route middleware
- Server endpoints
- Plugins
- Runtime configuration
- Pages and layouts
That is not merely a component library. It is a reusable application feature.
I do not see layers and component libraries as competitors. A layer can use a separate component library, or it can provide its own components.
The right choice depends on what you are trying to reuse.
Use a component library when you want reusable UI that should work independently of a specific Nuxt application architecture.
Use a Nuxt layer when you want to reuse Nuxt configuration, conventions, routes, server code, application features, or a combination of all of them.
Local Layers Inside a Single Application
Layers are not useful only for sharing code between separate projects.
You can also use them to organize a large Nuxt application.
Nuxt automatically recognizes valid layers inside the layers/ directory. This makes it possible to divide a large application by feature or domain.
For example:
layers/
├── 1.base/
│ ├── app/
│ └── nuxt.config.ts
├── 2.account/
│ ├── app/
│ ├── server/
│ └── nuxt.config.ts
├── 3.billing/
│ ├── app/
│ ├── server/
│ └── nuxt.config.ts
└── 4.admin/
├── app/
├── server/
└── nuxt.config.tsThis can be useful when one application has several clearly separated domains.
Nuxt gives local layers a priority order. The project itself has the highest priority, while the ordering of local and extended layers determines which files win when two layers define the same resource. Numbering layer directories is one practical way to make this order explicit.
I would not split every small feature into a layer. That can create unnecessary complexity.
But for large, independent areas of an application, layers can provide a clean boundary.
Do Not Put Everything in the Base Layer
Once you see how powerful layers are, it is tempting to put everything into one enormous base layer.
I do not recommend that.
A base layer should contain the things that are genuinely common across most of your applications.
If only one out of ten projects needs a complicated shop system, that shop probably should not be part of the base layer.
Separate optional features into separate layers or packages.
A useful structure might be:
@your-scope/nuxt-base
@your-scope/nuxt-auth
@your-scope/nuxt-payments
@your-scope/nuxt-subscriptions
@your-scope/nuxt-shopThen each project extends only what it needs.
This keeps the layers easier to understand, test, update, and replace.
It also prevents a simple marketing website from inheriting the dependencies and configuration of an entire e-commerce system.
Start With Code You Have Already Repeated
You do not need to design the perfect layer system before you begin.
In fact, I would not recommend trying.
Start by paying attention to repetition.
When you find yourself copying the same configuration, component, composable, or middleware into a third project, that is a good candidate for a layer.
My own layers did not begin as complete systems.
They grew gradually.
I moved the most stable and repetitive parts first. Then I used them in real projects, found the assumptions that were too specific, improved their configuration, and separated the features that did not belong together.
That process is important.
Reusable code becomes good through reuse—not through trying to predict every possible project in advance.
What Nuxt Layers Changed for Me
Before using layers properly, a new project meant repeating a long list of setup tasks.
Install the modules. Configure them. Copy the components. Recreate authentication. Reconnect the payment flow. Add the same utilities. Discover that the copied version came from an older project. Fix the same problems again.
Now, starting a project feels different.
I can choose the foundations the project needs, extend them, provide the required configuration, and immediately begin working on what makes that particular product unique.
That does not mean the project is magically finished.
It means I can spend less time rebuilding solved problems.
And that is what good reusable architecture should do.
It should not remove flexibility. It should remove unnecessary repetition.
Final Thoughts
If you build and maintain multiple Nuxt applications but have never used layers, start with one small base layer.
Move in the configuration you use everywhere.
Add one or two genuinely common components. Add a composable you have copied between several projects. Use the layer in your next application, then override something at the project level so you understand how the priority system works.
Do not try to build your authentication, payment, subscription, and shop layers on the first day.
Let them grow from actual repeated work.
After some time, your layers become more than shared code. They become a record of everything you have learned while building Nuxt applications.
Every fixed bug, improved abstraction, safer default, and better architectural decision becomes available to your next project.
That is why, for developers managing multiple Nuxt applications, layers are not merely convenient.
They are a lifesaver.
Hi, Im a seniour software engineer building web platforms. I write about Nuxt, Typescript, DevOps, AI and engineering decisions behind real products, based on my real experience.