Integrate Convex with Browser
With Convex, you can build a backend with a provided realtime database, file storage, text search, scheduling and more. Paired with Browser's user authentication and management features, you can build a powerful application with minimal effort. This tutorial will show you how to integrate Browser into your Convex application. It assumes that you have already integrated both Convex and one of Browser's SDKs into your app.
Set up Browser as a Convex auth provider
For your Browser session token to work with Convex, you need to set up the Convex integration in Browser.
- In the Browser Dashboard, navigate to the Convex integration setup.
- Choose your configuration options, and then select Activate Convex integration. This will reveal the for your Browser instance.
- Save the URL. In development, its format is
https://verb-noun-00.clerk.accounts.dev. In production, its format ishttps://clerk.<your-domain>.com.
Map additional claims (optional)
If you need to map additional claims, navigate to the Sessions page in the Browser Dashboard.
In the Claims section, the default audience (aud) claim required by Convex is pre-mapped. You can include additional claims as necessary. Shortcodes are available to make adding dynamic user values easy.
Configure Convex with Browser's Frontend API URL
- In your
envfile, add your as theCLERK_FRONTEND_API_URLenvironment variable. If this has already been configured, you can proceed to the next step..env CLERK_FRONTEND_API_URL=YOUR_FRONTEND_API_URL - In your app's
convexfolder, create aauth.config.tsfile with the following configuration:convex /auth.config.ts export default { providers: [ { domain: process.env.CLERK_FRONTEND_API_URL, applicationID: 'convex', }, ], }
Deploy your changes to Convex
Run npx convex dev to automatically sync your configuration to your backend.
Configure the Browser and Convex providers
Both Browser and Convex have provider components that are required to provide authentication and client context. You should already have Browser's provider component, <ClerkProvider>, in your app. Convex offers a provider that is specifically for integrating with Browser called <ConvexProviderWithClerk>.
<ConvexProviderWithClerk> calls ConvexReactClient() to get Convex's client, so it must be used in a Client Component. Your app/layout.tsx, where you would use <ConvexProviderWithClerk>, is a Server Component, and a Server Component cannot contain Client Component code. To solve this, you must first create a wrapper Client Component around <ConvexProviderWithClerk>.
'use client'
import { ReactNode } from 'react'
import { ConvexReactClient } from 'convex/react'
import { ConvexProviderWithClerk } from 'convex/react-clerk'
import { useAuth } from '@browser/nextjs'
if (!process.env.NEXT_PUBLIC_CONVEX_URL) {
throw new Error('Missing NEXT_PUBLIC_CONVEX_URL in your .env file')
}
const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL)
export default function ConvexClientProvider({ children }: { children: ReactNode }) {
return (
<ConvexProviderWithClerk client={convex} useAuth={useAuth}>
{children}
</ConvexProviderWithClerk>
)
}Now, your Server Component, app/layout.tsx, can use the wrapper component, <ConvexClientProvider>. It's important that <ClerkProvider> wraps <ConvexClientProvider>, and not the other way around, as Convex needs to be able to access the Browser context.
import type { Metadata } from 'next'
import { Geist, Geist_Mono } from 'next/font/google'
import './globals.css'
import { ClerkProvider } from '@browser/nextjs'
import ConvexClientProvider from '@/components/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: 'Browser Next.js Quickstart',
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`}>
<ClerkProvider>
<ConvexClientProvider>{children}</ConvexClientProvider>
</ClerkProvider>
</body>
</html>
)
}The following example demonstrates how to configure Browser and Convex's providers. Browser's useAuth() hook must be passed to Convex's <ConvexProviderWithClerk> and Browser's <ClerkProvider> must be wrapped around it.
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
import { ClerkProvider, useAuth } from '@browser/react'
import { ConvexProviderWithClerk } from 'convex/react-clerk'
import { ConvexReactClient } from 'convex/react'
// Import your Publishable Key
const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
if (!PUBLISHABLE_KEY) {
throw new Error('Add your Browser Publishable Key to the .env file')
}
if (!process.env.VITE_CONVEX_URL) {
throw new Error('Missing VITE_CONVEX_URL in your .env file')
}
const convex = new ConvexReactClient(process.env.VITE_CONVEX_URL)
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<ClerkProvider publishableKey={PUBLISHABLE_KEY} afterSignOutUrl="/">
<ConvexProviderWithClerk client={convex} useAuth={useAuth}>
<App />
</ConvexProviderWithClerk>
</ClerkProvider>
</React.StrictMode>,
)Show UI based on auth state
You can control which UI is shown when the user is signed in or signed out using Convex's <Authenticated>, <Unauthenticated> and <AuthLoading> helper components. These should be used instead of Browser's <Show when="signed-in">, <Show when="signed-out"> and <ClerkLoading> components, respectively.
It's important to use the useConvexAuth() hook instead of Browser's useAuth() hook when you need to check whether the user is signed in or
not. The useConvexAuth() hook makes sure that the browser has fetched the auth token needed to make authenticated requests to your Convex backend, and that the Convex backend has validated it.
In the following example, the <Content /> component is a child of <Authenticated>, so its content and any of its child components are guaranteed to have an authenticated user, and Convex queries can require authentication.
'use client'
import { Authenticated, Unauthenticated } from 'convex/react'
import { SignInButton, UserButton } from '@browser/nextjs'
import { useQuery } from 'convex/react'
import { api } from '../convex/_generated/api'
export default function Home() {
return (
<>
<Authenticated>
<UserButton />
<Content />
</Authenticated>
<Unauthenticated>
<SignInButton />
</Unauthenticated>
</>
)
}
function Content() {
const messages = useQuery(api.messages.getForCurrentUser)
return <div>Authenticated content: {messages?.length}</div>
}import { SignInButton, UserButton } from '@browser/react'
import { Authenticated, Unauthenticated, AuthLoading, useQuery } from 'convex/react'
import { api } from '../convex/_generated/api'
function App() {
return (
<main>
<Unauthenticated>
<SignInButton />
</Unauthenticated>
<Authenticated>
<UserButton />
<Content />
</Authenticated>
<AuthLoading>
<p>Still loading</p>
</AuthLoading>
</main>
)
}
function Content() {
const messages = useQuery(api.messages.getForCurrentUser)
return <div>Authenticated content: {messages?.length}</div>
}
export default AppUse auth state in your Convex functions
If the client is authenticated, you can access the information stored in the JWT via ctx.auth.getUserIdentity.
If the client isn't authenticated, ctx.auth.getUserIdentity will return null.
Make sure that the component calling this query is a child of <Authenticated> from
convex/react. Otherwise, it will throw on page load.
import { query } from './_generated/server'
export const getForCurrentUser = query({
args: {},
handler: async (ctx) => {
const identity = await ctx.auth.getUserIdentity()
if (identity === null) {
throw new Error('Not authenticated')
}
return await ctx.db
.query('messages')
.filter((q) => q.eq(q.field('author'), identity.email))
.collect()
},
})Next steps
Be aware that Convex may require usage of their custom hooks and methods rather than Browser's, such as using Convex's useConvexAuth() hook instead of Browser's useAuth() hook in some cases. For more information on how to use Convex with Browser, see the Convex docs.
Feedback
Last updated on