Skip to content

Join the Seedly owners community →

Databases

Setting Up Convex

Add Convex to a new Next.js app by following the official quickstart step by step

Written by 12 min read1 activity
Pixl, your presenter

Pixl presents

One npm install, one npx convex dev, and Convex sets up the folder you'd otherwise fumble through by hand. Easy.One npm install, one npx convex dev, and Convex sets up the folder you'd otherwise fumble through by hand. Easy.

Pixl watches a wooden crate open itself as a folder and coiled cord climb out
One command sets up the convex folder for you

You know what Convex is now. Time to actually plug it into a real Next.js app. We'll follow the official Next.js quickstart from the Convex docs, using the App Router.

Before You Start#

You'll need Node.js and npm installed, plus a terminal you don't mind typing into. The Convex tutorial asks for Node.js version 20 or newer. Check yours by running node --version.

Part 1. Create the App and Install Convex#

Step 1. Create a Next.js App#

Make a new Next.js app with the create-next-app command. The docs say to pick the default option for every prompt, so just smash Enter each time.

npx create-next-app@latest my-app

Step 2. Install Convex#

Hop into your new app folder and install the convex package. This one package gives you both the client and server library.

cd my-app && npm install convex

Step 3. Start a Convex Dev Deployment#

Now run the Convex dev command.

npx convex dev

The quickstart says this will prompt you to log in with GitHub, create a project, and save your deployment URLs.

It also creates a convex/ folder in your app. That's where your backend functions are going to live.

Leave this command running. It keeps an eye on your convex/ folder and syncs your functions to your dev deployment in the cloud every time you hit save.

What Did That Command Create?#

Pixl sits between two wooden retro monitors with scrolling coloured bars
Keep convex dev running in one window, your app in another

After npx convex dev runs the first time, you'll spot a few new things in your project.

The convex/ folder. This is home base for your query and mutation functions. Inside it, Convex also keeps a _generated folder. The dev command updates it automatically so your editor knows the names and types of your functions. You don't touch those files by hand.

The .env.local file. Convex saves your project settings here so future runs of npx convex dev know which deployment to connect to. The CLI docs say this includes a CONVEX_DEPLOYMENT variable with the name of your dev deployment. For a Next.js app, your deployment URL gets saved under a name starting with NEXT_PUBLIC_, so your browser code can read it. The docs show an example like this one.

NEXT_PUBLIC_CONVEX_URL=https://guiltless-dog-960.convex.cloud

Yours will be different. That's normal (and no, you don't get to pick the funny name).

Part 2. Add Data and a Query#

Step 4. Create Sample Data#

In your editor, create a file called sampleData.jsonl in your my-app folder and paste in these three lines. Each line is one document.

{"text": "Buy groceries", "isCompleted": true}
{"text": "Go for a swim", "isCompleted": true}
{"text": "Integrate Convex", "isCompleted": false}

Step 5. Import the Data#

In your second terminal, inside the my-app folder, use the Convex import command to add a tasks table with that sample data.

npx convex import --table tasks sampleData.jsonl

Remember, Convex tables pop into existence as soon as they get their first document. No need to create the tasks table first.

Step 6. Write a Query Function#

In the convex/ folder, add a new file called tasks.ts with this code.

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

This query reads every document in the tasks table. Since the file is called tasks.ts and the export is called get, your app calls it as api.tasks.get.

Part 3. Connect Next.js to Convex#

Step 7. Create the Provider Component#

Your React components need a connection to Convex. That connection is a ConvexReactClient, and you hand it to your app through a ConvexProvider.

In the app/ folder, add a new file called ConvexClientProvider.tsx.

"use client";
 
import { ConvexProvider, ConvexReactClient } from "convex/react";
import { ReactNode } from "react";
 
const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);
 
export function ConvexClientProvider({ children }: { children: ReactNode }) {
  return <ConvexProvider client={convex}>{children}</ConvexProvider>;
}

See how it reads NEXT_PUBLIC_CONVEX_URL? That's the value Convex already saved to .env.local for you. The "use client" line at the top tells Next.js this component runs in the browser.

Step 8. Wrap Your App with the Provider#

Open app/layout.tsx and wrap the children of the body element with ConvexClientProvider. Here's the full file from the docs.

import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { ConvexClientProvider } from "./ConvexClientProvider";
 
const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});
 
const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});
 
export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};
 
export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
        <ConvexClientProvider>{children}</ConvexClientProvider>
      </body>
    </html>
  );
}

The only new bits are the ConvexClientProvider import and the line that wraps children. Everything else came with your Next.js app.

Step 9. Show the Data on Your Page#

Replace app/page.tsx with this code. It uses the useQuery hook to call your api.tasks.get function.

"use client";
 
import { useQuery } from "convex/react";
import { api } from "../convex/_generated/api";
 
export default function Home() {
  const tasks = useQuery(api.tasks.get);
  return (
    <main className="flex min-h-screen flex-col items-center justify-between p-24">
      {tasks?.map(({ _id, text }) => (
        <div key={_id}>{text}</div>
      ))}
    </main>
  );
}

That ?. after tasks matters a LOT. While the data's still loading, useQuery returns undefined, so this keeps the page from crashing before your tasks show up.

Step 10. Start the App#

With npx convex dev still running in your first terminal, start Next.js in the second one.

npm run dev

Open http://localhost:3000 in your browser. You should see your three tasks sitting there.

TL;DR#

  • Install Convex with npm install convex, then run npx convex dev
  • npx convex dev creates the convex/ folder and saves your deployment details to .env.local
  • Keep npx convex dev running while you work so your functions stay in sync
  • A query in convex/tasks.ts named get becomes api.tasks.get in your app
  • ConvexClientProvider connects your Next.js app to Convex using NEXT_PUBLIC_CONVEX_URL
  • useQuery loads data and keeps it fresh all on its own

What's Next?#

Your app can read data now. Next we'll add a schema, write a mutation that adds new tasks and call it from a form with useMutation.

This lesson ends with a short activity.