Before installing, make sure you have:
pnpm add better-convex-nuxt
npm install better-convex-nuxt
yarn add better-convex-nuxt
Add the module to your nuxt.config.ts:
export default defineNuxtConfig({
modules: ['better-convex-nuxt'],
convex: {
url: process.env.CONVEX_URL,
},
})
.env.local while Nuxt reads from .env. You need to configure them to share the same file..env.local)Step 1: Run Convex dev to generate .env.local with your CONVEX_URL:
pnpm convex dev
This creates .env.local containing:
CONVEX_URL=https://your-project-123.convex.cloud
CONVEX_DEPLOYMENT=dev:your-project-123
Step 2: Update your package.json to tell Nuxt to read from .env.local:
{
"scripts": {
"dev": "nuxt dev --dotenv .env.local"
}
}
Now both tools share the same environment file:
pnpm convex dev → writes to .env.localpnpm dev → reads from .env.local (via --dotenv flag)Create a simple query to verify everything works:
<script setup lang="ts">
import { api } from '~/convex/_generated/api'
const { data, status } = useConvexQuery(api.tasks.list, {})
</script>
<template>
<div>
<p v-if="status === 'pending'">Loading...</p>
<p v-else-if="status === 'error'">Error loading tasks</p>
<p v-else>Found {{ data?.length ?? 0 }} tasks</p>
</div>
</template>
If you see your task count, you're all set!
The module accepts these configuration options:
| Option | Type | Default | Description |
|---|---|---|---|
url | string | process.env.CONVEX_URL | Convex deployment URL (WebSocket) |
trustedOrigins | string[] | [] | Additional trusted origins for CORS on auth proxy |
permissions | boolean | false | Enable createPermissions factory for RBAC |
logging.enabled | boolean | 'debug' | false | Enable logging (true = info, 'debug' = verbose) |
logging.format | 'pretty' | 'json' | 'pretty' | Output format for logs |
export default defineNuxtConfig({
modules: ['better-convex-nuxt'],
convex: {
url: process.env.CONVEX_URL,
permissions: true, // Enable permission composables
logging: {
enabled: process.env.NODE_ENV === 'development', // Enable in dev
},
},
})
The module automatically derives the Convex HTTP site URL from your CONVEX_URL:
https://your-project.convex.cloud → https://your-project.convex.sitesiteUrl unless you're using a custom domain. Setting it to localhost or your app's URL will cause a self-request deadlock during SSR auth.For custom Convex domains, you can override the auto-derived URL:
export default defineNuxtConfig({
convex: {
url: process.env.CONVEX_URL,
siteUrl: 'https://api.your-custom-domain.com', // Only for custom domains
},
})
The auth proxy validates request origins against siteUrl by default. For multi-domain setups (staging, preview deployments), add trusted origins:
export default defineNuxtConfig({
convex: {
url: process.env.CONVEX_URL,
trustedOrigins: [
'https://staging.myapp.com',
'https://preview-*.vercel.app', // Wildcard for preview deployments
],
},
})
* to match any substring (e.g., https://preview-*.vercel.app matches https://preview-abc123.vercel.app).| Variable | Maps To | Description |
|---|---|---|
CONVEX_URL | convex.url | Convex deployment URL |
CONVEX_URL is the only URL you need to set for the module. The Convex HTTP site URL is auto-derived. SITE_URL (used by Better Auth in Convex) is a separate variable for your app's public callback URL.The module automatically provides:
useConvexQuery, useConvexMutation, useConvexAction, and more<ConvexAuthenticated>, <ConvexUnauthenticated>, <ConvexAuthLoading>fetchQuery, fetchMutation, fetchAction for API routesThis documentation assumes your Convex functions are in ~/convex (project root). If you use a different location, update your imports accordingly:
// Default: ~/convex
import { api } from '~/convex/_generated/api'
// If using src/convex
import { api } from '~/src/convex/_generated/api'
// If using a custom path
import { api } from '~/my-backend/_generated/api'
.env.local file)If you followed the recommended setup, pnpm convex dev created .env.local with your CONVEX_URL. Add additional variables as needed:
# Auto-generated by Convex CLI
CONVEX_URL=https://your-project-123.convex.cloud
CONVEX_DEPLOYMENT=dev:your-project-123
# Your app's public URL
# Development: use localhost
SITE_URL=http://localhost:3000
# Production: use your actual domain
# SITE_URL=https://your-app.com
.env.local, ensure your dev script includes --dotenv .env.local:"dev": "nuxt dev --dotenv .env.local"
Set these in the Convex Dashboard under Settings > Environment Variables:
# Secret for Better Auth (generate with: openssl rand -base64 32)
BETTER_AUTH_SECRET=ULPDeGxMKBd7TOROpEZfGhHqCErVvB7QNGLW5DKCkNs=
# Static JWKS for token validation (see Performance docs for generation)
JWKS=[{"alg":"RS256","createdAt":...,"privateKey":"...","publicKey":"..."}]
# Must match your Nuxt app's SITE_URL!
SITE_URL=http://localhost:3000
SITE_URL must be set in BOTH places and must match exactly:.env.local (or .env) filehttps://your-app.com).After changing environment files, restart your dev server - Nuxt only reads environment variables at startup.
This usually means Nuxt isn't reading the correct env file:
.env.local? Ensure your dev script includes --dotenv .env.local.env? Verify the file exists in your project root and contains CONVEX_URLFind your URL in the Convex dashboard under Settings.
If TypeScript doesn't recognize Convex types, run:
npx convex dev
This generates the _generated/ folder with type definitions.
Common causes:
SITE_URL mismatch - Most common issue! Ensure SITE_URL is identical in both:.env.local (or .env) fileBETTER_AUTH_SECRETJWKSSITE_URLhttp://localhost:3000 (not 127.0.0.1)See Authentication for complete setup.