Layer System
Kimesh's layer system allows you to build modular applications by sharing routes, components, and composables across projects.
Overview
Layers are self-contained modules that can extend your application with:
- Routes (placed in correct directory structure)
- Components (with auto-prefixing)
- Composables
- CSS files
How Layers Work
Routes in layers must be placed in the correct directory structure matching their real URL path. For example, a blog layer with routes at /blog/* should have:
blog-layer/
├── routes/
│ ├── blog.vue # Layout for /blog
│ └── blog/
│ ├── index.vue # /blog
│ └── $postId.vue # /blog/:postId
└── kimesh.config.tsThis ensures routes are explicit and predictable - no magic path prefixing.
Priority System
Layers are resolved with a priority system:
| Priority | Source | Description |
|---|---|---|
| 0 | App | Your application (highest priority) |
| 1+ | extends array | Layers in kimesh.config.ts (order preserved) |
| N+ | layers/ directory | Auto-scanned layers (alphabetical) |
Higher priority layers override lower priority ones when conflicts occur.
Configuring Layers
Using extends Array
Add layers in your kimesh.config.ts:
import { defineKmConfig } from '@kimesh/kit'
export default defineKmConfig({
extends: [
// Local layer (relative path)
'./layers/cms',
// NPM package
'@kimesh-layers/auth',
// Workspace package
'@company/admin-layer',
],
})Auto-Scanning layers/ Directory
Kimesh automatically scans the layers/ directory:
my-app/
├── src/
│ └── routes/
├── layers/
│ ├── admin/ # Auto-discovered
│ │ └── routes/
│ │ └── admin/
│ │ └── index.vue # /admin
│ └── cms/ # Auto-discovered
│ └── routes/
│ └── cms/
│ └── index.vue # /cms
└── kimesh.config.tsLayer Structure
A layer can contain any of these directories:
my-layer/
├── routes/ # Route files (directory structure = URL path)
├── components/ # Vue components
├── composables/ # Composable functions
└── kimesh.config.ts # Layer configuration (optional)Layer Configuration
Create a kimesh.config.ts in your layer:
import { defineKmConfig } from '@kimesh/kit'
export default defineKmConfig({
name: 'admin',
// Route configuration
routes: {
folder: 'routes', // Default: 'routes'
},
// Component configuration
components: {
dirs: ['components'],
prefix: 'Admin', // AdminButton, AdminCard, etc.
},
// Composable configuration
composables: {
dirs: ['composables'],
},
// CSS files to include
css: ['css/admin.css'],
})Source Types
Layers can come from different sources:
Local Layers
Layers within your project:
extends: [
'./layers/admin',
'../shared-layers/common',
]NPM Packages
Published layer packages:
extends: [
'@kimesh-layers/auth',
'@kimesh-layers/cms',
]Workspace Packages
In a monorepo:
extends: [
'@company/layer-auth',
'@company/layer-admin',
]Route Structure Convention
Routes must be placed in directories matching their URL path:
# Blog layer → routes at /blog/*
blog-layer/routes/
├── blog.vue # Layout wrapper for /blog
└── blog/
├── index.vue # /blog
├── $slug.vue # /blog/:slug
└── new.vue # /blog/new
# Admin layer → routes at /admin/*
admin-layer/routes/
├── admin.vue # Layout wrapper for /admin
└── admin/
├── index.vue # /admin
├── users.vue # /admin/users
└── settings.vue # /admin/settingsHost Layout Integration
If your host app has a layout route that matches a layer's routes, the layer's routes will be nested under the host layout:
# Host app
host/src/routes/
├── __root.vue # Root layout
└── blog.vue # Host's blog layout (wraps layer routes)
# Blog layer
blog-layer/routes/
├── blog.vue # Layer's blog layout
└── blog/
└── index.vue # /blog
# Result: __root.vue → host/blog.vue → layer/blog.vue → layer/blog/index.vueComponent Prefixing
Components from layers are automatically prefixed to avoid conflicts:
// Layer 'admin' components become:
components/
├── Button.vue # <AdminButton />
├── Card.vue # <AdminCard />
└── Table.vue # <AdminTable />Disable prefixing:
export default defineKmConfig({
components: {
prefix: false,
},
})Conflict Resolution
When multiple layers define the same route, component, or composable:
- Higher priority wins - App (priority 0) always wins
- Explicit configuration -
extendsarray order is preserved - Alphabetical fallback - Auto-scanned layers use alphabetical order
Example Conflict
# Host app defines /admin
host/src/routes/
└── admin.vue # /admin (priority 0 - wins)
# Admin layer also defines /admin
admin-layer/routes/
└── admin/
└── index.vue # /admin (priority 1 - nested under host)Layer Aliases
Each layer gets path aliases for imports:
// In a component
import { useAuth } from '#layers/auth/composables/useAuth'
import AdminLayout from '#layers/admin/components/Layout.vue'Context-Aware Alias Resolution
Kimesh provides context-aware resolution for ~/ and @/ aliases. When you import using these aliases, they resolve relative to the importing file's layer, not globally to the host app.
How It Works
// In layer: my-layer/src/routes/feature.vue
import Sidebar from '~/components/Sidebar.vue'
// Without context-aware: Resolves to host/src/components/Sidebar.vue ❌
// With context-aware: Resolves to my-layer/src/components/Sidebar.vue ✅Alias Resolution Rules
| Alias | Resolves To |
|---|---|
~/ | Layer root path |
@/ | Layer source path (src/ if exists, else root) |
Benefits
- Self-contained layers: Layers can use standard aliases without worrying about host conflicts
- No path rewriting: Keep familiar
~/and@/patterns in layer code - Automatic fallback: Falls back to app layer if file isn't in any layer
Virtual Module Support
The resolver also handles virtual modules (like route definitions) by detecting which layer the original file belongs to and resolving aliases accordingly.
// In route definition extracted to virtual module
// Still resolves @/api/client relative to the route's layer
import { apiClient } from '@/api/client'Creating a Layer Package
1. Create Layer Structure
my-layer/
├── routes/
│ └── my-feature/
│ └── index.vue
├── components/
│ └── MyComponent.vue
├── composables/
│ └── useMyFeature.ts
├── kimesh.config.ts
└── package.json2. Configure package.json
{
"name": "@company/my-layer",
"version": "1.0.0",
"exports": {
".": "./kimesh.config.ts"
},
"files": ["routes", "components", "composables", "kimesh.config.ts"]
}3. Configure the Layer
// kimesh.config.ts
import { defineKmConfig } from '@kimesh/kit'
export default defineKmConfig({
name: 'my-layer',
components: {
dirs: ['components'],
},
composables: {
dirs: ['composables'],
},
})4. Publish
npm publishAPI Reference
KimeshLayerConfig
interface KimeshLayerConfig {
name: string
path: string
routes?: {
folder?: string // Default: 'routes'
include?: string[]
exclude?: string[]
}
components?: {
dirs?: string[]
prefix?: string | false
global?: boolean
extensions?: string[]
}
composables?: {
dirs?: string[]
extensions?: string[]
}
css?: string[]
}ResolvedLayer
interface ResolvedLayer {
name: string
path: string
priority: number
config: KimeshLayerConfig
extends: string[]
isApp: boolean
source: 'local' | 'npm' | 'workspace'
aliases: Record<string, string>
}