Crux
Getting Started

Expo / React Native

Use Crux from an Expo or React Native app via a Node.js or Convex backend.

Crux runs on the server, not on-device. From an Expo app, you call your backend (Node.js, Next.js Route Handler, or Convex action) which then calls Crux. This guide shows the recommended setup with each option.

Why server-side? Crux prompts compose contexts, manage memory, and call provider APIs — all of which require server-side credentials and persistent storage. The mobile app sends inputs and receives outputs.

Pattern 1: Expo + Node/Next.js backend

The simplest setup: your Expo app posts to a backend you control.

Backend (Next.js Route Handler)

app/api/summarize/route.ts
import { generate } from '@crux/ai'
import { openai } from '@ai-sdk/openai'

import { summarize } from '@/lib/ai/prompts'

export async function POST(req: Request) {
  const { text } = await req.json()

  const result = await generate(summarize, {
    model: openai('gpt-4o-mini'),
    input: { text },
  })

  return Response.json(result.object)
}

See the Next.js guide for the prompt definition and full Next.js setup.

Expo client

app/screens/Summarize.tsx
import { useState } from 'react'
import { View, TextInput, Button, Text } from 'react-native'

const API_URL = process.env.EXPO_PUBLIC_API_URL // your backend

export function Summarize() {
  const [text, setText] = useState('')
  const [summary, setSummary] = useState<string | null>(null)

  async function handleSummarize() {
    const res = await fetch(`${API_URL}/api/summarize`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ text }),
    })
    const data = await res.json()
    setSummary(data.summary)
  }

  return (
    <View>
      <TextInput value={text} onChangeText={setText} multiline />
      <Button title="Summarize" onPress={handleSummarize} />
      {summary && <Text>{summary}</Text>}
    </View>
  )
}

For streaming responses, use the AI SDK's useChat and a streaming Route Handler — see the Next.js guide.

Pattern 2: Expo + Convex backend

If you're using Convex, your Expo app calls Convex actions directly via the Convex React Native client. The backend setup is identical to the Convex guide.

Expo client

app/screens/Chat.tsx
import { useAction } from 'convex/react'
import { api } from '@/convex/_generated/api'

export function Chat() {
  const respond = useAction(api.agent.chat.respond)

  async function handleSend(message: string) {
    const reply = await respond({ message, mode: 'casual' })
    // ...display reply
  }

  // ...
}

Live Plans & Tasks UI

If your Expo app needs to subscribe to plan or task list changes (for approval flows, agent progress, etc.), wrap it in a CruxProvider and use the live hooks:

App.tsx
import { CruxProvider } from '@crux/react'
import { createSSETransport } from '@crux/react'

const transport = createSSETransport({
  url: `${process.env.EXPO_PUBLIC_API_URL}/api/crux/sse`,
})

export default function App() {
  return <CruxProvider transport={transport}>{/* your screens */}</CruxProvider>
}
app/screens/PlanReview.tsx
import { usePlan } from '@crux/react'

export function PlanReview({ planId }: { planId: string }) {
  const plan = usePlan(planId)
  if (!plan) return null
  return <Text>{plan.content}</Text>
}

The SSE transport reconnects automatically. For environments where SSE is unreliable (some carrier proxies), use createPollingTransport() instead.

Next steps

On this page