Skip to content

File Naming Conventions

Kimesh uses file-based routing with intuitive naming conventions. This page documents all supported patterns.

Quick Reference

PatternDescriptionExample URL
__root.vueRoot layoutWraps all routes
index.vueIndex route/ or /posts
about.vueRegular page/about
$id.vueDynamic param/posts/:id
$.vueCatch-all (splat)/:pathMatch(.*)*
_auth.vuePathless layoutNo URL segment
(admin)/Group folderNo URL segment (folder only)
posts.vue + posts/Layout route/posts with children
posts_.vueLayout escapeForces layout type
posts.index.vueFlat notation/posts
-utils.tsExcluded fileNot a route
[x]Escape characterapi[.]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
template
<!-- __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       # /docs

Regular Pages: name.vue

Any .vue file becomes a route:

routes/
├── about.vue           # /about
├── contact.vue         # /contact
└── pricing.vue         # /pricing

Dynamic Parameters: $param.vue

Use $ prefix for dynamic route segments:

routes/
├── users/
│   └── $id.vue         # /users/:id
├── posts/
│   └── $slug.vue       # /posts/:slug
└── $username.vue       # /:username

Multiple dynamic segments:

routes/
└── repos/
    └── $owner/
        └── $repo.vue   # /repos/:owner/:repo

Access params in your component:

template
<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:

template
<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    # /settings

The 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
template
<!-- _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/new

The posts.vue component wraps all routes in the posts/ directory:

template
<!-- 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     # /users

This is equivalent to the nested structure:

routes/
├── posts/
│   ├── index.vue       # /posts
│   ├── $id.vue         # /posts/:id
│   └── new.vue         # /posts/new
└── users/
    └── index.vue       # /users

Excluded 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 definitions

Group 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    # /settings

Group 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.config

Complete 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
  • /aboutabout.vue
  • /login_auth/login.vue (wrapped by _auth.vue)
  • /register_auth/register.vue (wrapped by _auth.vue)
  • /blogblog/index.vue (wrapped by blog.vue)
  • /blog/:slugblog/$slug.vue (wrapped by blog.vue)
  • /docsdocs/index.vue
  • /docs/*docs/$.vue
  • /*$.vue (404)

Released under the MIT License.