Guide

Getting Started

Learn how to integrate Convex into your Nuxt 4 application.
Prerequisite: You need a Convex account to follow this tutorial. If you don't have one, you can sign up for free here.

Create a new Nuxt project

First, initialize a fresh Nuxt project using the minimal template.

pnpm create nuxt@latest my-convex-app

Install dependencies

Install the Convex client and the Nuxt module.

pnpm add convex better-convex-nuxt

Configure Nuxt

Add the module to your nuxt.config.ts and provide the Convex deployment URL.

nuxt.config.ts
export default defineNuxtConfig({
  compatibilityDate: '2025-07-15',
  modules: ['better-convex-nuxt'],

  convex: {
    url: process.env.CONVEX_URL
  }
})

Initialize Convex

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
Keep this terminal window running! It automatically syncs your backend functions whenever you save changes in the convex/ folder.

Update Environment Handling

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.

package.json
{
  "scripts": {
    "dev": "nuxt dev --dotenv .env.local",
  }
}

Prepare Sample Data

Create a file to define some initial data for your database.

sampleData.jsonl
{"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

Define the Schema

Create a schema file to describe your data structure. This ensures type safety across your full stack.

convex/schema.ts
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";

export default defineSchema({
  tasks: defineTable({
    text: v.string(),
    isCompleted: v.boolean(),
  }),
});

Create a Database Query

Define a function to fetch the tasks from the database.

convex/tasks.ts
import { query } from "./_generated/server";

export const get = query({
  args: {},
  handler: async (ctx) => {
    return await ctx.db.query("tasks").collect();
  },
});

Display Data in your App

Use the useConvexQuery composable to subscribe to your data. It fetches on the server (SSR) and updates in real-time via WebSocket.

app/app.vue
<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>
SSR by default: Like Nuxt's useFetch, queries run on the server during SSR. Your page renders with data already loaded - no loading spinners on initial page load!

Start Development

Launch your app and view it at http://localhost:3000.

pnpm dev

Project Structure

After following the steps above, your project should look like this:

convex/tasks.ts
import { query } from "./_generated/server";

export const get = query({
  args: {},
  handler: async (ctx) => {
    return await ctx.db.query("tasks").collect();
  },
});

Troubleshooting

Next Steps

You've successfully set up a real-time Nuxt application with Convex! Continue learning with these guides:

Basics

Learn about SSR, mutations, and controlling data loading behavior.

Authentication

Add user authentication with Better Auth and protect your data.

Query Options

Explore all useConvexQuery options: lazy loading, transforms, and more.