Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/dvlkit/nuxe/llms.txt

Use this file to discover all available pages before exploring further.

Nuxe discovers your app’s pages, layouts, components, composables, middleware, and plugins by convention — it scans specific directories and wires everything together automatically. You do not need to configure entry points, import paths, or router files by hand. The directory tree below shows the full layout that Nuxe understands; every directory and file has a precise role, and only app/app.vue is strictly required to get a working app.

Directory tree

my-app/
├── app/
   ├── app.vue               # Root component — must render <NuxeLayout> and <RouterView />
   ├── error.vue             # Custom error page (receives an `error` prop)
   ├── pages/                # File-based routing — each .vue file becomes a route
   ├── index.vue         # → /
   ├── about.vue         # → /about
   └── admin/
       └── users.vue     # → /admin/users
   ├── layouts/              # Reusable layout shells
   ├── default.vue       # Used by pages that do not declare a layout
   └── admin.vue         # Used by pages with meta: { layout: 'admin' }
   ├── components/           # Auto-imported Vue components
   ├── hello.vue         # Available as <Hello /> everywhere
   └── forms/
       └── input.vue     # Available as <FormsInput /> everywhere
   ├── composables/          # Auto-imported composables
   └── use-counter.ts    # Available as useCounter() in any <script setup>
   ├── middleware/           # Route middleware files
   ├── auth.global.ts    # Runs on every navigation (client + server)
   ├── auth.server.global.ts  # Runs on every navigation, SSR only
   └── admin.ts          # Named middleware — opt-in per page
   └── plugins/              # Nuxe plugins (run before app mount)
       ├── hello.ts          # Runs on both client and server
       ├── client-only.client.ts  # Runs on client only
       └── server-only.server.ts  # Runs on server only
├── server/
   └── api/                  # Server API route handlers
       ├── ping.get.ts       # → GET /api/ping
       └── whoami.get.ts     # → GET /api/whoami
├── nuxe.config.ts            # Optional framework config
├── tsconfig.json
└── package.json
The .nuxe/ directory is created automatically at build and dev time. It contains generated virtual modules, type declarations, and internal scaffolding. You should never edit files inside .nuxe/ — it is fully managed by the framework and is excluded from version control by the scaffolded .gitignore.

Files and directories

app/app.vue

The root component of your application. It is the single entry point Nuxe renders on every request. At minimum it must include <RouterView /> so that the active page is displayed. In most apps it also contains site-wide navigation and wraps the view in <NuxeLayout> to enable layout switching:
app/app.vue
<script setup lang="ts">
import { useHead } from '@dvlkit/nuxe'
import { NuxeLoadingIndicator } from '@dvlkit/nuxe'
import { NuxeLayout } from '@dvlkit/nuxe/components/nuxe-layout'

useHead({
  title: 'my-app',
})
</script>

<template>
  <NuxeLoadingIndicator />
  <nav>
    <RouterLink to="/">Home</RouterLink>
    <RouterLink to="/about">About</RouterLink>
  </nav>
  <NuxeLayout>
    <RouterView />
  </NuxeLayout>
</template>

app/pages/

Every .vue file placed here becomes a route. The file path relative to app/pages/ maps directly to the URL:
FileRoute
pages/index.vue/
pages/about.vue/about
pages/admin/users.vue/admin/users
No router configuration file is needed. Nuxe scans the directory on startup and regenerates the route table automatically when files are added or removed in development.

app/layouts/

Layout components are shells that wrap page content. Each layout file must expose a <slot /> where the page will be rendered. Nuxe resolves the active layout at runtime based on the page’s meta.layout value:
app/layouts/default.vue
<template>
  <header>My App</header>
  <main>
    <slot />
  </main>
</template>
Pages that do not declare a layout use layouts/default.vue. The meta.layout name is case-insensitive — admin and Admin both resolve to layouts/admin.vue. Adding a new layout is as simple as creating a new file in this directory.

app/components/

Vue components in this directory are auto-imported across all templates and <script setup> blocks — no import statements required. Nuxe derives the component name from the file path relative to app/components/, joining folder segments with PascalCase:
FileAuto-import name
components/hello.vue<Hello />
components/forms/input.vue<FormsInput />

app/composables/

TypeScript or JavaScript files in this directory are auto-imported into every <script setup> block. A file that exports a default function named useCounter is available as useCounter() without any import. Named exports are also scanned and made available by their export name.
Auto-imports work for both default and named exports. If a composable file exports multiple utilities, all of them are available globally inside <script setup>.

app/middleware/

Route middleware files export a handler created with defineNuxeRouteMiddleware (auto-imported). The filename controls when the middleware runs:
Filename patternBehavior
name.tsNamed middleware — must be opted into per page via definePage
name.global.tsRuns automatically on every navigation (client and server)
name.server.global.tsRuns automatically on every navigation during SSR only
Example named middleware:
app/middleware/admin.ts
export default defineNuxeRouteMiddleware((to, from) => {
  console.log('[admin]', from.path, '->', to.path)
})

app/plugins/

Plugin files export a handler created with defineNuxePlugin (auto-imported). Plugins run before the Vue app is mounted and receive the NuxeApp instance, which exposes config (the resolved runtime config) and lifecycle hooks such as app:mounted. The filename suffix controls the execution environment:
Filename patternRuns on
name.tsClient and server
name.client.tsClient only
name.server.tsServer only
app/plugins/hello.ts
export default defineNuxePlugin((nuxeApp) => {
  console.log('[plugin:hello] app:created', nuxeApp.config.public.apiBase)

  nuxeApp.hook('app:mounted', () => {
    console.log('[plugin:hello] app:mounted')
  })
})

app/error.vue

An optional custom error page rendered whenever Nuxe catches an unhandled error or a route throws a NuxeError. It receives an error prop typed as NuxeError (containing statusCode and statusMessage):
app/error.vue
<script setup lang="ts">
import { useHead } from '@dvlkit/nuxe'
import type { NuxeError } from '@dvlkit/nuxe'

const props = defineProps<{
  error: NuxeError
}>()

useHead({
  title: `${props.error.statusCode}${props.error.statusMessage}`,
})
</script>

<template>
  <h1>{{ error.statusCode }}</h1>
  <p>{{ error.statusMessage }}</p>
  <RouterLink to="/">Go home</RouterLink>
</template>

server/api/

Files in this directory are compiled into server-side API route handlers. The filename encodes the HTTP method and the URL path:
FileEndpoint
server/api/ping.get.tsGET /api/ping
server/api/whoami.get.tsGET /api/whoami
Handlers use h3 primitives imported from @dvlkit/nuxe/server:
server/api/ping.get.ts
import { defineEventHandler, getQuery, setResponseStatus } from '@dvlkit/nuxe/server'

export default defineEventHandler(async (event) => {
  const query = getQuery(event)

  if (query.delay) {
    await new Promise((resolve) => setTimeout(resolve, Number(query.delay)))
  }

  return {
    message: 'pong',
    timestamp: Date.now(),
    query: typeof query.q === 'string' ? query.q : null,
  }
})
The API prefix defaults to /api and can be changed via server.apiPrefix in nuxe.config.ts.

nuxe.config.ts

The optional framework configuration file. Create it only when you need to deviate from defaults. It must use defineConfig imported from @dvlkit/nuxe (or @dvlkit/nuxe/config) to get full TypeScript type checking:
nuxe.config.ts
import { defineConfig } from '@dvlkit/nuxe'

export default defineConfig({
  server: {
    port: 4000,       // default: 3000
    apiPrefix: '/v1', // default: '/api'
  },
  runtimeConfig: {
    // server-only (private)
    apiSecret: process.env.API_SECRET ?? '',
    // exposed to the client under useRuntimeConfig().public
    public: {
      apiBase: '/v1',
    },
  },
  vite: {
    // any Vite UserConfig option
  },
  baseUrl: 'https://my-app.example.com',
})
All keys are optional. An empty defineConfig({}) is perfectly valid and results in all defaults being applied.

Build docs developers (and LLMs) love