First, initialize a fresh Nuxt project using the minimal template.
pnpm create nuxt@latest my-convex-app
bun create nuxt@latest my-convex-app
npm create nuxt@latest my-convex-app
Install the Convex client and the Nuxt module.
pnpm add convex better-convex-nuxt
bun add convex better-convex-nuxt
npm add convex better-convex-nuxt
Add the module to your nuxt.config.ts and provide the Convex deployment URL.
export default defineNuxtConfig({
compatibilityDate: '2025-07-15',
modules: ['better-convex-nuxt'],
convex: {
url: process.env.CONVEX_URL
}
})
Run the Convex dev command. This will prompt you to log in and create a new project. It will also generate a .env.local file containing your CONVEX_URL.
npx convex dev
convex/ folder.By default, Convex saves variables to .env.local, but Nuxt looks for .env. Update your dev script in package.json to include the correct flag.
{
"scripts": {
"dev": "nuxt dev --dotenv .env.local",
}
}
Create a file to define some initial data for your database.
{"text": "Buy groceries", "isCompleted": true}
{"text": "Go for a swim", "isCompleted": true}
{"text": "Integrate Convex", "isCompleted": false}
Now, import this data into a table named tasks:
npx convex import --table tasks sampleData.jsonl
Create a schema file to describe your data structure. This ensures type safety across your full stack.
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
export default defineSchema({
tasks: defineTable({
text: v.string(),
isCompleted: v.boolean(),
}),
});
Define a function to fetch the tasks from the database.
import { query } from "./_generated/server";
export const get = query({
args: {},
handler: async (ctx) => {
return await ctx.db.query("tasks").collect();
},
});
Use the useConvexQuery composable to subscribe to your data. It fetches on the server (SSR) and updates in real-time via WebSocket.
<script setup lang="ts">
import { api } from '~~/convex/_generated/api'
// Subscribes to the 'get' function in 'convex/tasks.ts'
// Data is fetched on the server (SSR enabled by default)
const { data: tasks, error } = await useConvexQuery(api.tasks.get, {})
</script>
<template>
<main>
<h1>My Tasks</h1>
<div v-if="error">Error: {{ error.message }}</div>
<ul v-else>
<li v-for="task in tasks" :key="task._id">
{{ task.isCompleted ? '✅' : '❌' }} {{ task.text }}
</li>
</ul>
</main>
</template>
useFetch, queries run on the server during SSR. Your page renders with data already loaded - no loading spinners on initial page load!Launch your app and view it at http://localhost:3000.
pnpm dev
bun dev
npm run dev
After following the steps above, your project should look like this:
import { query } from "./_generated/server";
export const get = query({
args: {},
handler: async (ctx) => {
return await ctx.db.query("tasks").collect();
},
});
Ensure that npx convex dev is running in a separate terminal. This syncs your local functions to the Convex cloud.
Check your .env.local file. It should contain CONVEX_URL. Make sure you added the --dotenv .env.local flag to your package.json scripts.
You've successfully set up a real-time Nuxt application with Convex! Continue learning with these guides: