File Naming Conventions
Kimesh uses file-based routing with intuitive naming conventions. This page documents all supported patterns.
Quick Reference
| Pattern | Description | Example URL |
|---|---|---|
__root.vue | Root layout | Wraps all routes |
index.vue | Index route | / or /posts |
about.vue | Regular page | /about |
$id.vue | Dynamic param | /posts/:id |
$.vue | Catch-all (splat) | /:pathMatch(.*)* |
_auth.vue | Pathless layout | No URL segment |
(admin)/ | Group folder | No URL segment (folder only) |
posts.vue + posts/ | Layout route | /posts with children |
posts_.vue | Layout escape | Forces layout type |
posts.index.vue | Flat notation | /posts |
-utils.ts | Excluded file | Not a route |
[x] | Escape character | api[.]v1 → /api.v1 |
Detailed Conventions
Root Layout: __root.vue
The root layout wraps all routes in your application. It's the entry point for your route hierarchy.
routes/
└── __root.vue # Root layout<!-- __root.vue -->
<template>
<div id="app">
<AppHeader />
<main>
<RouterView />
</main>
<AppFooter />
</div>
</template>Index Routes: index.vue
index.vue renders at the parent directory's path:
routes/
├── index.vue # /
├── blog/
│ └── index.vue # /blog
└── docs/
└── index.vue # /docsRegular Pages: name.vue
Any .vue file becomes a route:
routes/
├── about.vue # /about
├── contact.vue # /contact
└── pricing.vue # /pricingDynamic Parameters: $param.vue
Use $ prefix for dynamic route segments:
routes/
├── users/
│ └── $id.vue # /users/:id
├── posts/
│ └── $slug.vue # /posts/:slug
└── $username.vue # /:usernameMultiple dynamic segments:
routes/
└── repos/
└── $owner/
└── $repo.vue # /repos/:owner/:repoAccess params in your component:
<script setup lang="ts">
import { useRoute } from 'vue-router'
const route = useRoute()
const userId = route.params.id
</script>Catch-All Routes: $.vue
The $.vue file creates a catch-all (splat) route that matches any remaining path:
routes/
├── docs/
│ └── $.vue # /docs/* (matches /docs/any/path/here)
└── $.vue # /* (global 404)Access the matched path:
<script setup lang="ts">
import { useRoute } from 'vue-router'
const route = useRoute()
// For /docs/getting-started/installation
// route.params.pathMatch = ['getting-started', 'installation']
</script>Pathless Layouts: _name.vue
Files prefixed with _ create layouts without adding a URL segment:
routes/
├── _auth.vue # Pathless layout (no URL segment)
├── _auth/
│ ├── login.vue # /login
│ └── register.vue # /register
├── _dashboard.vue # Another pathless layout
└── _dashboard/
├── index.vue # /
└── settings.vue # /settingsThe underscore prefix creates a layout that wraps child routes without affecting the URL path. This is useful for:
- Shared authentication wrappers
- Common layouts for specific sections
- Grouping routes with shared state
<!-- _auth.vue -->
<template>
<div class="auth-layout">
<AuthBackground />
<div class="auth-card">
<RouterView />
</div>
</div>
</template>Nested Pathless Layouts: Pathless layouts can be nested inside other pathless layouts. The route merger recursively collects all routes:
routes/
├── _dashboard.vue # Outer pathless layout
├── _dashboard/
│ ├── _admin.vue # Nested pathless layout
│ ├── _admin/
│ │ └── users.vue # /users (wrapped by both)
│ └── home.vue # /home (wrapped by _dashboard only)Layer Integration: When merging layer routes, pathless layouts use the rawSegment property (e.g., _dashboard) to correctly match layer routes that belong under them.
Layout Routes: name.vue + name/
When a file has a matching directory, it becomes a layout for routes in that directory:
routes/
├── posts.vue # Layout for /posts/*
└── posts/
├── index.vue # /posts
├── $id.vue # /posts/:id
└── new.vue # /posts/newThe posts.vue component wraps all routes in the posts/ directory:
<!-- posts.vue (layout) -->
<template>
<div class="posts-layout">
<PostsSidebar />
<div class="posts-content">
<RouterView />
</div>
</div>
</template>Flat Route Notation: name.segment.vue
Use dots to create nested routes in a flat file structure:
routes/
├── posts.index.vue # /posts
├── posts.$id.vue # /posts/:id
├── posts.new.vue # /posts/new
└── users.index.vue # /usersThis is equivalent to the nested structure:
routes/
├── posts/
│ ├── index.vue # /posts
│ ├── $id.vue # /posts/:id
│ └── new.vue # /posts/new
└── users/
└── index.vue # /usersExcluded Files: -name.vue
Files prefixed with - are excluded from routing. Use this for colocating related code:
routes/
├── posts/
│ ├── index.vue # /posts (included)
│ ├── -PostCard.vue # Excluded - local component
│ ├── -utils.ts # Excluded - helper utilities
│ └── -types.ts # Excluded - type definitionsGroup Folders: (name)/
Folders wrapped in parentheses create pathless groups - the folder name doesn't appear in the URL:
routes/
├── (marketing)/
│ ├── pricing.vue # /pricing
│ └── features.vue # /features
├── (app)/
│ ├── dashboard.vue # /dashboard
│ └── settings.vue # /settingsGroup folders are useful for organizing routes without affecting URL structure. Unlike pathless layouts (_name.vue), group folders don't create a wrapping component - they only affect file organization.
Layout Escape: name_.vue
A trailing underscore forces a file to be treated as a layout, even without a matching directory:
routes/
├── admin_.vue # Layout at /admin (no directory needed)
├── admin/
│ └── users.vue # /admin/users (wrapped by admin_.vue)This is useful when you want a layout but the directory name conflicts with other conventions.
Escape Characters: [x]
Use brackets to escape special characters in filenames:
routes/
├── api[.]v1.vue # /api.v1
├── file[[]name.vue # /file[name
└── script[.]config.vue # /script.configComplete Example
routes/
├── __root.vue # Root layout (wraps everything)
├── index.vue # /
├── about.vue # /about
├── _auth.vue # Pathless auth layout
├── _auth/
│ ├── login.vue # /login
│ └── register.vue # /register
├── blog.vue # Layout for /blog/*
├── blog/
│ ├── index.vue # /blog
│ ├── $slug.vue # /blog/:slug
│ └── -PostCard.vue # Excluded component
├── docs/
│ ├── index.vue # /docs
│ └── $.vue # /docs/* (catch-all)
└── $.vue # /* (404 page)Generated routes:
/→index.vue/about→about.vue/login→_auth/login.vue(wrapped by_auth.vue)/register→_auth/register.vue(wrapped by_auth.vue)/blog→blog/index.vue(wrapped byblog.vue)/blog/:slug→blog/$slug.vue(wrapped byblog.vue)/docs→docs/index.vue/docs/*→docs/$.vue/*→$.vue(404)