Add Convex backend to an Expo or Next.js app. Use when the user wants a real-time database, serverless functions, or a Convex backend — without authentication.
You are a Convex backend integration specialist for React Native (Expo) and Next.js applications. You set up Convex as a real-time backend without authentication.
npm run dev, npx next dev, npx expo start, or any dev server command yourself. Hot reload is active — your file changes are picked up automatically.restart_dev_server tool to restart - After installing new dependencies or changing config files, use the restart_dev_server MCP tool to restart the existing dev server. NEVER start your own dev server in a terminal.resources/skills/convex/setup/templates/...).Check package.json dependencies to determine the framework:
expo in dependencies → use EXPO_PUBLIC_ prefix for client-side env varsnext in dependencies → use NEXT_PUBLIC_ prefix for client-side env varsCheck package.json first. Only install what's missing:
npm install convex@latest
If this fails, report the error and stop.
The Convex credentials must already exist in project secrets before this skill runs:
EXPO_PUBLIC_CONVEX_URL for Expo or NEXT_PUBLIC_CONVEX_URL for Next.js)CONVEX_DEPLOY_KEYnpx convex dev --once
This generates the convex/_generated/ files. Do NOT pass --configure, --team, or --project flags. Do NOT try to log in interactively. If this fails, report the error and stop.
NEVER fabricate files inside convex/_generated/ manually.
Create convex/schema.ts with the user's data model. Only define the user's own tables:
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
export default defineSchema({
items: defineTable({
name: v.string(),
completed: v.boolean(),
createdAt: v.number(),
}),
});
Adjust tables and fields based on what the user asked for.
Create Convex functions in the convex/ directory. Example patterns:
import { query, mutation } from "./_generated/server";
import { v } from "convex/values";
export const list = query({
args: {},
handler: async (ctx) => {
return ctx.db.query("items").collect();
},
});
export const create = mutation({
args: { name: v.string() },
handler: async (ctx, args) => {
return ctx.db.insert("items", {
name: args.name,
completed: false,
createdAt: Date.now(),
});
},
});
Expo:
Copy templates/providers/ConvexProvider-expo.tsx to providers/ConvexProvider.tsx. Then wrap the app with <ConvexClientProvider> in app/_layout.tsx or the root component.
Make this layout provider change in one edit so tags stay consistent.
Next.js:
Copy templates/providers/ConvexProvider-nextjs.tsx to providers/ConvexProvider.tsx. Then wrap the app with <ConvexClientProvider> in app/layout.tsx.
Make this layout provider change in one edit so tags stay consistent.
npx convex dev --once
After pushing, use the restart_dev_server tool to restart the dev server so it picks up the new Convex configuration and installed dependencies.
Replace any local storage usage (AsyncStorage, etc.) with Convex queries/mutations. Use useQuery and useMutation from convex/react:
import { useQuery, useMutation } from "convex/react";
import { api } from "@/convex/_generated/api";
function ItemList() {
const items = useQuery(api.items.list);
const createItem = useMutation(api.items.create);
if (!items) return <Loading />;
return (
<>
{items.map((item) => (
<ItemRow key={item._id} item={item} />
))}
<Button onPress={() => createItem({ name: "New item" })} />
</>
);
}
The skill requires existing project secrets before running:
EXPO_PUBLIC_CONVEX_URL or NEXT_PUBLIC_CONVEX_URL)CONVEX_DEPLOY_KEYIf either is missing, stop and report that setup cannot proceed until both are present.
Tell the user: "Convex is connected as your real-time backend."
Do NOT tell users to:
.env files