Vue

Vue 3 integration with Litestar Vite using the Composition API and Single File Components.

Quick Start

litestar assets init --template vue

This creates a Vue 3 project with TypeScript support.

Project Structure

Vue applications use SPA mode - Vite serves the index.html directly:

my-app/
├── app.py              # Litestar backend (API only)
├── index.html          # Vite entry point (root level)
├── package.json
├── vite.config.ts
├── tsconfig.json
└── src/
    ├── main.ts         # Vue entry point
    ├── App.vue         # Root component
    └── style.css

Backend Setup

In SPA mode, Litestar serves only your API endpoints. The VitePlugin handles serving the frontend automatically:

examples/vue/app.py
vite = VitePlugin(
    config=ViteConfig(
        mode="spa",
        dev_mode=DEV_MODE,
        paths=PathConfig(root=here),
        types=TypeGenConfig(),
        runtime=RuntimeConfig(port=5011),
    )
)

app = Litestar(route_handlers=[LibraryController], plugins=[vite], debug=True)

Key points:

  • mode="spa" tells Vite to serve index.html for non-API routes

  • No template_config needed - Jinja is not used in SPA mode

  • TypeGenConfig() enables TypeScript type generation from OpenAPI

Vite Configuration

vite.config.ts
import tailwindcss from "@tailwindcss/vite"
import vue from "@vitejs/plugin-vue"
import litestar from "litestar-vite-plugin"
import { defineConfig } from "vite"

export default defineConfig({
  plugins: [
    tailwindcss(),
    vue(),
    litestar({
      input: ["src/main.ts"],
      resourceDir: "src",
    }),
  ],
})

Key configuration:

  • input - Entry point(s) for your application

  • resourceDir - Source directory (explicitly set to src)

  • The plugin auto-reads assetUrl, bundleDir from .litestar.json (generated by Python)

HTML Entry Point

In SPA mode, index.html lives at the project root and uses standard Vite syntax:

index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>vue</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>

Note

Unlike template mode, SPA mode doesn’t use Jinja helpers like {{ vite() }}. Vite processes the HTML directly.

Vue Component

<script setup lang="ts">
import { ref, onMounted } from "vue";

interface Summary {
  app: string;
  headline: string;
}

const summary = ref<Summary | null>(null);

onMounted(async () => {
  const res = await fetch("/api/summary");
  summary.value = await res.json();
});
</script>

<template>
  <div>
    <h1>Vue + Litestar</h1>
    <p v-if="summary">{{ summary.headline }}</p>
  </div>
</template>

Running

# Recommended: Litestar proxies Vite automatically in dev mode
litestar run --reload

# Alternative: Two-process setup
litestar assets serve  # Vite dev server
litestar run --reload  # Backend only (in another terminal)

Type Generation

With types=TypeGenConfig() enabled, run:

litestar assets generate-types

This generates TypeScript types from your OpenAPI schema. See Type Generation for more details.

See Also