Skip to main content
The SnackBase SDK provides React hooks and context providers for seamless integration with React applications.

Installation

Install the SDK and React peer dependency:
npm
npm install @snackbase/sdk react
yarn
yarn add @snackbase/sdk react
pnpm
pnpm add @snackbase/sdk react
React 18 or higher is required for the React integration.

Setup

1. Wrap with Provider

Wrap your application with SnackBaseProvider:
import { SnackBaseProvider } from "@snackbase/sdk/react";

function App() {
  return (
    <SnackBaseProvider
      baseUrl="https://api.example.com"
      apiKey={process.env.SNACKBASE_API_KEY}
      defaultAccount="my-account"
    >
      <YourApp />
    </SnackBaseProvider>
  );
}

2. Use Hooks

Use the provided hooks in your components:
import { useAuth, useRecord } from "@snackbase/sdk/react";

function Profile() {
  const { user, login, logout } = useAuth();
  const { data: profile, loading } = useRecord("profiles", user?.id);

  if (!user) {
    return <button onClick={() => login(email, password)}>Login</button>;
  }

  return (
    <div>
      <h1>Welcome, {user.email}</h1>
      {loading ? <p>Loading...</p> : <pre>{JSON.stringify(profile, null, 2)}</pre>}
      <button onClick={logout}>Logout</button>
    </div>
  );
}

Provider Props

PropTypeRequiredDescription
baseUrlstringYesAPI base URL
apiKeystringNoAPI key for server authentication
defaultAccountstringNoDefault account slug
timeoutnumberNoRequest timeout (ms)
maxRetriesnumberNoMax retry attempts
storageBackendStorageBackendNoToken storage backend
enableLoggingbooleanNoEnable request logging
logLevelLogLevelNoLogging level

Environment-Specific Setup

Development

<SnackBaseProvider
  baseUrl={process.env.NEXT_PUBLIC_SNACKBASE_URL!}
  defaultAccount="dev-account"
  enableLogging={true}
  logLevel="debug"
>
  <App />
</SnackBaseProvider>

Production

<SnackBaseProvider
  baseUrl={process.env.NEXT_PUBLIC_SNACKBASE_URL!}
  apiKey={process.env.SNACKBASE_API_KEY}
  enableLogging={false}
>
  <App />
</SnackBaseProvider>

With Next.js

App Router

// app/layout.tsx
import { SnackBaseProvider } from "@snackbase/sdk/react";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <SnackBaseProvider
          baseUrl={process.env.NEXT_PUBLIC_SNACKBASE_URL!}
        >
          {children}
        </SnackBaseProvider>
      </body>
    </html>
  );
}

Pages Router

// pages/_app.tsx
import type { AppProps } from "next/app";
import { SnackBaseProvider } from "@snackbase/sdk/react";

export default function App({ Component, pageProps }: AppProps) {
  return (
    <SnackBaseProvider
      baseUrl={process.env.NEXT_PUBLIC_SNACKBASE_URL!}
    >
      <Component {...pageProps} />
    </SnackBaseProvider>
  );
}

With Vite

// src/main.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import { SnackBaseProvider } from "@snackbase/sdk/react";
import App from "./App";

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <SnackBaseProvider
      baseUrl={import.meta.env.VITE_SNACKBASE_URL}
    >
      <App />
    </SnackBaseProvider>
  </React.StrictMode>
);

Access Client Directly

For advanced use cases, access the client directly:
import { useSnackBase } from "@snackbase/sdk/react";

function AdvancedComponent() {
  const client = useSnackBase();

  useEffect(() => {
    // Use client directly
    client.collections.list().then((collections) => {
      console.log("Collections:", collections);
    });
  }, [client]);

  return <div>Check console for collections</div>;
}

Server-Side Rendering

For SSR, skip provider on server:
import { SnackBaseProvider } from "@snackbase/sdk/react";

function App({ children }: { children: React.ReactNode }) {
  // Only provide on client
  if (typeof window === "undefined") {
    return <>{children}</>;
  }

  return (
    <SnackBaseProvider
      baseUrl={process.env.NEXT_PUBLIC_SNACKBASE_URL!}
    >
      {children}
    </SnackBaseProvider>
  );
}

TypeScript Support

The React integration is fully typed:
import { useAuth, useRecord } from "@snackbase/sdk/react";
import type { User, Post } from "@snackbase/sdk";

function Profile() {
  const { user } = useAuth<User>();
  const { data: post } = useRecord<Post>("posts", "post-id");

  // user and post are fully typed
  console.log(user?.email);
  console.log(post?.title);
}

Complete Example

import { SnackBaseProvider, useAuth, useRecord } from "@snackbase/sdk/react";

function App() {
  return (
    <SnackBaseProvider
      baseUrl="https://api.example.com"
      defaultAccount="my-account"
    >
      <Main />
    </SnackBaseProvider>
  );
}

function Main() {
  const { user, login, logout, isLoading } = useAuth();

  if (isLoading) {
    return <div>Loading...</div>;
  }

  if (!user) {
    return <LoginForm onLogin={login} />;
  }

  return (
    <div>
      <header>
        <h1>Welcome, {user.email}</h1>
        <button onClick={logout}>Logout</button>
      </header>
      <Dashboard />
    </div>
  );
}

function LoginForm({ onLogin }: { onLogin: (credentials: any) => Promise<void> }) {
  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    const form = e.currentTarget;
    const email = (form.elements.namedItem("email") as HTMLInputElement).value;
    const password = (form.elements.namedItem("password") as HTMLInputElement).value;
    await onLogin({ email, password });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="email" type="email" placeholder="Email" required />
      <input name="password" type="password" placeholder="Password" required />
      <button type="submit">Login</button>
    </form>
  );
}

function Dashboard() {
  const { user } = useAuth();
  const { data: profile, loading, error } = useRecord("profiles", user?.id || "");

  if (loading) return <div>Loading profile...</div>;
  if (error) return <div>Error loading profile</div>;

  return (
    <div>
      <h2>Profile</h2>
      <pre>{JSON.stringify(profile, null, 2)}</pre>
    </div>
  );
}

Next Steps