React
Use the React entrypoint when you want FFDB wired into component lifecycle and reactive UI updates. The provider owns client initialization, and hooks expose ready-to-use auth, query, and sync surfaces.
Why use the React entrypoint
- Name
Single app-level client- Description
FFDBProvidercreates and tears down one client instance for the React tree.
- Name
Hook-first ergonomics- Description
useFFDB,useDB,useAuth,useQuery, anduseRawQueryremove manual wiring.
- Name
Reactive data updates- Description
Query hooks subscribe to client changes and refetch when local data changes.
Main React interfaces
type FFDBProviderProps = {
options: CreateClientOptions
fallback?: ReactNode
onError?: (error: Error) => void
}
type UseFFDBResult = {
client
db
sql
auth
destroy
sync
getAccess
subscribe
isLoading
error
clientVersion
isReady
}
type UseQueryOptions = {
refetchOnFocus?: boolean
refetchOnReconnect?: boolean
refetchInterval?: number
enabled?: boolean
deps?: unknown[]
}
End-to-end setup
import { FFDBProvider, useAuth, useFFDB, useQuery } from 'ffdb-client/react'
import type { Database } from './ffdb.types'
export function App() {
return (
<FFDBProvider<Database>
options={{
config: {
apiUrl: import.meta.env.VITE_FFDB_API_URL,
},
}}
fallback={<p>Connecting to FFDB...</p>}
onError={(error) => console.error('FFDB init failed', error)}
>
<UsersPage />
</FFDBProvider>
)
}
function UsersPage() {
const ffdb = useFFDB<Database>()
const auth = useAuth()
const session = auth.useSession()
const { data, isLoading, error, refetch } = useQuery(
(db) => db.selectFrom('user').select(['id', 'email']).execute(),
{
enabled: ffdb.isReady && Boolean(session.data?.user?.id),
deps: [session.data?.user?.id],
refetchOnFocus: true,
refetchOnReconnect: true,
},
)
if (ffdb.isLoading || session.isPending) return <p>Loading...</p>
if (ffdb.error) return <p>Client error: {ffdb.error.message}</p>
if (error) return <p>Query error: {error.message}</p>
return (
<section>
<button onClick={() => refetch()}>Refresh</button>
{isLoading ? <p>Fetching...</p> : <pre>{JSON.stringify(data, null, 2)}</pre>}
</section>
)
}
Choosing the right hook
- Name
useFFDB()- Description
Use when you need readiness state and multiple client surfaces in one place.
- Name
useDB()- Description
Use when you only need typed query builder access.
- Name
useAuth()- Description
Use for sign-in, sign-out, and session lifecycle operations.
- Name
useQuery()- Description
Best for typed Kysely queries with refetch controls.
- Name
useRawQuery()- Description
Best for SQL-driven UI reads that are easier to express as raw SQL.
Practical performance and correctness notes
- Use
enabledanddepsto avoid running auth-dependent queries too early. - Prefer
refetchOnReconnectfor unstable networks. - Use
useRawQuery(..., { bypassCache: true })when you explicitly need a non-cached read. - Surface provider and hook errors in UI to reduce silent failures.
React hooks provide convenience, but the same client semantics still apply. Treat sync as eventually consistent and model loading/error states explicitly.