Skip to main content
Docs

Integrate Convex with Clerk

With Convex, you can build a backend with a provided realtime database, file storage, text search, scheduling, and more. Paired with Clerk's authentication and user management features, you can build native mobile apps with a secure auth flow and realtime data access.

This guide shows how to integrate Convex with Clerk in your native mobile app. It assumes you have already integrated both Convex and Clerk in your app.

Set up Clerk as a Convex auth provider

For your Clerk session token to work with Convex, you need to set up the Convex integration in Clerk.

  1. In the Clerk Dashboard, navigate to the Convex integration setup.
  2. Choose your configuration options, and then select Activate Convex integration. This will reveal the for your Clerk instance.
  3. Save the URL. In development, its format is https://verb-noun-00.clerk.accounts.dev. In production, its format is https://clerk.<your-domain>.com.

Map additional claims (optional)

If you need to map additional claims, navigate to the Sessions page in the Clerk 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 Clerk's Frontend API URL

In your app's convex folder, create a auth.config.ts file with the following configuration, using the saved from earlier:

convex/auth.config.ts
export default {
  providers: [
    {
      domain: 'YOUR_FRONTEND_API_URL',
      applicationID: 'convex',
    },
  ],
}

Deploy your changes to Convex

Run npx convex dev to automatically sync your configuration to your backend.

Add the Clerk Convex SDK to your app

Add clerk-convex-swift via Swift Package Manager to your app:

Package.swift
dependencies: [
  .package(url: "https://github.com/clerk/clerk-convex-swift", from: "0.1.0")
]

targets: [
  .target(
    name: "YourApp",
    dependencies: [
      .product(name: "ClerkConvex", package: "clerk-convex-swift")
    ]
  )
]

Configure Clerk first with your Clerk , then initialize ConvexClientWithAuth with ClerkConvexAuthProvider using your Convex deployment URL. Refer to the Convex deployment docs for more info.

MyApp.swift
import ClerkConvex
import ClerkKit
import ConvexMobile
import SwiftUI

@MainActor
let client = ConvexClientWithAuth(
  deploymentUrl: "YOUR_CONVEX_DEPLOYMENT_URL",
  authProvider: ClerkConvexAuthProvider()
)

@main
struct MyApp: App {
  init() {
    Clerk.configure(publishableKey: "YOUR_PUBLISHABLE_KEY")
  }

  var body: some Scene {
    WindowGroup {
      ContentView()
        .environment(Clerk.shared)
    }
  }
}

After users authenticate with Clerk, auth state is automatically synced to Convex. Use client for subscriptions, mutations, actions, and auth state.

Show UI based on auth state

Convex exposes auth state from the authenticated client. Use that state to render loading, signed-out, and signed-in UI.

ContentView.swift
import ConvexMobile
import SwiftUI

struct ContentView: View {
  @State private var authState: AuthState<String> = .loading

  var body: some View {
    Group {
      switch authState {
      case .loading:
        ProgressView()
      case .unauthenticated:
        Text("Unauthenticated")
      case .authenticated:
        AuthenticatedView()
      }
    }
    .task {
      for await state in client.authState.values {
        authState = state
      }
    }
  }
}

Make requests and present data in the UI

With Clerk and Convex configured, authenticated requests stay in sync so you can subscribe and render data with minimal setup.

AuthenticatedView.swift
import ConvexMobile
import SwiftUI

struct AuthenticatedView: View {
  @State private var messages: [Message] = []

  var body: some View {
    List(messages) { message in
      VStack(alignment: .leading) {
        Text(message.author)
        Text(message.body)
      }
    }
    .task {
      let publisher = client
        .subscribe(to: "messages:list")
        .replaceError(with: [Message]())

      for await latest in publisher.values {
        messages = latest
      }
    }
  }
}

struct Message: Identifiable, Codable {
  let id: String
  let author: String
  let body: String
}

Next steps

For more information on how to use Convex with Clerk, see the Convex docs.

Feedback

What did you think of this content?

Last updated on