@lupinum/better-convex-nuxt
Better Convex
Use Convex in Nuxt and Vue. Pages render on the server and then update live in the browser.
[!WARNING] These packages are prerelease software. Read the changelog before every upgrade. The authentication component cannot reuse the database of another Better Auth integration. Start it with a fresh component.
Why use Better Convex?
Convex is a backend with a realtime database. Its queries push new results to the browser when data changes. Better Convex connects Convex to Nuxt and Vue:
useConvexQueryloads a query during server rendering, sends the result with the page, and keeps it live in the browser.useConvexMutation,useConvexAction,useConvexForm, anduseConvexFileUploadreturn reactivepending,error, anddatastate.- Writes, uploads, and
useConvexOperationbelong to the user who started them. When that user changes, no later request is sent, and the error tells whether the last one was sent. - A failed call rejects with a
ConvexCallErrorthat has amessage, acode, and thefunctionNamethat failed.useConvexFormreports validation and submission failures asConvexFormError. serverConvex(event)calls Convex from a Nitro route as the signed-in user.- Optional Better Auth support keeps sessions in a Convex component and signs users in to Convex.
- An optional MCP package lets AI hosts, such as ChatGPT and Claude, call your Convex functions as tools.
Better Convex is a set of composables and helpers, not a framework. It does not require a registry or a fixed folder layout. The only generated code is Convex's own API and, when you use Better Auth, the auth component that better-convex init writes to convex/betterAuth/ with its generated schema.
When to use it
Use @lupinum/better-convex-nuxt in a Nuxt 4.6+ application (Nuxt 5 is tested on its nightly while it is a pre-release). Use @lupinum/better-convex-vue in a Vue application without Nuxt, for example a Vite single-page application. Use @lupinum/better-convex-agents to let MCP hosts and agents call your Convex functions.
Better Convex does not decide who may read or change data. Check the user, ownership, membership, and roles in every Convex function. Route middleware and hidden buttons do not protect data.
Requirements
- Node.js
^22.22.3 || ^24.15.0 || >=26.0.0for the Nuxt package,^22.19.0 || ^24.11.0 || >=26.0.0for the Vue package, and^22.19.0 || ^24.11.0for the Functions and Agents packages - Nuxt
^4.6.0 || ^5.0.0-0, tested on 4.6.1 and the Nuxt 5 nightly (pre-release until Nuxt 5 ships) - Convex
>=1.42.2 <2 - Vue
>=3.5 <4 - For authentication:
better-auth,@better-auth/core, and@better-auth/oauth-provider, each at exactly1.7.7
Installation
Install the module in a Nuxt application:
pnpm add @lupinum/better-convex-nuxt@next convex@^1.42.2
The next dist-tag is the 1.0 release candidate. Upgrading from a beta?
See MIGRATING.md.
Add the module to your Nuxt configuration:
export default defineNuxtConfig({
modules: ['@lupinum/better-convex-nuxt'],
})
Connect a Convex deployment. This command signs you in to Convex, creates or selects a deployment, writes its URLs to .env.local, and creates the convex/ folder:
pnpm exec better-convex convex configure
Keep it running while you work on Convex functions. It pushes each change to your development deployment.
The default Nuxt template shows a welcome screen instead of your pages. Replace app/app.vue:
<template>
<NuxtPage />
</template>
Quick start
Add a Convex query:
import { query } from './_generated/server'
export const list = query({
args: {},
handler: async (ctx) => await ctx.db.query('tasks').order('desc').take(50),
})
This query is public on purpose. Anyone can call it. Add authentication and an owner check before you store real data.
Show it on a page:
<script setup lang="ts">
import { api } from '#convex/api'
const { data: tasks, status, error } = await useConvexQuery(api.tasks.list)
</script>
<template>
<p v-if="status === 'pending'">Loading tasks…</p>
<p v-else-if="error">Could not load tasks: {{ error.message }}</p>
<ul v-else>
<li v-for="task in tasks" :key="task._id">{{ task.text }}</li>
</ul>
</template>
Start Nuxt in a second terminal:
pnpm exec nuxt dev --dotenv .env.local
The server renders the list. After the page loads, the list stays live: add a row to the tasks table in the Convex dashboard, and the page updates without a reload.
A query without arguments may omit the arguments object. Pass 'skip' instead of arguments to pause a query. Follow the first realtime page guide for the full walkthrough.
Agent setup
@lupinum/better-convex-nuxt ships its documentation inside the package for coding
agents such as Claude Code, Codex and Cursor. The pages match the installed
version, so an agent does not follow the website for a different version.
To set it up, give your coding agent this task:
Set up @lupinum/better-convex-nuxt for coding agents as described under "Agent setup" in its README.
Steps for the agent
-
Find
node_modules/@lupinum/better-convex-nuxt/dist/agent/AGENTS.md. Look in the directory of the project that depends on@lupinum/better-convex-nuxtfirst, then in its parent directories up to the repository root (workspaces can hoist packages). Read it; it lists the documentation pages. -
Add the section below to the project's agent instructions:
AGENTS.md, orCLAUDE.mdif the project has only that file. If it has neither, createAGENTS.md. Write the path relative to the repository root, throughnode_modules/@lupinum/better-convex-nuxt(for exampleapps/web/node_modules/@lupinum/better-convex-nuxt/...in a workspace). Never write a resolved path such asnode_modules/.pnpm/...: it contains the version and breaks after an upgrade. If a section for@lupinum/better-convex-nuxtalready exists, leave it as it is.Use the path you found in place of the sample path:
## @lupinum/better-convex-nuxt Before you change code that uses @lupinum/better-convex-nuxt, read `node_modules/@lupinum/better-convex-nuxt/dist/agent/AGENTS.md` and the pages it lists. They document the installed version. Prefer them over what you remember about this package and over the website. -
Do not copy the documentation into the project and do not install a skill. The section points into the installed package, so it stays correct after every upgrade or downgrade.
If the file does not exist, the installed version has no packaged documentation. Read the package README and its TypeScript types instead.
Add Better Convex to an application
Give your coding agent this task:
Add Better Convex to this application. Install @lupinum/better-convex-nuxt@next
(or @lupinum/better-convex-vue@next for Vue without Nuxt). Set it up for coding
agents as described under "Agent setup" in the package README. Then read
node_modules/<package>/dist/agent/AGENTS.md and follow its "Start here" table.
Finish with the "Check your setup" steps of the Installation page.
Server calls and mutations
Call a mutation from a component. Destructure the function and its state:
const { mutate: createTask, pending, error } = useConvexMutation(api.tasks.create)
await createTask({ text: 'Review the release' })
createTask rejects with a ConvexCallError. When your Convex function throws new ConvexError({ code: 'TASK_LOCKED', message: 'This task is locked' }), the error has message 'This task is locked', code 'TASK_LOCKED', and functionName 'tasks:create'.
Call Convex from a Nitro route with serverConvex. Create one caller in each request:
import { defineEventHandler } from 'nuxt/server'
import { api } from '#convex/api'
import { serverConvex } from '#convex/server'
export default defineEventHandler(async (event) => {
const convex = serverConvex(event)
return await convex.query(api.tasks.list)
})
Do not keep a caller between requests. requireConvexUser(event) returns the signed-in user or throws a 401 error. toConvexH3Error(error) turns any error into an HTTP error (NuxtError) with a matching HTTP status. Import defineEventHandler and other server utilities from nuxt/server.
Authentication
Authentication is off until you add a convex.auth object to the Nuxt configuration. To turn it on, install the exact Better Auth versions and run the setup command:
pnpm add better-auth@1.7.7 @better-auth/core@1.7.7 @better-auth/oauth-provider@1.7.7
pnpm exec better-convex init --typed-client
The command shows every file it will write and asks before it writes. It asks again before it sets development secrets or creates the first signing key. It writes the proxy secret to .env.local and sets the same value in Convex. --typed-client also writes app/convex-auth.ts, the file for Better Auth client plugins. The command does not configure production.
The browser talks to Better Auth through /api/auth on your own origin. Nuxt forwards these requests to Convex. useConvexAuth() returns the auth status, the user, and the Better Auth client. In Convex functions, auth.requireUser(ctx) returns the signed-in user or throws.
No sign-in method is on until you name it in createBetterConvexAuth: pass emailAndPassword: {} for passwords (verified addresses required), emailOTP: {} for email codes, or socialProviders. Apps that decide access by email should use email codes.
Follow the add authentication guide before you enable auth.
Packages
| Package | Use it for |
|---|---|
@lupinum/better-convex-nuxt |
Nuxt: server rendering, Nitro calls, file uploads, DevTools, and Better Auth. |
@lupinum/better-convex-vue |
Vue without Nuxt: queries, mutations, actions, forms, uploads, and errors. |
@lupinum/better-convex-functions |
Optional: one policy and one row rule per table for every Convex function. |
@lupinum/better-convex-agents |
Tools, approvals and an MCP endpoint for hosts such as ChatGPT and Claude. |
The agents package is separate. Installing the Nuxt or Vue package does not start an MCP server.
Documentation
Read the Better Convex documentation. Start with choose your path. Read the mental model and the limitations before you plan a larger application.
The API reference page lists every public export.
Contributing and development
Read CONTRIBUTING.md before you open a pull request. Run the full check before you submit a change:
corepack enable
pnpm install
pnpm verify
AGENTS.md lists every command. Releases use Changesets: a maintainer merges the "Version packages" pull request and approves the publish, which uses npm trusted publishing with provenance.
Support and security
Open a GitHub issue for bugs and focused proposals. Join the Lupinum OSS Discord for project discussion.
Report a vulnerability privately as described in SECURITY.md. Do not report a vulnerability in a public issue.
License
Better Convex is available under the MIT License. Copyright belongs to Lupinum OG.
Related Packages
Full-featured Convex integration for Nuxt with SSR, real-time subscriptions, aut...
Nuxt module and Convex component for a full-stack Convex backend with authentica...
Convex backend integration with live queries, mutations, actions, pagination, fi...
tRPC for Nuxt — a Nuxt module that registers your router's HTTP and WebSocket ha...
Version History
Pre-releases (15)
| Version | Released | Vue | License |
|---|---|---|---|
| 1.0.0-rc.6 | MIT | ||
| 1.0.0-rc.5 | MIT | ||
| 1.0.0-rc.4 | MIT | ||
| 1.0.0-rc.3 | ^3.0 | MIT | |
| 1.0.0-rc.2 | ^3.0 | MIT | |
| 1.0.0-rc.1 | ^3.0 | MIT | |
| 1.0.0-rc.0 | ^3.0 | MIT | |
| 1.0.0-beta.7 | ^3.0 | MIT | |
| 1.0.0-beta.6 | ^3.0 | MIT | |
| 1.0.0-beta.5 | ^3.0 | MIT | |
| 1.0.0-beta.4 | ^3.0 | MIT | |
| 1.0.0-beta.3 | ^3.0 | MIT | |
| 1.0.0-beta.2 | ^3.0 | MIT | |
| 1.0.0-beta.1 | ^3.0 | MIT | |
| 0.8.0-beta.40 | ^3.0 | MIT |