React is a popular JavaScript library for building user interfaces. The SurrealDB SDK for JavaScript can also be used in your React applications to interact with your SurrealDB instance.
In this guide, we will walk you through setting up and querying your first project with the SurrealDB SDK for React.
This guide assumes the following:
First, install the SurrealDB SDK using your favorite package manager:
bun install surrealdb
npm install --save surrealdb
yarn add surrealdb
pnpm install surrealdb
ImportantThe SurrealDB SDK for JavaScript is also available in the JSR registry as
@surrealdb/surrealdb
.
After installing the SDK, import it into your project. We recommend initializing the SDK in a Context Provider in order to expose it throughout your application. This makes the client accessible anywhere in your component tree, simplifies cleanup, and provides built-in state management for connection success or failure.
In addition to surrealdb
, we recommend using @tanstack/react-query for managing the connection state:
bun install @tanstack/react-query
npm install --save @tanstack/react-query
yarn add @tanstack/react-query
pnpm install @tanstack/react-query
Create a new file, e.g., SurrealProvider.tsx
, which will contain the context provider and hooks for accessing the SurrealDB client. You can copy the following code, which takes care of managing the connection.
Feel free to make any changes to fit your project structure and coding style.
src/contexts/SurrealProvider.tsximport { Surreal } from "surrealdb"; import { useMutation } from "@tanstack/react-query"; import React, { createContext, useContext, useEffect, useMemo, useCallback, useState } from "react"; interface SurrealProviderProps { children: React.ReactNode; /** The database endpoint URL */ endpoint: string; /** Optional existing Surreal client */ client?: Surreal; /* Optional connection parameters */ params?: Parameters<Surreal["connect"]>[1]; /** Auto connect on component mount, defaults to true */ autoConnect?: boolean; } interface SurrealProviderState { /** The Surreal instance */ client: Surreal; /** Whether the connection is pending */ isConnecting: boolean; /** Whether the connection was successfully established */ isSuccess: boolean; /** Whether the connection rejected in an error */ isError: boolean; /** The connection error, if present */ error: unknown; /** Connect to the Surreal instance */ connect: () => Promise<true>; /** Close the Surreal instance */ close: () => Promise<true>; } const SurrealContext = createContext<SurrealProviderState | undefined>(undefined); export function SurrealProvider({ children, client, endpoint, params, autoConnect = true, }: SurrealProviderProps) { // Surreal instance remains stable across re-renders const [surrealInstance] = useState(() => client ?? new Surreal()); // React Query mutation for connecting to Surreal const { mutateAsync: connectMutation, isPending, isSuccess, isError, error, reset, } = useMutation({ mutationFn: () => surrealInstance.connect(endpoint, params), }); // Wrap mutateAsync in a stable callback const connect = useCallback(() => connectMutation(), [connectMutation]); // Wrap close() in a stable callback const close = useCallback(() => surrealInstance.close(), [surrealInstance]); // Auto-connect on mount (if enabled) and cleanup on unmount useEffect(() => { if (autoConnect) { connect(); } return () => { reset(); surrealInstance.close(); }; }, [autoConnect, connect, reset, surrealInstance]); // Memoize the context value const value: SurrealProviderState = useMemo( () => ({ client: surrealInstance, isConnecting: isPending, isSuccess, isError, error, connect, close, }), [surrealInstance, isPending, isSuccess, isError, error, connect, close], ); return <SurrealContext.Provider value={value}>{children}</SurrealContext.Provider>; } /** * Access the Surreal connection state from the context. */ export function useSurreal() { const context = useContext(SurrealContext); if (!context) { throw new Error("useSurreal must be used within a SurrealProvider"); } return context; } /** * Access the Surreal client from the context. */ export function useSurrealClient() { const { client } = useSurreal(); return client; }
In your top level component (usually index.tsx
), wrap your root component with SurrealProvider
:
src/index.tsximport React from "react"; import ReactDOM from "react-dom/client"; import App from "./App"; import { SurrealProvider } from "./SurrealProvider"; const root = ReactDOM.createRoot(document.getElementById("root")!); root.render( <React.StrictMode> <SurrealProvider endpoint="http://127.0.0.1:8000" autoConnect> <App /> </SurrealProvider> </React.StrictMode> );
Anywhere inside the provider component tree, you can now access the Surreal client or check the connection status:
import { useEffect } from "react"; import { useSurreal, useSurrealClient } from "./SurrealProvider"; export function ExampleComponent() { const { client, isConnecting, isSuccess, isError, error, connect } = useSurreal(); // or if you only need the client: // const client = useSurrealClient(); useEffect(() => { if (isSuccess) { // Example: run a query once the connection is successful client.query("SELECT * FROM users").then(console.log).catch(console.error); } }, [isSuccess, client]); if (isConnecting) return <p>Connecting to SurrealDB...</p>; if (isError) return <p>Failed to connect: {String(error)}</p>; return ( <div> <h1>Users</h1> {/* Example button to manually reconnect */} <button onClick={connect}>Reconnect</button> </div> ); }
If you need custom logic (e.g., calling db.use(...)
, providing authentication tokens, etc.), you can:
Use the params
prop:
<SurrealProvider endpoint="http://127.0.0.1:8000" params={{ namespace: "myNs", database: "myDb" }} > <App /> </SurrealProvider>
Supply your own preconfigured Surreal instance:
import Surreal from "surrealdb"; const customClient = new Surreal(); await customClient.connect("http://127.0.0.1:8000/rpc"); await customClient.use({ namespace: "myNs", database: "myDb" }); <SurrealProvider client={customClient} endpoint="http://127.0.0.1:8000" > <App /> </SurrealProvider>
If you are only interested in accessing SDK methods, you can use useSurrealClient()
to directly obtain a reference to the Surreal
client instance. In the example below, we use the .query()
method which allows you write raw SurrealQL queries:
import { useState, useEffect } from "react"; import { useSurrealClient } from "./SurrealProvider"; export function ExampleComponent() { const client = useSurrealClient(); const [users, setUsers] = useState([]); const fetchUsers = async () => { try { const result = await client.query("SELECT * FROM users"); setUsers(result[0] || []); } catch (err) { console.error(err); } }; useEffect(() => { fetchUsers(); }, []); return ( <div> {users.map((user) => ( <div key={user?.id}> <h3>{user?.name}</h3> <p>Email: {user?.email}</p> </div> ))} {users.length === 0 && <p>No users found</p>} </div> ); }
Now that you have SurrealDB integrated into your React project, you’re ready to start building! To learn more about interacting with your database: