React hooks

This reference describes the main hooks exported by ffdb-client/react, what they return, and when to choose each one.

Provider requirement

All hooks below require FFDBProvider in the React tree.

<FFDBProvider options={{ config: { apiUrl: import.meta.env.VITE_FFDB_API_URL } }}>
  <App />
</FFDBProvider>

useFFDB

Use when you want the broadest client surface with readiness and error state.

type UseFFDBResult = {
  client
  db
  sql
  auth
  destroy
  sync
  getAccess
  subscribe
  isLoading
  error
  clientVersion
  isReady
}

useDB

Use when a component only needs the typed query builder.

const db = useDB<Database>()

useAuth

Use for auth methods and session hook access.

const auth = useAuth()
const session = auth.useSession()

useSession() returns:

{
  data,
  error,
  isPending,
  isRefetching,
  refetch,
}

useFFDBStatus

Use for lightweight status-only checks when you do not need the full client object.

const { isLoading, error } = useFFDBStatus()

useQuery

Typed Kysely query hook for reactive data reads.

useQuery((db) => db.selectFrom('user').selectAll().execute(), {
  enabled: true,
  deps: [],
  refetchOnFocus: true,
  refetchOnReconnect: false,
  refetchInterval: undefined,
})

Options:

type UseQueryOptions = {
  refetchOnFocus?: boolean
  refetchOnReconnect?: boolean
  refetchInterval?: number
  enabled?: boolean
  deps?: unknown[]
}

Return:

type UseQueryResult<T> = {
  data: T[] | null
  error: Error | null
  isLoading: boolean
  isFetching: boolean
  refetch: () => Promise<void>
  mutate: (data: T[] | null) => void
}

useRawQuery

SQL-string hook for cases where raw SQL is clearer than query-builder composition.

useRawQuery<{ id: string; name: string }>(
  {
    sql: 'SELECT id, name FROM user WHERE name LIKE ? ORDER BY name LIMIT 20',
    values: [`%${term}%`],
    bypassCache: false,
  },
  {
    enabled: term.length > 1,
    deps: [term],
    refetchOnReconnect: true,
  },
)

Input:

type UseRawQueryInput = {
  sql: string
  values?: readonly unknown[]
  bypassCache?: boolean
}

End-to-end hook composition example

import { useAuth, useFFDB, useQuery } from 'ffdb-client/react'

export function UsersPanel() {
  const { isReady, error: clientError } = useFFDB()
  const auth = useAuth()
  const session = auth.useSession()

  const { data, isLoading, error, refetch } = useQuery(
    (db) => db.selectFrom('user').select(['id', 'email']).execute(),
    {
      enabled: isReady && Boolean(session.data?.user?.id),
      deps: [session.data?.user?.id],
      refetchOnFocus: true,
      refetchOnReconnect: true,
    },
  )

  if (clientError) return <p>Client error: {clientError.message}</p>
  if (session.isPending || isLoading) return <p>Loading...</p>
  if (error) return <p>Query error: {error.message}</p>

  return (
    <section>
      <button onClick={() => refetch()}>Refresh</button>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </section>
  )
}

Practical guidance

  1. Use enabled and deps deliberately for auth-scoped queries.
  2. Prefer useQuery for typed CRUD screens and useRawQuery for SQL-heavy search/aggregation.
  3. Model isLoading and isFetching separately for better UX.
  4. Use mutate for optimistic UI adjustments, then refetch to confirm final state.

See also

  1. Reference: createClient
  2. Reference: storage adapters
  3. Reference: errors

Was this page helpful?