---
title: "Plain Vue (without Nuxt)"
description: "Wire ConvexVueClient into any Vue 3 app — providing the client, importing the composables, and installing auth state without Nuxt automating it."
canonical_url: "https://nuxt-convex-module.dev/guide/plain-vue"
---
# Plain Vue (without Nuxt)

> Wire ConvexVueClient into any Vue 3 app — providing the client, importing the composables, and installing auth state without Nuxt automating it.

Every `/client` entry has a `/vue` alias — the Vue-facing half of that integration. `nuxt-convex-module/vue`, `clerk/vue`, `auth0/vue` and `polar/vue` are plain Vue 3, no Nuxt required: the Nuxt module only automates the wiring below, and in a bare Vue app (Vite, or any other setup) you do it yourself in `main.ts`. `better-auth/vue` is the exception — it resolves the `#convex/auth-client` alias and runtime config, so it needs Nuxt.

Nuxt-only conveniences you give up: auto-imports (import every composable and component from `nuxt-convex-module/vue` instead), the [`#convex/*` aliases](https://nuxt-convex-module.dev/getting-started/configuration#import-aliases) (import your generated modules directly), the automatic client/api provide plugin, [`useAsyncQuery`](https://nuxt-convex-module.dev/guide/server-and-ssr#useasyncquery-ssr-data-the-nuxt-way) (`nuxt-convex-module/app`), the Nitro server utilities from [Server & SSR](https://nuxt-convex-module.dev/guide/server-and-ssr), and the whole [Better Auth](https://nuxt-convex-module.dev/components/better-auth) integration.

## Providing the client

The client travels through Vue's provide/inject under `ConvexClientKey` (an `InjectionKey<ConvexVueClient>`) — the Vue analog of `convex/react`'s `<ConvexProvider>`. Every composable throws `Could not find Convex client!` when it is missing; `useConvex()` itself returns `undefined`.

```ts [main.ts]
import { createApp } from 'vue'
import {
  ConvexVueClient,
  ConvexClientKey,
  provideConvexApi,
} from 'nuxt-convex-module/vue'
import { api } from '../convex/_generated/api'
import App from './App.vue'

// This is the one place the client options can be set — under Nuxt the
// module constructs the client without them.
const client = new ConvexVueClient(import.meta.env.VITE_CONVEX_URL, { verbose: true })

const app = createApp(App)
app.provide(ConvexClientKey, client)
provideConvexApi(api, app)
app.mount('#app')
```

The WebSocket opens lazily on the first subscription. If you ever tear the app down, call `await client.close()` to dispose of it.

`provideConvexApi(api, app)` stores your generated `api` under `ConvexApiKey` so the zero-argument conveniences work — `useConvexApi()` / `useConvexNamespace()` and the [Polar billing components](https://nuxt-convex-module.dev/components/polar) defaulting to `api.billing`. It's optional: `useQuery(api.messages.list)` and friends take the function reference explicitly and don't need it. Pass the `app` argument when calling it from `main.ts`; without it, `provideConvexApi` uses `provide()` and must run inside a component's setup.

## Using the composables

Import them where Nuxt would have auto-imported:

```vue [components/Messages.vue]
<script setup lang="ts">
import { useQuery, useMutation } from 'nuxt-convex-module/vue'
import { api } from '../convex/_generated/api'

const messages = useQuery(api.messages.list)
const send = useMutation(api.messages.send)
</script>

<template>
  <ul>
    <li v-for="message in messages" :key="message._id">{{ message.body }}</li>
  </ul>
</template>
```

## Auth wiring

Inside a component's setup, `provideConvexAuth` is the whole story — it builds the reactive `ConvexAuthState`, wires the client's `setAuth`/`clearAuth` watchers, and provides the state under `ConvexAuthStateKey` for `useConvexAuth()` and the `<Authenticated>` / `<Unauthenticated>` / `<AuthLoading>` / `<AuthRefreshing>` components below it:

```vue [App.vue]
<script setup lang="ts">
import { provideConvexAuth, useConvex } from 'nuxt-convex-module/vue'
import { useMyAuth } from './auth' // your auth provider's composable

const client = useConvex()
const { isLoading, isAuthenticated } = provideConvexAuth({ client, useAuth: useMyAuth })
</script>
```

Your `useAuth` composable returns `{ isLoading, isAuthenticated, fetchAccessToken }` (plus an optional `authVersion`). `fetchAccessToken` is an async function returning the JWT (or `null`); pass it as a `Ref` whose identity changes — or bump `authVersion` — when re-authentication is needed. See [Auth State](https://nuxt-convex-module.dev/guide/auth-state) for the full contract.

### Outside a component

`provideConvexAuth` needs a setup context. To install auth state at the app level from `main.ts`, use `createScopedConvexAuthState` and provide the result yourself:

```ts [main.ts]
import { createScopedConvexAuthState, ConvexAuthStateKey } from 'nuxt-convex-module/vue'

const { state, scope } = createScopedConvexAuthState({ client, useAuth: myUseAuth })
app.provide(ConvexAuthStateKey, state)

// on teardown:
scope.stop()
```

It runs the auth watchers inside a detached `EffectScope` and returns it so you can dispose them with `scope.stop()`. The lower-level `createConvexAuthState(options, scope?)` builds the same state without providing it — but called outside a component setup with no scope, its watchers can never be disposed, so prefer the scoped variant there.

<note>

Full signatures for all of these live in the [API reference](https://nuxt-convex-module.dev/api-reference/reference/client).

</note>


## Sitemap

See the full [sitemap](https://nuxt-convex-module.dev/sitemap.md) for all pages.
